From 9f71b271dbe5fa3cd6cef0f803a4db82aca286ee Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Thu, 16 Apr 2026 00:12:43 +0200 Subject: remoteproc: renesas: Add Renesas R-Car Gen5 remote processor driver Add R-Car Gen5 RSIP controller remoteproc driver capable of starting the SCP, Cortex-R52 and Cortex-A720 cores in Renesas R-Car R8A78000 X5H SoC. The SCP core is started by releasing the core from reset, the Cortex-R52 and Cortex-A720 are started using the SCP SCMI call. The entry point for SCP core is fixed to its STCM, entry points for Cortex-R52 and Cortex-A720 are set during rproc load. Signed-off-by: Marek Vasut --- drivers/remoteproc/Kconfig | 9 + drivers/remoteproc/Makefile | 1 + drivers/remoteproc/renesas_rsip.c | 358 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 368 insertions(+) create mode 100644 drivers/remoteproc/renesas_rsip.c diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig index f07ae903a28..7f9e35283f9 100644 --- a/drivers/remoteproc/Kconfig +++ b/drivers/remoteproc/Kconfig @@ -48,6 +48,15 @@ config REMOTEPROC_RENESAS_APMU Say 'y' here to add support for Renesas R-Car Gen4 Cortex-R52 processor via the remoteproc framework. +config REMOTEPROC_RENESAS_RSIP + bool "Support for Renesas R-Car Gen5 RSIP start of SCP/CR52/CA720 processors" + select REMOTEPROC + depends on ARCH_RENESAS && RCAR_GEN5 && DM && OF_CONTROL + help + Say 'y' here to add support for Renesas R-Car Gen5 RSIP + start of SCP, Cortex-R52 and Cortex-A720 processors via + the remoteproc framework. + config REMOTEPROC_SANDBOX bool "Support for Test processor for Sandbox" select REMOTEPROC diff --git a/drivers/remoteproc/Makefile b/drivers/remoteproc/Makefile index 7ea8023c50b..250915d0de4 100644 --- a/drivers/remoteproc/Makefile +++ b/drivers/remoteproc/Makefile @@ -11,6 +11,7 @@ obj-$(CONFIG_K3_SYSTEM_CONTROLLER) += k3_system_controller.o obj-$(CONFIG_REMOTEPROC_ADI_SC5XX) += adi_sc5xx_rproc.o obj-$(CONFIG_REMOTEPROC_IMX) += imx_rproc.o obj-$(CONFIG_REMOTEPROC_RENESAS_APMU) += renesas_apmu.o +obj-$(CONFIG_REMOTEPROC_RENESAS_RSIP) += renesas_rsip.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_rsip.c b/drivers/remoteproc/renesas_rsip.c new file mode 100644 index 00000000000..76ccaf93b1a --- /dev/null +++ b/drivers/remoteproc/renesas_rsip.c @@ -0,0 +1,358 @@ +// SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause +/* + * Copyright (C) 2026 Renesas Electronics Corp. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* R-Car X5H contains 1 SCP core, 6 lockstep Cortex-R52 and 32 Cortex-A720AE cores. */ +#define RCAR5_SCP_CORES 1 +#define RCAR5_CR52_CORES 12 +#define RCAR5_CA720_CORES 32 + +#define SCP_BASE 0xc1340000 +#define SCP_CPUWAIT (SCP_BASE + 0x30) +#define SCP_CPUWAIT_WAIT BIT(0) +#define SCP_STCM 0xc1000000 + +struct scp_scmi_shmem { + u32 reserved0; + u32 status; + u64 reserved1; + u32 flags; + u32 length; + u32 message_header; + u8 payload[]; +}; + +struct scp_scmi_pd_power_state_set_a2p { + u32 flags; + u32 domain_id; + u32 power_state; + u32 boot_addr; +}; + +/* The addresses in range 0x08000000..0x1fffffff are incremented by 0xa0000000 */ +#define MFIS_COMMON_BASE 0xb89e1000 +#define MFIS_SCP_WACNTR (MFIS_COMMON_BASE + 0x904) + +/* The addresses in range 0x08000000..0x1fffffff are incremented by 0xa0000000 */ +#define MFIS_SCP_BASE 0xb8840000 +#define MFIS_SCP_REICR8 (MFIS_SCP_BASE + 0x28004) +#define MFIS_SCP_CODEVALUE 0xacc00000 +#define MFIS_SCP_REG_MASK GENMASK(19, 0) + +/** + * mfis_scp_unlock() - Release MFIS-SCP lock + * @lck: MFIS lock register + */ +static void mfis_scp_unlock(const u32 lck) +{ + writel(MFIS_SCP_CODEVALUE | (lck & MFIS_SCP_REG_MASK), MFIS_SCP_WACNTR); +} + +#define SCP_SCMI_SHMEM_AREA09 0xc1060800 +#define SCP_SCMI_STATUS_MASK 0x3 +#define SCP_SCMI_STATUS_FREE 0x1 + +/** + * scp_wait_fw_free() - Wait for SCP channel to be free for communication + */ +static void scp_wait_fw_free(void) +{ + struct scp_scmi_shmem *shmem = (struct scp_scmi_shmem *)SCP_SCMI_SHMEM_AREA09; + + while ((shmem->status & SCP_SCMI_STATUS_MASK) != SCP_SCMI_STATUS_FREE) + mdelay(1); +} + +/** + * scp_send_interrupt() - Raise interrupt on SCP side + */ +static void scp_send_interrupt(void) +{ + mfis_scp_unlock(MFIS_SCP_REICR8); + /* Send SCP IRQ */ + writel(1, MFIS_SCP_REICR8); +} + +/* SCMI power domain IDs */ +#define SCMI_PD_CORE_RT_CORE00 117 +#define SCMI_PD_CORE_AP_CORE00 129 + +/* + * FIXME: This is custom extension to the SCMI PD protocol: + * - Protocol 0x11 (PD) + * - Command 0x11 (POWER_STATE_SET_BOOTADDR - custom) + * This must be removed when proper upstream SCP port exists + */ +#define SCMI_PD_POWER_STATE_SET_BOOTADDR 0x4411 + +/** + * scp_cpu_core_start() - Boot CPU core by invoking SCP via SCMI + * @core: CPU core to boot + * @ep: Entry point + */ +static void scp_cpu_core_start(const u32 core, const u32 ep) +{ + struct scp_scmi_shmem *shmem = (struct scp_scmi_shmem *)SCP_SCMI_SHMEM_AREA09; + struct scp_scmi_pd_power_state_set_a2p scmi_parameter = { + .flags = 1, /* Asynchronous power transition using APMU */ + .domain_id = core, + .power_state = 0, /* Power on */ + .boot_addr = ep, + }; + u32 status; + + /* Wait for SCP to be free, then set it busy */ + scp_wait_fw_free(); + shmem->status &= ~SCP_SCMI_STATUS_FREE; + + /* Set up the message and copy it to SHMEM */ + shmem->message_header = SCMI_PD_POWER_STATE_SET_BOOTADDR; + memcpy(shmem->payload, &scmi_parameter, sizeof(scmi_parameter)); + shmem->length = sizeof(shmem->message_header) + sizeof(scmi_parameter); + + /* Send message to SCP and wait for completion */ + scp_send_interrupt(); + scp_wait_fw_free(); + + /* Read back the result */ + status = readl((uintptr_t)shmem->payload); + if (status) + printf("SCP POWER_STATE_SET failed, status=0x%x\n", status); +} + +/** + * struct renesas_rsip_rproc_privdata - remote processor private data + * @core_id: CPU core id + * @ep: Entry point + */ +struct renesas_rsip_rproc_privdata { + ulong core_id; + ulong ep; +}; + +/** + * renesas_rsip_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_rsip_rproc_load(struct udevice *dev, ulong addr, ulong size) +{ + struct renesas_rsip_rproc_privdata *priv = dev_get_priv(dev); + + priv->ep = addr; + + if (priv->core_id == 0) /* SCP */ + memcpy((void *)SCP_STCM, (void *)addr, size); + + return 0; +} + +/** + * renesas_rsip_rproc_start() - Start the remote processor + * @dev: corresponding remote processor device + * + * Return: 0 if all went ok, else corresponding -ve error + */ +static int renesas_rsip_rproc_start(struct udevice *dev) +{ + struct renesas_rsip_rproc_privdata *priv = dev_get_priv(dev); + int scmi_core; + + if (priv->core_id == 0) { + /* SCP */ + clrbits_le32(SCP_CPUWAIT, SCP_CPUWAIT_WAIT); + return 0; + } else if (priv->core_id >= RCAR5_SCP_CORES && + priv->core_id < RCAR5_SCP_CORES + RCAR5_CR52_CORES) { + /* CR52 */ + scmi_core = priv->core_id - RCAR5_SCP_CORES + + SCMI_PD_CORE_RT_CORE00; + } else if (priv->core_id >= RCAR5_SCP_CORES + RCAR5_CR52_CORES && + priv->core_id < RCAR5_SCP_CORES + RCAR5_CR52_CORES + RCAR5_CA720_CORES) { + /* CA720 */ + scmi_core = priv->core_id - RCAR5_SCP_CORES - RCAR5_CR52_CORES + + SCMI_PD_CORE_AP_CORE00; + } + + scp_cpu_core_start(scmi_core, priv->ep); + + return 0; +} + +/** + * renesas_rsip_rproc_stop() - Stop the remote processor + * @dev: corresponding remote processor device + * + * Return: 0 if all went ok, else corresponding -ve error + */ +static int renesas_rsip_rproc_stop(struct udevice *dev) +{ + struct renesas_rsip_rproc_privdata *priv = dev_get_priv(dev); + + if (priv->core_id == 0) { /* SCP */ + setbits_le32(SCP_CPUWAIT, SCP_CPUWAIT_WAIT); + return 0; + } + + return 0; +} + +/** + * renesas_rsip_rproc_reset() - Reset the remote processor + * @dev: corresponding remote processor device + * + * Return: 0 if all went ok, else corresponding -ve error + */ +static int renesas_rsip_rproc_reset(struct udevice *dev) +{ + renesas_rsip_rproc_stop(dev); + renesas_rsip_rproc_start(dev); + return 0; +} + +/** + * renesas_rsip_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_rsip_rproc_is_running(struct udevice *dev) +{ + /* We assume the core is stopped. */ + return 1; +} + +/** + * renesas_rsip_rproc_init() - Initialize the remote processor + * @dev: corresponding remote processor device + * + * Return: 0 if all went ok, else corresponding -ve error + */ +static int renesas_rsip_rproc_init(struct udevice *dev) +{ + return 0; +} + +/** + * renesas_rsip_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 + * @is_iomem: optional pointer filled in to indicate if @da is iomapped memory + * + * Return: converted virtual address + */ +static void *renesas_rsip_rproc_device_to_virt(struct udevice *dev, ulong da, + ulong size, bool *is_iomem) +{ + return (void *)da; +} + +static const struct dm_rproc_ops renesas_rsip_rproc_ops = { + .init = renesas_rsip_rproc_init, + .load = renesas_rsip_rproc_load, + .start = renesas_rsip_rproc_start, + .stop = renesas_rsip_rproc_stop, + .reset = renesas_rsip_rproc_reset, + .is_running = renesas_rsip_rproc_is_running, + .device_to_virt = renesas_rsip_rproc_device_to_virt, +}; + +/** + * renesas_rsip_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_rsip_rproc_of_to_plat(struct udevice *dev) +{ + struct renesas_rsip_rproc_privdata *priv = dev_get_priv(dev); + + priv->core_id = dev_get_driver_data(dev); + + return 0; +} + +U_BOOT_DRIVER(renesas_rsip_core) = { + .name = "rcar-rsip-core", + .id = UCLASS_REMOTEPROC, + .ops = &renesas_rsip_rproc_ops, + .of_to_plat = renesas_rsip_rproc_of_to_plat, + .priv_auto = sizeof(struct renesas_rsip_rproc_privdata), +}; + +/** + * renesas_rsip_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_rsip_rproc_bind(struct udevice *parent) +{ + ofnode pnode = dev_ofnode(parent); + struct udevice *cdev; + struct driver *cdrv; + char name[32]; + ulong i; + int ret; + + cdrv = lists_driver_lookup_name("rcar-rsip-core"); + if (!cdrv) + return -ENOENT; + + /* Singleton SCP core is core 0 */ + ret = device_bind_with_driver_data(parent, cdrv, + strdup("rcar-rsip-scp"), + 0, pnode, &cdev); + if (ret) + return ret; + + /* Cores 1..13 are Cortex-R52 */ + for (i = 0; i < RCAR5_CR52_CORES; i++) { + snprintf(name, sizeof(name), "rcar-rsip-cr.%ld", i); + ret = device_bind_with_driver_data(parent, cdrv, strdup(name), + i + RCAR5_SCP_CORES, pnode, + &cdev); + if (ret) + return ret; + } + + /* Cores 14..46 are Cortex-A720 */ + for (i = 0; i < RCAR5_CA720_CORES; i++) { + snprintf(name, sizeof(name), "rcar-rsip-ca.%ld", i); + ret = device_bind_with_driver_data(parent, cdrv, strdup(name), + i + RCAR5_SCP_CORES + RCAR5_CR52_CORES, + pnode, &cdev); + if (ret) + return ret; + } + + return 0; +} + +static const struct udevice_id renesas_rsip_rproc_ids[] = { + { .compatible = "renesas,r8a78000-rproc" }, + { } +}; + +U_BOOT_DRIVER(renesas_rsip_rproc) = { + .name = "rcar-rsip-rproc", + .of_match = renesas_rsip_rproc_ids, + .id = UCLASS_NOP, + .bind = renesas_rsip_rproc_bind, +}; -- cgit v1.2.3