allwinner: Move sunxi_cpu_power_off_self() into platforms

The code to power the current core off when SCPI is not available is now
different for the two supported SoC families.
To make adding new platforms easier, move sunxi_cpu_power_off_self()
into the SoC directory, so we don't need to carry definitions for both
methods for all SoCs.

On the H6 we just need to trigger the CPUIDLE hardware, so can get rid
of all the code to program the ARISC, which is now only needed for the
A64 version.

Change-Id: Id2a1ac7dcb375e2fd021b441575ce86b4d7edf2c
Signed-off-by: Andre Przywara <andre.przywara@arm.com>
This commit is contained in:
Andre Przywara 2021-02-14 23:56:04 +00:00
parent eb15bdaad2
commit 9227719dbf
5 changed files with 68 additions and 120 deletions

View File

@ -6,13 +6,9 @@
#include <errno.h>
#include <platform_def.h>
#include <arch_helpers.h>
#include <common/debug.h>
#include <lib/mmio.h>
#include <lib/xlat_tables/xlat_tables_v2.h>
#include <plat/common/platform.h>
#include <sunxi_def.h>
#include <sunxi_mmap.h>
@ -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);
}

View File

@ -19,10 +19,6 @@
#include <sunxi_mmap.h>
#include <sunxi_private.h>
#ifndef SUNXI_CPUIDLE_EN_REG
#include <core_off_arisc.h>
#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);

View File

@ -14,6 +14,7 @@
#include <drivers/allwinner/sunxi_rsb.h>
#include <lib/mmio.h>
#include <core_off_arisc.h>
#include <sunxi_def.h>
#include <sunxi_mmap.h>
#include <sunxi_private.h>
@ -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);
}

View File

@ -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, <corenr> */
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 */
};

View File

@ -10,7 +10,9 @@
#include <common/debug.h>
#include <drivers/allwinner/axp.h>
#include <drivers/allwinner/sunxi_rsb.h>
#include <lib/mmio.h>
#include <sunxi_cpucfg.h>
#include <sunxi_def.h>
#include <sunxi_mmap.h>
#include <sunxi_private.h>
@ -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));
}