summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--plat/allwinner/common/include/sunxi_private.h2
-rw-r--r--plat/allwinner/common/sunxi_common.c51
2 files changed, 53 insertions, 0 deletions
diff --git a/plat/allwinner/common/include/sunxi_private.h b/plat/allwinner/common/include/sunxi_private.h
index a2d7c38c..1e1b0a4d 100644
--- a/plat/allwinner/common/include/sunxi_private.h
+++ b/plat/allwinner/common/include/sunxi_private.h
@@ -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 */
diff --git a/plat/allwinner/common/sunxi_common.c b/plat/allwinner/common/sunxi_common.c
index a39c149e..2eb26a91 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);
+}