allwinner: Prepare for executing code on the management processor

The more recent Allwinner SoCs contain an OpenRISC management
controller (called arisc or CPUS), which shares the bus with the ARM cores,
but runs on a separate power domain. This is meant to handle power
management with the ARM cores off.
There are efforts to run sophisticated firmware on that core
(communicating via SCPI with the ARM world), but for now can use it for
the rather simple task of helping to turn the ARM cores off. As this
cannot be done by ARM code itself (because execution stops at the
first of the three required steps), we can offload some instructions to
this management processor.
This introduces a helper function to hand over a bunch of instructions
and triggers execution. We introduce a bakery lock to avoid two cores
trying to use that (single) arisc core. The arisc code is expected to
put itself into reset after is has finished execution.

Signed-off-by: Andre Przywara <andre.przywara@arm.com>
This commit is contained in:
Andre Przywara 2018-10-14 11:45:41 +01:00
parent ccd3ab2de8
commit 11480b9010
2 changed files with 53 additions and 0 deletions

View File

@ -20,5 +20,7 @@ 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,
int patch_offset, uint16_t param);
#endif /* SUNXI_PRIVATE_H */

View File

@ -4,12 +4,14 @@
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <arch_helpers.h>
#include <debug.h>
#include <errno.h>
#include <mmio.h>
#include <platform.h>
#include <platform_def.h>
#include <sunxi_def.h>
#include <sunxi_mmap.h>
#include <sunxi_private.h>
#include <xlat_tables_v2.h>
@ -157,3 +159,52 @@ 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,
int patch_offset, uint16_t param)
{
uintptr_t arisc_reset_vec = SUNXI_SRAM_A2_BASE - 0x4000 + 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. */
if (patch_offset >= 0 && patch_offset <= (size - 4))
code[patch_offset] = (code[patch_offset] & ~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);
}