remoteproc: renesas: Add Renesas R-Car Gen4 remote processor driver
authorMarek Vasut <marek.vasut+renesas@mailbox.org>
Fri, 20 Dec 2024 00:02:14 +0000 (01:02 +0100)
committerMarek Vasut <marek.vasut+renesas@mailbox.org>
Sun, 29 Dec 2024 15:55:31 +0000 (16:55 +0100)
Add R-Car Gen4 APMU controller remoteproc driver capable of starting
the Cortex-R52 cores in Renesas R8A779G0 V4H/V4M SoC. The APMU IP is
in fact a power management unit capable of additional operations, but
those are not used by U-Boot so far.

This requires slight adjustment to the SPL entry point code, as that
is being executed on the Cortex-R52 #0 and the Cortex-R52 #0 enters an
endless loop once it starts the rest of the SPL on Cortex-A76 core.
The endless loop now checks for content of APMU CRBARP registers and
tests whether valid VLD_BARP and BAREN_VALID bits are set, if so, the
Cortex-R52 core exits the endless loop and jumps to address started
in CRBARP[31:18] register in ARM mode, which is a trampoline code to
jump to the final entry point.

The trampoline code is in place to avoid limitation of CRBARP[31:18]
address field, which limits the core start address to memory addresses
aligned to 0x40000 or 256 kiB . The trampoline is placed at 0x40000
aligned address and jumps to the final entry point, which can be at
an address with arbitrary alignment at instruction granularity.

Signed-off-by: Marek Vasut <marek.vasut+renesas@mailbox.org>
arch/arm/mach-renesas/include/mach/boot0.h
drivers/remoteproc/Kconfig
drivers/remoteproc/Makefile
drivers/remoteproc/renesas_apmu.c [new file with mode: 0644]

index 1bbfec9..fc68ffc 100644 (file)
@@ -28,19 +28,19 @@ _start:
        /* r1=0xe6170800 */
        .inst   0xe3a004e6      /* mov     r0,     #0xe6000000 */
        .inst   0xe3801817      /* orr     r1, r0, #0x170000 */
-       .inst   0xe3811b02      /* orr     r1, r1, #0x800 */
+       .inst   0xe3814b02      /* orr     r4, r1, #0x800 */
 
        /* r0=0xe6280000 */
        .inst   0xe380070a      /* orr     r0, r0, #0x280000 */
 
        /* APMU_RVBARPLC0 = (address of 'b reset' below) | CA_CORE0_VLD_RVBARP */
-       .inst   0xe28f3068      /* add     r3, pc, #0x68 */
+       .inst   0xe28f3088      /* add     r3, pc, #0x88 */
        .inst   0xe3833001      /* orr     r3, r3, #1 */
-       .inst   0xe5813038      /* str     r3, [r1, #56]   @ 0x38 */
+       .inst   0xe5843038      /* str     r3, [r4, #56]   @ 0x38 */
 
        /* APMU_RVBARPHC0 = 0 */
-       .inst   0xe3a03000      /* mov     r3, #0 */
-       .inst   0xe581303c      /* str     r3, [r1, #60]   @ 0x3c */
+       .inst   0xe3a05000      /* mov     r5, #0 */
+       .inst   0xe584503c      /* str     r5, [r4, #60]   @ 0x3c */
 
        /* PRR & 0xff00 ?= 0x5c00, test if this is V4H or V4M */
        .inst   0xe3a024ff      /* mov     r2, #0xff000000 */
@@ -67,13 +67,21 @@ _start:
        /* } */
        /* APMU_PWRCTRLC0 = CA_CORE0_WUP_REQ */
        .inst   0xe3a03001      /* mov     r3, #1 */
-       .inst   0xe5813000      /* str     r3, [r1] */
-       /* Endless loop */
+       .inst   0xe5843000      /* str     r3, [r4] */
+       /* Test for APMU_CRBARP valid BAR flags and jump to CR entry point */
+       .inst   0xe3814c03      /* orr     r4, r1, #768    @ 0x300 */
+       .inst   0xe584503c      /* str     r5, [r4, #60]   @ 0x3c */
+       .inst   0xe594203c      /* ldr     r2, [r4, #60]   @ 0x3c */
+       .inst   0xe20230ff      /* and     r3, r2, #255    @ 0xff */
+       .inst   0xe3530011      /* cmp     r3, #17 */
+       .inst   0x1afffffb      /* bne     78 <reset-0x28> */
+       .inst   0xe1a02922      /* lsr     r2, r2, #18 */
+       .inst   0xe1a02902      /* lsl     r2, r2, #18 */
+       .inst   0xe1a0f002      /* mov     pc, r2 */
+       .inst   0xeafffffe      /* b       94 <reset-0xc> */
        .inst   0xe1a00000      /* nop                     @ (mov r0, r0) */
