Introduce remoteproc support for Corstone-1000 external systems The Corstone-1000 IoT Reference Design Platform supports up to two external systems processors. These processors can be switched on or off using their reset registers. For more details, please see the SSE-710 External System Remote Processor binding [1] and the SSE-710 Host Base System Control binding [2]. The reset registers are MMIO mapped registers accessed using regmap. [1]: Documentation/devicetree/bindings/remoteproc/arm,sse710-extsys.yaml [2]: Documentation/devicetree/bindings/arm/arm,sse710-host-base-sysctrl.yaml Signed-off-by: Abdellatif El Khlifi <abdellatif.elkhlifi@xxxxxxx> --- drivers/remoteproc/Kconfig | 14 + drivers/remoteproc/Makefile | 1 + drivers/remoteproc/corstone1000_rproc.c | 350 ++++++++++++++++++++++++ 3 files changed, 365 insertions(+) create mode 100644 drivers/remoteproc/corstone1000_rproc.c diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig index 0f0862e20a93..a0ff5d4f2319 100644 --- a/drivers/remoteproc/Kconfig +++ b/drivers/remoteproc/Kconfig @@ -379,6 +379,20 @@ config XLNX_R5_REMOTEPROC It's safe to say N if not interested in using RPU r5f cores. +config CORSTONE1000_REMOTEPROC + tristate "Arm Corstone-1000 remoteproc support" + depends on ARM64 || (HAS_IOMEM && COMPILE_TEST) + help + Say y here to support Arm Corstone-1000 remote processors via the + remote processor framework. + + Corstone-1000 remote processors are controlled with a reset status + and control registers. The driver also supports control of multiple + remote cores at the same time. + + It's safe to say N here if not interested in utilizing remote + processors. + endif # REMOTEPROC endmenu diff --git a/drivers/remoteproc/Makefile b/drivers/remoteproc/Makefile index 5ff4e2fee4ab..e017f75143e3 100644 --- a/drivers/remoteproc/Makefile +++ b/drivers/remoteproc/Makefile @@ -40,3 +40,4 @@ obj-$(CONFIG_TI_K3_DSP_REMOTEPROC) += ti_k3_dsp_remoteproc.o obj-$(CONFIG_TI_K3_M4_REMOTEPROC) += ti_k3_m4_remoteproc.o obj-$(CONFIG_TI_K3_R5_REMOTEPROC) += ti_k3_r5_remoteproc.o obj-$(CONFIG_XLNX_R5_REMOTEPROC) += xlnx_r5_remoteproc.o +obj-$(CONFIG_CORSTONE1000_REMOTEPROC) += corstone1000_rproc.o diff --git a/drivers/remoteproc/corstone1000_rproc.c b/drivers/remoteproc/corstone1000_rproc.c new file mode 100644 index 000000000000..bf351af6a1c3 --- /dev/null +++ b/drivers/remoteproc/corstone1000_rproc.c @@ -0,0 +1,350 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Arm Corstone-1000 Remoteproc driver + * + * The driver adds remoteproc support for the external cores used in Arm + * Corstone-1000 IoT Reference Design Platform [1][2] + * [1] Arm Corstone-1000 Technical Overview: https://developer.arm.com/documentation/102360/0000 + * [2] Arm Corstone SSE-710 Subsystem Technical Reference Manual: https://developer.arm.com/documentation/102342/0000 + * + * Copyright (C) 2024 ARM Ltd. + */ + +#include <linux/bitfield.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/firmware.h> +#include <linux/kernel.h> +#include <linux/mfd/syscon.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/remoteproc.h> + +#include "remoteproc_internal.h" + +/** + * struct corstone1000_rproc - Arm remote processor instance + * @rproc: rproc handler + * @regmap: MMIO register map + * @ctrl_reg: control register offset + * @state_reg: state register offset + */ +struct corstone1000_rproc { + struct rproc *rproc; + struct regmap *regmap; + u16 ctrl_reg; + u16 state_reg; +}; + +/* Definitions for Arm Corstone-1000 External System */ + +/* External Systems identifiers */ +#define EXT_SYS0_ID (0) /* External System 0 ID */ +#define EXT_SYS1_ID (1) /* External System 1 ID */ + +/* External System 0 registers offset */ +#define EXT_SYS0_RST_CTRL (0x310) /* Reset Control register */ +#define EXT_SYS0_RST_ST (0x314) /* Reset Status register */ + +/* External System 1 registers offset */ +#define EXT_SYS1_RST_CTRL (0x318) /* Reset Control register */ +#define EXT_SYS1_RST_ST (0x31c) /* Reset Status register */ + +/* External System Reset Control register bit definitions */ +#define EXTSYS_RST_CTRL_CPUWAIT BIT(0) /* CPU Wait control */ +#define EXTSYS_RST_CTRL_RST_REQ BIT(1) /*Reset request */ + +/* Status of reset request bits */ +#define EXTSYS_RST_ACK_MASK GENMASK(2, 1) +#define GET_EXTSYS_RST_ST_RST_ACK(x) ((u8)(FIELD_GET(EXTSYS_RST_ACK_MASK, \ + (x)))) + +/* Possible values for the Status of reset request */ +#define EXTSYS_RST_ACK_NO_RESET_REQ (0x0) +#define EXTSYS_RST_ACK_NOT_COMPLETE (0x1) +#define EXTSYS_RST_ACK_COMPLETE (0x2) +#define EXTSYS_RST_ACK_RESERVED (0x3) + +/* Polling settings used when reading the Status of reset request */ +#define EXTSYS_RST_ACK_POLL_TRIES (3) +#define EXTSYS_RST_ACK_POLL_TIMEOUT (1000) + +static int corstone1000_rproc_extsys_poll_rst_ack(struct rproc *rproc, + u8 exp_ack, u8 *rst_ack); + +/** + * corstone1000_rproc_start() - custom start operation + * @rproc: pointer to the remote processor object + * + * Start function for Corstone-1000 External System. + * Allow the External System core start execute instructions. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +static int corstone1000_rproc_start(struct rproc *rproc) +{ + struct corstone1000_rproc *priv = rproc->priv; + + /* CPUWAIT signal of the External System is de-asserted */ + + return regmap_write_bits(priv->regmap, priv->ctrl_reg, + EXTSYS_RST_CTRL_CPUWAIT, + (unsigned int)~EXTSYS_RST_CTRL_CPUWAIT); +} + +/** + * corstone1000_rproc_stop() - custom stop operation + * @rproc: pointer to the remote processor object + * + * Reset all logic within the External System, the core will be in a halt state. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +static int corstone1000_rproc_stop(struct rproc *rproc) +{ + struct corstone1000_rproc *priv = rproc->priv; + u8 rst_ack, req_status; + int ret; + + ret = regmap_write_bits(priv->regmap, priv->ctrl_reg, + EXTSYS_RST_CTRL_RST_REQ, + EXTSYS_RST_CTRL_RST_REQ); + if (ret) + return ret; + + ret = corstone1000_rproc_extsys_poll_rst_ack(rproc, + EXTSYS_RST_ACK_COMPLETE | + EXTSYS_RST_ACK_NOT_COMPLETE, + &rst_ack); + if (ret) + return ret; + + req_status = rst_ack; + + ret = regmap_write_bits(priv->regmap, priv->ctrl_reg, + EXTSYS_RST_CTRL_RST_REQ, + (unsigned int)~EXTSYS_RST_CTRL_RST_REQ); + if (ret) + return ret; + + ret = corstone1000_rproc_extsys_poll_rst_ack(rproc, + EXTSYS_RST_ACK_NO_RESET_REQ, + &rst_ack); + if (ret) + return ret; + + if (req_status == EXTSYS_RST_ACK_COMPLETE) + return 0; + + return -EACCES; +} + +/** + * corstone1000_rproc_parse_fw() - Parse firmware operation + * @rproc: pointer to the remote processor object + * @fw: pointer to the firmware + * + * Does nothing currently. No resource information needed from the firmware + * image. + * + * Return: + * + * 0 for success. + */ +static int corstone1000_rproc_parse_fw(struct rproc *rproc, + const struct firmware *fw) +{ + return 0; +} + +/** + * corstone1000_rproc_load() - Load firmware to memory operation + * @rproc: pointer to the remote processor object + * @fw: pointer to the firmware + * + * The remoteproc subsystem expects a firmware image to be provided and loaded + * into memory. In case of Corstone-1000, the firmware is already loaded before + * the Corstone-1000 is powered on and this is done through the FPGA board + * bootloader in case of the FPGA target. Or by the Corstone-1000 FVP model + * when using the FVP target. + * + * Return: + * + * 0 for success. + */ +static int corstone1000_rproc_load(struct rproc *rproc, + const struct firmware *fw) +{ + return 0; +} + +static const struct rproc_ops corstone1000_rproc_ops = { + .start = corstone1000_rproc_start, + .stop = corstone1000_rproc_stop, + .load = corstone1000_rproc_load, + .parse_fw = corstone1000_rproc_parse_fw, +}; + +/** + * corstone1000_rproc_extsys_poll_rst_ack() - poll RST_ACK bits + * @rproc: pointer to the remote processor object + * @exp_ack: expected bits value + * @rst_ack: bits value read + * + * Tries to read RST_ACK bits until the timeout expires. + * EXTSYS_RST_ACK_POLL_TRIES tries are made, + * every EXTSYS_RST_ACK_POLL_TIMEOUT milliseconds. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +static int corstone1000_rproc_extsys_poll_rst_ack(struct rproc *rproc, + u8 exp_ack, u8 *rst_ack) +{ + struct device *dev; + struct corstone1000_rproc *priv; + int tries = EXTSYS_RST_ACK_POLL_TRIES; + int ret; + unsigned long timeout; + u32 state_reg; + + if (!rproc || !rst_ack) + return -ENODATA; + + dev = rproc->dev.parent; + priv = rproc->priv; + + do { + ret = regmap_read(priv->regmap, priv->state_reg, &state_reg); + if (ret) + return ret; + + *rst_ack = GET_EXTSYS_RST_ST_RST_ACK(state_reg); + + if (*rst_ack == EXTSYS_RST_ACK_RESERVED) { + dev_err(dev, "unexpected RST_ACK value: 0x%x\n", + *rst_ack); + return -EINVAL; + } + + /* + * when RST_REQ bit is cleared (No reset requested) + * RST_ACK is expected to be 00 (No reset requested) + */ + if (exp_ack == EXTSYS_RST_ACK_NO_RESET_REQ && + *rst_ack == exp_ack) + return 0; + + /* + * when a reset is requested (RST_REQ set) , RST_ACK is either + * 01 (Reset request unable to complete) or 10 (Reset request + * complete) + */ + if (*rst_ack & exp_ack) + return 0; + + timeout = msleep_interruptible(EXTSYS_RST_ACK_POLL_TIMEOUT); + + if (timeout) { + dev_err(dev, "polling RST_ACK aborted\n"); + return -ECONNABORTED; + } + } while (--tries); + + dev_err(dev, "polling RST_ACK timed out\n"); + + return -ETIMEDOUT; +} + +/** + * corstone1000_rproc_probe() - the platform device probe + * @pdev: the platform device + * + * Setup an rproc device and regmap using syscon + * + * Return: + * + * 0 on success. Otherwise, failure + */ +static int corstone1000_rproc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct device_node *parent; + struct corstone1000_rproc *priv; + struct rproc *rproc; + const char *fw_name; + struct regmap *regmap; + int ret; + u8 extsys_id = EXT_SYS0_ID; + + ret = rproc_of_parse_firmware(dev, 0, &fw_name); + if (ret) + return dev_err_probe(dev, ret, + "can't parse firmware-name from DT\n"); + + dev_dbg(dev, "firmware-name: %s\n", fw_name); + + rproc = devm_rproc_alloc(dev, np->name, &corstone1000_rproc_ops, fw_name, + sizeof(*priv)); + if (!rproc) + return dev_err_probe(dev, -ENOMEM, + "can't allocate rproc device\n"); + + priv = rproc->priv; + priv->rproc = rproc; + + parent = of_get_parent(dev->of_node); /* parent is syscon node */ + regmap = syscon_node_to_regmap(parent); + of_node_put(parent); + if (IS_ERR_OR_NULL(regmap)) + return dev_err_probe(dev, PTR_ERR(regmap), + "can't read syscon node\n"); + priv->regmap = regmap; + + ret = of_property_read_u8(np, "#extsys-id", &extsys_id); + if (ret) + return dev_err_probe(dev, ret, "can't read '#extsys-id' property\n"); + + if (extsys_id == EXT_SYS0_ID) { + priv->ctrl_reg = EXT_SYS0_RST_CTRL; + priv->state_reg = EXT_SYS0_RST_ST; + } else { + priv->ctrl_reg = EXT_SYS1_RST_CTRL; + priv->state_reg = EXT_SYS1_RST_ST; + } + + platform_set_drvdata(pdev, priv); + + ret = devm_rproc_add(dev, rproc); + if (ret) + return dev_err_probe(dev, ret, "can't add remote processor\n"); + + return 0; +} + +static const struct of_device_id corstone1000_rproc_of_match[] = { + { .compatible = "arm,sse710-extsys"}, + { /* sentinel */}, +}; +MODULE_DEVICE_TABLE(of, corstone1000_rproc_of_match); + +static struct platform_driver corstone1000_rproc_driver = { + .probe = corstone1000_rproc_probe, + .driver = { + .name = "corstone1000-rproc", + .of_match_table = corstone1000_rproc_of_match, + }, +}; +module_platform_driver(corstone1000_rproc_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Arm Corstone-1000 Remote Processor Control Driver"); +MODULE_AUTHOR("Abdellatif El Khlifi <abdellatif.elkhlifi@xxxxxxx>"); -- 2.25.1