diff --git a/plat/allwinner/common/sunxi_common.c b/plat/allwinner/common/sunxi_common.c index 5b536a043..78f96f293 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 @@ -154,50 +150,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/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/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)); +}