-       .inst   0xeafffffd      /* b       70 <reset-0x10> */
        .inst   0xe1a00000      /* nop                     @ (mov r0, r0) */
-       .inst   0xe1a00000      /* nop                     @ (mov r0, r0) */
-       /* Offset 0x80 */
+       /* Offset 0xa0 */
 #endif
        b       reset
 #endif
index a49802c..2790b16 100644 (file)
@@ -22,6 +22,14 @@ config K3_SYSTEM_CONTROLLER
        help
          Say 'y' here to add support for TI' K3 System Controller.
 
+config REMOTEPROC_RENESAS_APMU
+       bool "Support for Renesas R-Car Gen4 APMU start of CR52 processor"
+       select REMOTEPROC
+       depends on ARCH_RENESAS && RCAR_GEN4 && DM && OF_CONTROL
+       help
+         Say 'y' here to add support for Renesas R-Car Gen4 Cortex-A52
+         processor via the remoteproc framework.
+
 config REMOTEPROC_SANDBOX
        bool "Support for Test processor for Sandbox"
        select REMOTEPROC
index 801b096..3a092b7 100644 (file)
@@ -8,6 +8,7 @@ obj-$(CONFIG_$(XPL_)REMOTEPROC) += rproc-uclass.o rproc-elf-loader.o
 
 # Remote proc drivers - Please keep this list alphabetically sorted.
 obj-$(CONFIG_K3_SYSTEM_CONTROLLER) += k3_system_controller.o
+obj-$(CONFIG_REMOTEPROC_RENESAS_APMU) += renesas_apmu.o
 obj-$(CONFIG_REMOTEPROC_SANDBOX) += sandbox_testproc.o
 obj-$(CONFIG_REMOTEPROC_STM32_COPRO) += stm32_copro.o
 obj-$(CONFIG_REMOTEPROC_TI_K3_ARM64) += ti_k3_arm64_rproc.o
