aboutsummaryrefslogtreecommitdiff
path: root/plat/allwinner/common/sunxi_common.c
diff options
context:
space:
mode:
authorAndre Przywara <andre.przywara@arm.com>2018-10-14 11:45:41 +0100
committerAndre Przywara <andre.przywara@arm.com>2018-10-20 16:23:59 +0100
commit11480b9010d4b188e06395b7ac01b4aa164a110d (patch)
tree9bb050fb8c602739acfec3e3f3955ccbf2020dfe /plat/allwinner/common/sunxi_common.c
parentccd3ab2de8c02181507bd73a377ac4219d165263 (diff)
downloadtrusted-firmware-a-11480b9010d4b188e06395b7ac01b4aa164a110d.tar.gz
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>
Diffstat (limited to 'plat/allwinner/common/sunxi_common.c')
-rw-r--r--plat/allwinner/common/sunxi_common.c51
1 files changed, 51 insertions, 0 deletions
diff --git a/plat/allwinner/common/sunxi_common.c b/plat/allwinner/common/sunxi_common.c
index a39c149ee6..2eb26a91bc 100644
--- a/plat/allwinner/common/sunxi_common.c
+++ b/plat/allwinner/common/sunxi_common.c
@@ -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);
+}