diff --git a/drivers/remoteproc/renesas_apmu.c b/drivers/remoteproc/renesas_apmu.c
new file mode 100644 (file)
index 0000000..32d138e
--- /dev/null
@@ -0,0 +1,266 @@
+// SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause
+/*
+ * Copyright (C) 2024 Renesas Electronics Corp.
+ */
+
+#include <asm/io.h>
+#include <dm.h>
+#include <dm/device-internal.h>
+#include <dm/lists.h>
+#include <errno.h>
+#include <hang.h>
+#include <linux/iopoll.h>
+#include <linux/sizes.h>
+#include <malloc.h>
+#include <remoteproc.h>
+
+/* R-Car V4H/V4M contain 3 clusters / 3 cores */
+#define RCAR4_CR52_CORES               3
+
+/* Reset Control Register for Cortex-R52 #n */
+#define APMU_CRRSTCTRL(n)              (0x304 + ((n) * 0x40))
+#define APMU_CRRSTCTRL_CR52RST         BIT(0)
+
+/* Base Address Register for Cortex-R52 #n */
+#define APMU_CRBARP(n)                 (0x33c + ((n) * 0x40))
+#define APMU_CRBARP_CR_VLD_BARP                BIT(0)
+#define APMU_CRBARP_CR_BAREN_VALID     BIT(4)
+#define APMU_CRBARP_CR_RBAR_MASK       0xfffc0000
+#define APMU_CRBARP_CR_RBAR_ALIGN      0x40000
+
+/**
+ * struct renesas_apmu_rproc_privdata - remote processor private data
+ * @regs:              controller registers
+ * @core_id:           CPU core id
+ * @trampoline:                jump trampoline code
+ */
+struct renesas_apmu_rproc_privdata {
+       void __iomem    *regs;
+       ulong           core_id;
+       u32             *trampoline;
+};
+
+/*
+ * CRBARP address is aligned to 0x40000 / 256 kiB , this trampoline
+ * allows arbitrary address alignment at instruction granularity.
+ */
+static const u32 renesas_apmu_rproc_trampoline[4] = {
+       0xe59f0004,     /* ldr r0, [pc, #4] */
+       0xe1a0f000,     /* mov pc, r0 */
+       0xeafffffe,     /* 1: b 1b */
+       0xabcd1234      /* jump target (rewritten on load) */
+};
+
+/**
+ * renesas_apmu_rproc_load() - Load the remote processor
+ * @dev:       corresponding remote processor device
+ * @addr:      Address in memory where image is stored
+ * @size:      Size in bytes of the image
+ *
+ * Return: 0 if all went ok, else corresponding -ve error
+ */
+static int renesas_apmu_rproc_load(struct udevice *dev, ulong addr, ulong size)
+{
+       struct renesas_apmu_rproc_privdata *priv = dev_get_priv(dev);
+       u32 trampolineaddr = (u32)(uintptr_t)(priv->trampoline);
+
+       priv->trampoline[3] = addr;
+       flush_dcache_range(trampolineaddr,
+                          trampolineaddr +
+                          sizeof(renesas_apmu_rproc_trampoline));
+
+       /* CR52 boot address set */
+       writel(trampolineaddr | APMU_CRBARP_CR_VLD_BARP,
+              priv->regs + APMU_CRBARP(priv->core_id));
+       writel(trampolineaddr | APMU_CRBARP_CR_VLD_BARP | APMU_CRBARP_CR_BAREN_VALID,
+              priv->regs + APMU_CRBARP(priv->core_id));
+
+       return 0;
+}
+
+/**
+ * renesas_apmu_rproc_start() - Start the remote processor
+ * @dev:       corresponding remote processor device
+ *
+ * Return: 0 if all went ok, else corresponding -ve error
+ */
+static int renesas_apmu_rproc_start(struct udevice *dev)
+{
+       struct renesas_apmu_rproc_privdata *priv = dev_get_priv(dev);
+
+       /* Clear APMU_CRRSTCTRL_CR52RST, the only bit in this register */
+       writel(0, priv->regs + APMU_CRRSTCTRL(priv->core_id));
+
+       return 0;
+}
+
+/**
+ * renesas_apmu_rproc_stop() - Stop the remote processor
+ * @dev:       corresponding remote processor device
+ *
+ * Return: 0 if all went ok, else corresponding -ve error
+ */
+static int renesas_apmu_rproc_stop(struct udevice *dev)
+{
+       struct renesas_apmu_rproc_privdata *priv = dev_get_priv(dev);
+
+       /* Set APMU_CRRSTCTRL_CR52RST, the only bit in this register */
+       writel(APMU_CRRSTCTRL_CR52RST,
+              priv->regs + APMU_CRRSTCTRL(priv->core_id));
+
+       return 0;
+}
+
+/**
+ * renesas_apmu_rproc_reset() - Reset the remote processor
+ * @dev:       corresponding remote processor device
+ *
+ * Return: 0 if all went ok, else corresponding -ve error
+ */
+static int renesas_apmu_rproc_reset(struct udevice *dev)
+{
+       renesas_apmu_rproc_stop(dev);
+       renesas_apmu_rproc_start(dev);
+       return 0;
+}
+
+/**
+ * renesas_apmu_rproc_is_running() - Is the remote processor running
+ * @dev:       corresponding remote processor device
+ *
+ * Return: 0 if the remote processor is running, 1 otherwise
+ */
+static int renesas_apmu_rproc_is_running(struct udevice *dev)
+{
+       struct renesas_apmu_rproc_privdata *priv = dev_get_priv(dev);
+
+       return readl(priv->regs + APMU_CRRSTCTRL(priv->core_id)) &
+              APMU_CRRSTCTRL_CR52RST;
+}
+
+/**
+ * renesas_apmu_rproc_init() - Initialize the remote processor CRBAR registers
+ * @dev:       corresponding remote processor device
+ *
+ * Return: 0 if all went ok, else corresponding -ve error
+ */
+static int renesas_apmu_rproc_init(struct udevice *dev)
+{
+       struct renesas_apmu_rproc_privdata *priv = dev_get_priv(dev);
+
+       /* If the core is running already, do nothing. */
+       if (renesas_apmu_rproc_is_running(dev))
+               return 0;
+
+       /* Clear and invalidate CRBARP content */
+       writel(0, priv->regs + APMU_CRBARP(priv->core_id));
+
+       return 0;
+}
+
+/**
+ * renesas_apmu_rproc_device_to_virt() - Convert device address to virtual address
+ * @dev:       corresponding remote processor device
+ * @da:                device address
+ * @size:      Size of the memory region @da is pointing to
+ *
+ * Return: converted virtual address
+ */
+static void *renesas_apmu_rproc_device_to_virt(struct udevice *dev, ulong da,
+                                              ulong size)
+{
+       /*
+        * The Cortex R52 and A76 share the same address space,
+        * this operation is a no-op.
+        */
+       return (void *)da;
+}
+
+static const struct dm_rproc_ops renesas_apmu_rproc_ops = {
+       .init           = renesas_apmu_rproc_init,
+       .load           = renesas_apmu_rproc_load,
+       .start          = renesas_apmu_rproc_start,
+       .stop           = renesas_apmu_rproc_stop,
+       .reset          = renesas_apmu_rproc_reset,
+       .is_running     = renesas_apmu_rproc_is_running,
+       .device_to_virt = renesas_apmu_rproc_device_to_virt,
+};
+
+/**
+ * renesas_apmu_rproc_of_to_plat() - Convert OF data to platform data
+ * @dev:       corresponding remote processor device
+ *
+ * Return: 0 if all went ok, else corresponding -ve error
+ */
+static int renesas_apmu_rproc_of_to_plat(struct udevice *dev)
+{
+       struct renesas_apmu_rproc_privdata *priv = dev_get_priv(dev);
+
+       priv->core_id = dev_get_driver_data(dev);
+
+       priv->regs = dev_read_addr_ptr(dev);
+       if (!priv->regs)
+               return -EINVAL;
+
+       priv->trampoline = memalign(APMU_CRBARP_CR_RBAR_ALIGN,
+                                   sizeof(renesas_apmu_rproc_trampoline));
+       if (!priv->trampoline)
+               return -ENOMEM;
+
+       memcpy(priv->trampoline, renesas_apmu_rproc_trampoline,
+              sizeof(renesas_apmu_rproc_trampoline));
+
+       return 0;
+}
+
+U_BOOT_DRIVER(renesas_apmu_cr52) = {
+       .name           = "rcar-apmu-cr52",
+       .id             = UCLASS_REMOTEPROC,
+       .ops            = &renesas_apmu_rproc_ops,
+       .of_to_plat     = renesas_apmu_rproc_of_to_plat,
+       .priv_auto      = sizeof(struct renesas_apmu_rproc_privdata),
+};
+
+/**
+ * renesas_apmu_rproc_bind() - Bind rproc driver to each core control
+ * @dev:       corresponding remote processor parent device
+ *
+ * Return: 0 if all went ok, else corresponding -ve error
+ */
+static int renesas_apmu_rproc_bind(struct udevice *parent)
+{
+       const ulong cr52cores = RCAR4_CR52_CORES;
+       ofnode pnode = dev_ofnode(parent);
+       struct udevice *cdev;
+       struct driver *cdrv;
+       char name[32];
+       ulong i;
+       int ret;
+
+       cdrv = lists_driver_lookup_name("rcar-apmu-cr52");
+       if (!cdrv)
+               return -ENOENT;
+
+       for (i = 0; i < cr52cores; i++) {
+               snprintf(name, sizeof(name), "rcar-apmu-cr52.%ld", i);
+               ret = device_bind_with_driver_data(parent, cdrv, strdup(name),
+                                                  i, pnode, &cdev);
+               if (ret)
+                       return ret;
+       }
+
+       return 0;
+}
+
+static const struct udevice_id renesas_apmu_rproc_ids[] = {
+       { .compatible = "renesas,r8a779g0-cr52" },
+       { .compatible = "renesas,r8a779h0-cr52" },
+       { }
+};
+
+U_BOOT_DRIVER(renesas_apmu_rproc) = {
+       .name           = "rcar-apmu-rproc",
+       .of_match       = renesas_apmu_rproc_ids,
+       .id             = UCLASS_NOP,
+       .bind           = renesas_apmu_rproc_bind,
+};