From: Mathieu Poirier <mathieu.poirier@linaro.org>
To: abdellatif.elkhlifi@arm.com
Cc: Bjorn Andersson <andersson@kernel.org>,
Rob Herring <robh+dt@kernel.org>,
Liviu Dudau <liviu.dudau@arm.com>,
Sudeep Holla <sudeep.holla@arm.com>,
Lorenzo Pieralisi <lpieralisi@kernel.org>,
Krzysztof Kozlowski <krzysztof.kozlowski+dt@linaro.org>,
Conor Dooley <conor+dt@kernel.org>,
Drew.Reed@arm.com, Adam.Johnston@arm.com,
linux-arm-kernel@lists.infradead.org, devicetree@vger.kernel.org,
linux-kernel@vger.kernel.org, linux-remoteproc@vger.kernel.org
Subject: Re: [PATCH 1/3] remoteproc: Add Arm remoteproc driver
Date: Mon, 4 Mar 2024 11:42:49 -0700 [thread overview]
Message-ID: <ZeYWKVpeFm1+4mlT@p14s> (raw)
In-Reply-To: <20240301164227.339208-2-abdellatif.elkhlifi@arm.com>
Good day Abdellatif,
On Fri, Mar 01, 2024 at 04:42:25PM +0000, abdellatif.elkhlifi@arm.com wrote:
> From: Abdellatif El Khlifi <abdellatif.elkhlifi@arm.com>
>
> introduce remoteproc support for Arm remote processors
>
> The supported remote processors are those that come with a reset
> control register and a reset status register. The driver allows to
> switch on or off the remote processor.
>
> The current use case is Corstone-1000 External System (Cortex-M3).
>
> The driver can be extended to support other remote processors
> controlled with a reset control and a reset status registers.
>
> The driver also supports control of multiple remote processors at the
> same time.
>
> Signed-off-by: Abdellatif El Khlifi <abdellatif.elkhlifi@arm.com>
> ---
> MAINTAINERS | 6 +
> drivers/remoteproc/Kconfig | 18 ++
> drivers/remoteproc/Makefile | 1 +
> drivers/remoteproc/arm_rproc.c | 395 +++++++++++++++++++++++++++++++++
> 4 files changed, 420 insertions(+)
> create mode 100644 drivers/remoteproc/arm_rproc.c
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 8d1052fa6a69..54d6a40feea5 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -1764,6 +1764,12 @@ S: Maintained
> F: Documentation/devicetree/bindings/interrupt-controller/arm,vic.yaml
> F: drivers/irqchip/irq-vic.c
>
> +ARM REMOTEPROC DRIVER
> +M: Abdellatif El Khlifi <abdellatif.elkhlifi@arm.com>
> +L: linux-remoteproc@vger.kernel.org
> +S: Maintained
> +F: drivers/remoteproc/arm_rproc.c
> +
Humm... I'm not sure this is needed for now. You'll be CC'ed in future postings
anyway if someone changes this drivers.
> ARM SMC WATCHDOG DRIVER
> M: Julius Werner <jwerner@chromium.org>
> R: Evan Benn <evanbenn@chromium.org>
> diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig
> index 48845dc8fa85..57fbac454a5d 100644
> --- a/drivers/remoteproc/Kconfig
> +++ b/drivers/remoteproc/Kconfig
> @@ -365,6 +365,24 @@ config XLNX_R5_REMOTEPROC
>
> It's safe to say N if not interested in using RPU r5f cores.
>
> +config ARM_REMOTEPROC
> + tristate "Arm remoteproc support"
> + depends on HAS_IOMEM && ARM64
> + default n
> + help
> + Say y here to support Arm remote processors via the remote
> + processor framework.
> +
> + The supported processors are those that come with a reset control register
> + and a reset status register. The design can be extended to support different
> + processors meeting these requirements.
> + The driver also supports control of multiple remote cores at the same time.
> +
> + Supported remote cores:
> + Corstone-1000 External System (Cortex-M3)
> +
Please remove. The descrition in the Kconfig file should be related to a family
of device and the specific model number found in the driver.
> + It's safe to say N here.
> +
> endif # REMOTEPROC
>
> endmenu
> diff --git a/drivers/remoteproc/Makefile b/drivers/remoteproc/Makefile
> index 91314a9b43ce..73126310835b 100644
> --- a/drivers/remoteproc/Makefile
> +++ b/drivers/remoteproc/Makefile
> @@ -39,3 +39,4 @@ obj-$(CONFIG_STM32_RPROC) += stm32_rproc.o
> obj-$(CONFIG_TI_K3_DSP_REMOTEPROC) += ti_k3_dsp_remoteproc.o
> obj-$(CONFIG_TI_K3_R5_REMOTEPROC) += ti_k3_r5_remoteproc.o
> obj-$(CONFIG_XLNX_R5_REMOTEPROC) += xlnx_r5_remoteproc.o
> +obj-$(CONFIG_ARM_REMOTEPROC) += arm_rproc.o
> diff --git a/drivers/remoteproc/arm_rproc.c b/drivers/remoteproc/arm_rproc.c
> new file mode 100644
> index 000000000000..6afa78ae7ad3
> --- /dev/null
> +++ b/drivers/remoteproc/arm_rproc.c
> @@ -0,0 +1,395 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Copyright 2024 Arm Limited and/or its affiliates <open-source-office@arm.com>
> + *
> + * Authors:
> + * Abdellatif El Khlifi <abdellatif.elkhlifi@arm.com>
> + */
> +
> +#include <linux/delay.h>
> +#include <linux/err.h>
> +#include <linux/firmware.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/platform_device.h>
> +#include <linux/remoteproc.h>
> +
> +#include "remoteproc_internal.h"
> +
> +/**
> + * struct arm_rproc_reset_cfg - remote processor reset configuration
> + * @ctrl_reg: address of the control register
> + * @state_reg: address of the reset status register
> + */
> +struct arm_rproc_reset_cfg {
> + void __iomem *ctrl_reg;
> + void __iomem *state_reg;
> +};
> +
> +struct arm_rproc;
> +
This is useless, please remove.
> +/**
> + * struct arm_rproc_dcfg - Arm remote processor configuration
Configuration? This looks more like operations to me. Please consider
renaming.
> + * @stop: stop callback function
> + * @start: start callback function
> + */
> +struct arm_rproc_dcfg {
> + int (*stop)(struct rproc *rproc);
> + int (*start)(struct rproc *rproc);
> +};
> +
> +/**
> + * struct arm_rproc - Arm remote processor instance
> + * @rproc: rproc handler
> + * @core_dcfg: device configuration pointer
> + * @reset_cfg: reset configuration registers
> + */
> +struct arm_rproc {
> + struct rproc *rproc;
> + const struct arm_rproc_dcfg *core_dcfg;
> + struct arm_rproc_reset_cfg reset_cfg;
> +};
> +
> +/* Definitions for Arm Corstone-1000 External System */
> +
> +#define EXTSYS_RST_CTRL_CPUWAIT BIT(0)
> +#define EXTSYS_RST_CTRL_RST_REQ BIT(1)
> +
> +#define EXTSYS_RST_ACK_MASK GENMASK(2, 1)
> +#define EXTSYS_RST_ST_RST_ACK(x) \
> + ((u8)(FIELD_GET(EXTSYS_RST_ACK_MASK, (x))))
> +
> +#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)
> +
> +#define EXTSYS_RST_ACK_POLL_TRIES (3)
> +#define EXTSYS_RST_ACK_POLL_TIMEOUT (1000)
On my side most of the values above came out misaligned.
> +
> +/**
> + * arm_rproc_start_cs1000_extsys() - custom start function
> + * @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 arm_rproc_start_cs1000_extsys(struct rproc *rproc)
> +{
> + struct arm_rproc *priv = rproc->priv;
> + u32 ctrl_reg;
> +
> + /* CPUWAIT signal of the External System is de-asserted */
> + ctrl_reg = readl(priv->reset_cfg.ctrl_reg);
> + ctrl_reg &= ~EXTSYS_RST_CTRL_CPUWAIT;
> + writel(ctrl_reg, priv->reset_cfg.ctrl_reg);
> +
> + return 0;
> +}
> +
> +/**
> + * arm_rproc_cs1000_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 arm_rproc_cs1000_extsys_poll_rst_ack(struct rproc *rproc,
> + u8 exp_ack, u8 *rst_ack)
> +{
> + struct arm_rproc *priv = rproc->priv;
> + struct device *dev = rproc->dev.parent;
> + u32 state_reg;
> + int tries = EXTSYS_RST_ACK_POLL_TRIES;
> + unsigned long timeout;
> +
struct device *dev = rproc->dev.parent;
struct arm_rproc *priv = rproc->priv;
int tries = EXTSYS_RST_ACK_POLL_TRIES;
unsigned long timeout;
u32 state_reg;
> + do {
> + state_reg = readl(priv->reset_cfg.state_reg);
> + *rst_ack = 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;
> + }
> +
> + /* expected ACK value read */
> + if ((*rst_ack & exp_ack) || (*rst_ack == exp_ack))
I'm not sure why the second condition in this if() statement is needed. As far
as I can tell the first condition will trigger and the second one won't be
reached.
> + 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;
> +}
> +
> +/**
> + * arm_rproc_stop_cs1000_extsys() - custom stop function
> + * @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 arm_rproc_stop_cs1000_extsys(struct rproc *rproc)
> +{
> + struct arm_rproc *priv = rproc->priv;
> + struct device *dev = rproc->dev.parent;
> + u32 ctrl_reg;
> + u8 rst_ack, req_status;
> + int ret;
> +
struct device *dev = rproc->dev.parent;
struct arm_rproc *priv = rproc->priv;
u8 rst_ack, req_status;
u32 ctrl_reg;
int ret;
> + ctrl_reg = readl(priv->reset_cfg.ctrl_reg);
> + ctrl_reg |= EXTSYS_RST_CTRL_RST_REQ;
> + writel(ctrl_reg, priv->reset_cfg.ctrl_reg);
> +
> + ret = arm_rproc_cs1000_extsys_poll_rst_ack(rproc,
> + EXTSYS_RST_ACK_COMPLETE |
> + EXTSYS_RST_ACK_NOT_COMPLETE,
> + &rst_ack);
> + if (ret)
> + return ret;
> +
> + req_status = rst_ack;
> +
> + ctrl_reg = readl(priv->reset_cfg.ctrl_reg);
> + ctrl_reg &= ~EXTSYS_RST_CTRL_RST_REQ;
> + writel(ctrl_reg, priv->reset_cfg.ctrl_reg);
> +
> + ret = arm_rproc_cs1000_extsys_poll_rst_ack(rproc, 0, &rst_ack);
> + if (ret)
> + return ret;
> +
> + if (req_status == EXTSYS_RST_ACK_COMPLETE) {
> + dev_dbg(dev, "the requested reset has been accepted\n");
Please remove
> + return 0;
> + }
> +
> + dev_err(dev, "the requested reset has been denied\n");
There is enough error reporting in arm_rproc_cs1000_extsys_poll_rst_ack(),
please remove.
> + return -EACCES;
> +}
> +
> +static const struct arm_rproc_dcfg arm_rproc_cfg_corstone1000_extsys = {
> + .stop = arm_rproc_stop_cs1000_extsys,
> + .start = arm_rproc_start_cs1000_extsys,
> +};
> +
> +/**
> + * arm_rproc_stop() - Stop function for rproc_ops
> + * @rproc: pointer to the remote processor object
> + *
> + * Calls the stop() callback of the remote core
> + *
> + * Return:
> + *
> + * 0 on success. Otherwise, failure
> + */
> +static int arm_rproc_stop(struct rproc *rproc)
> +{
> + struct arm_rproc *priv = rproc->priv;
> +
> + return priv->core_dcfg->stop(rproc);
> +}
> +
> +/**
> + * arm_rproc_start() - Start function for rproc_ops
> + * @rproc: pointer to the remote processor object
> + *
> + * Calls the start() callback of the remote core
> + *
> + * Return:
> + *
> + * 0 on success. Otherwise, failure
> + */
> +static int arm_rproc_start(struct rproc *rproc)
> +{
> + struct arm_rproc *priv = rproc->priv;
> +
> + return priv->core_dcfg->start(rproc);
> +}
> +
> +/**
> + * arm_rproc_parse_fw() - Parse firmware function for rproc_ops
> + * @rproc: pointer to the remote processor object
> + * @fw: pointer to the firmware
> + *
> + * Does nothing currently.
> + *
> + * Return:
> + *
> + * 0 for success.
> + */
> +static int arm_rproc_parse_fw(struct rproc *rproc, const struct firmware *fw)
> +{
> + return 0;
> +}
> +
> +/**
> + * arm_rproc_load() - Load firmware to memory function for rproc_ops
> + * @rproc: pointer to the remote processor object
> + * @fw: pointer to the firmware
> + *
> + * Does nothing currently.
> + *
> + * Return:
> + *
> + * 0 for success.
> + */
> +static int arm_rproc_load(struct rproc *rproc, const struct firmware *fw)
> +{
What is the point of doing rproc_of_parse_firmware() if the firmware image is
not loaded to memory? Does the remote processor have some kind of default ROM
image to run if it doesn't find anything in memory?
> + return 0;
> +}
> +
> +static const struct rproc_ops arm_rproc_ops = {
> + .start = arm_rproc_start,
> + .stop = arm_rproc_stop,
> + .load = arm_rproc_load,
> + .parse_fw = arm_rproc_parse_fw,
> +};
> +
> +/**
> + * arm_rproc_probe() - the platform device probe
> + * @pdev: the platform device
> + *
> + * Read from the device tree the properties needed to setup
> + * the reset and comms for the remote processor.
> + * Also, allocate a rproc device and register it with the remoteproc subsystem.
> + *
> + * Return:
> + *
> + * 0 on success. Otherwise, failure
> + */
> +static int arm_rproc_probe(struct platform_device *pdev)
> +{
> + const struct arm_rproc_dcfg *core_dcfg;
> + struct device *dev = &pdev->dev;
> + struct device_node *np = dev->of_node;
> + struct arm_rproc *priv;
> + struct rproc *rproc;
> + const char *fw_name;
> + int ret;
> + struct resource *res;
> +
> + core_dcfg = of_device_get_match_data(dev);
> + if (!core_dcfg)
> + return -ENODEV;
> +
> + ret = rproc_of_parse_firmware(dev, 0, &fw_name);
> + if (ret) {
> + dev_err(dev,
> + "can't parse firmware-name from device tree (%pe)\n",
> + ERR_PTR(ret));
> + return ret;
Please replace with dev_err_probe().
> + }
> +
> + dev_dbg(dev, "firmware-name: %s\n", fw_name);
> +
> + rproc = rproc_alloc(dev, np->name, &arm_rproc_ops, fw_name,
> + sizeof(*priv));
Using devm_rproc_alloc() would make this driver even more simple.
> + if (!rproc)
> + return -ENOMEM;
> +
> + priv = rproc->priv;
> + priv->rproc = rproc;
> + priv->core_dcfg = core_dcfg;
> +
> + res = platform_get_resource_byname(pdev,
> + IORESOURCE_MEM, "reset-control");
> + priv->reset_cfg.ctrl_reg = devm_ioremap_resource(&pdev->dev, res);
> + if (IS_ERR(priv->reset_cfg.ctrl_reg)) {
> + ret = PTR_ERR(priv->reset_cfg.ctrl_reg);
> + dev_err(dev,
> + "can't map the reset-control register (%pe)\n",
> + ERR_PTR((unsigned long)priv->reset_cfg.ctrl_reg));
dev_err_probe()
> + goto err_free_rproc;
This isn't needed after moving to devm_rproc_alloc().
> + } else {
> + dev_dbg(dev, "reset-control: %p\n", priv->reset_cfg.ctrl_reg);
> + }
> +
> + res = platform_get_resource_byname(pdev,
> + IORESOURCE_MEM, "reset-status");
> + priv->reset_cfg.state_reg = devm_ioremap_resource(&pdev->dev, res);
> + if (IS_ERR(priv->reset_cfg.state_reg)) {
> + ret = PTR_ERR(priv->reset_cfg.state_reg);
> + dev_err(dev,
> + "can't map the reset-status register (%pe)\n",
> + ERR_PTR((unsigned long)priv->reset_cfg.state_reg));
> + goto err_free_rproc;
Same comments as above.
> + } else {
> + dev_dbg(dev, "reset-status: %p\n",
> + priv->reset_cfg.state_reg);
This isn't adding anything valuable, please remove.
> + }
> +
> + platform_set_drvdata(pdev, rproc);
> +
> + ret = rproc_add(rproc);
devm_rproc_add()
> + if (ret) {
> + dev_err(dev, "can't add remote processor (%pe)\n",
> + ERR_PTR(ret));
> + goto err_free_rproc;
> + } else {
> + dev_dbg(dev, "remote processor added\n");
Please remove.
> + }
> +
> + return 0;
> +
> +err_free_rproc:
> + rproc_free(rproc);
> +
> + return ret;
> +}
> +
> +/**
> + * arm_rproc_remove() - the platform device remove
> + * @pdev: the platform device
> + *
> + * Delete and free the resources used.
> + */
> +static void arm_rproc_remove(struct platform_device *pdev)
> +{
> + struct rproc *rproc = platform_get_drvdata(pdev);
> +
> + rproc_del(rproc);
> + rproc_free(rproc);
> +}
This shouldn't be needed anymore after using devm_rproc_alloc() and
devm_rproc_add().
> +
> +static const struct of_device_id arm_rproc_of_match[] = {
> + { .compatible = "arm,corstone1000-extsys", .data = &arm_rproc_cfg_corstone1000_extsys },
> + {},
> +};
> +MODULE_DEVICE_TABLE(of, arm_rproc_of_match);
> +
> +static struct platform_driver arm_rproc_driver = {
> + .probe = arm_rproc_probe,
> + .remove_new = arm_rproc_remove,
> + .driver = {
> + .name = "arm-rproc",
> + .of_match_table = arm_rproc_of_match,
> + },
> +};
> +module_platform_driver(arm_rproc_driver);
> +
I am echoing Krzysztof view about how generic this driver name is. This has to
be related to a family of processors or be made less generic in some way. Have
a look at what TI did for their K3 lineup [1] - I would like to see the same
thing done here.
Thanks,
Mathieu
[1]. https://elixir.bootlin.com/linux/latest/source/drivers/remoteproc/ti_k3_dsp_remoteproc.c#L898
> +MODULE_LICENSE("GPL");
> +MODULE_DESCRIPTION("Arm Remote Processor Control Driver");
> +MODULE_AUTHOR("Abdellatif El Khlifi <abdellatif.elkhlifi@arm.com>");
> --
> 2.25.1
>
WARNING: multiple messages have this Message-ID (diff)
From: Mathieu Poirier <mathieu.poirier@linaro.org>
To: abdellatif.elkhlifi@arm.com
Cc: Bjorn Andersson <andersson@kernel.org>,
Rob Herring <robh+dt@kernel.org>,
Liviu Dudau <liviu.dudau@arm.com>,
Sudeep Holla <sudeep.holla@arm.com>,
Lorenzo Pieralisi <lpieralisi@kernel.org>,
Krzysztof Kozlowski <krzysztof.kozlowski+dt@linaro.org>,
Conor Dooley <conor+dt@kernel.org>,
Drew.Reed@arm.com, Adam.Johnston@arm.com,
linux-arm-kernel@lists.infradead.org, devicetree@vger.kernel.org,
linux-kernel@vger.kernel.org, linux-remoteproc@vger.kernel.org
Subject: Re: [PATCH 1/3] remoteproc: Add Arm remoteproc driver
Date: Mon, 4 Mar 2024 11:42:49 -0700 [thread overview]
Message-ID: <ZeYWKVpeFm1+4mlT@p14s> (raw)
In-Reply-To: <20240301164227.339208-2-abdellatif.elkhlifi@arm.com>
Good day Abdellatif,
On Fri, Mar 01, 2024 at 04:42:25PM +0000, abdellatif.elkhlifi@arm.com wrote:
> From: Abdellatif El Khlifi <abdellatif.elkhlifi@arm.com>
>
> introduce remoteproc support for Arm remote processors
>
> The supported remote processors are those that come with a reset
> control register and a reset status register. The driver allows to
> switch on or off the remote processor.
>
> The current use case is Corstone-1000 External System (Cortex-M3).
>
> The driver can be extended to support other remote processors
> controlled with a reset control and a reset status registers.
>
> The driver also supports control of multiple remote processors at the
> same time.
>
> Signed-off-by: Abdellatif El Khlifi <abdellatif.elkhlifi@arm.com>
> ---
> MAINTAINERS | 6 +
> drivers/remoteproc/Kconfig | 18 ++
> drivers/remoteproc/Makefile | 1 +
> drivers/remoteproc/arm_rproc.c | 395 +++++++++++++++++++++++++++++++++
> 4 files changed, 420 insertions(+)
> create mode 100644 drivers/remoteproc/arm_rproc.c
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 8d1052fa6a69..54d6a40feea5 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -1764,6 +1764,12 @@ S: Maintained
> F: Documentation/devicetree/bindings/interrupt-controller/arm,vic.yaml
> F: drivers/irqchip/irq-vic.c
>
> +ARM REMOTEPROC DRIVER
> +M: Abdellatif El Khlifi <abdellatif.elkhlifi@arm.com>
> +L: linux-remoteproc@vger.kernel.org
> +S: Maintained
> +F: drivers/remoteproc/arm_rproc.c
> +
Humm... I'm not sure this is needed for now. You'll be CC'ed in future postings
anyway if someone changes this drivers.
> ARM SMC WATCHDOG DRIVER
> M: Julius Werner <jwerner@chromium.org>
> R: Evan Benn <evanbenn@chromium.org>
> diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig
> index 48845dc8fa85..57fbac454a5d 100644
> --- a/drivers/remoteproc/Kconfig
> +++ b/drivers/remoteproc/Kconfig
> @@ -365,6 +365,24 @@ config XLNX_R5_REMOTEPROC
>
> It's safe to say N if not interested in using RPU r5f cores.
>
> +config ARM_REMOTEPROC
> + tristate "Arm remoteproc support"
> + depends on HAS_IOMEM && ARM64
> + default n
> + help
> + Say y here to support Arm remote processors via the remote
> + processor framework.
> +
> + The supported processors are those that come with a reset control register
> + and a reset status register. The design can be extended to support different
> + processors meeting these requirements.
> + The driver also supports control of multiple remote cores at the same time.
> +
> + Supported remote cores:
> + Corstone-1000 External System (Cortex-M3)
> +
Please remove. The descrition in the Kconfig file should be related to a family
of device and the specific model number found in the driver.
> + It's safe to say N here.
> +
> endif # REMOTEPROC
>
> endmenu
> diff --git a/drivers/remoteproc/Makefile b/drivers/remoteproc/Makefile
> index 91314a9b43ce..73126310835b 100644
> --- a/drivers/remoteproc/Makefile
> +++ b/drivers/remoteproc/Makefile
> @@ -39,3 +39,4 @@ obj-$(CONFIG_STM32_RPROC) += stm32_rproc.o
> obj-$(CONFIG_TI_K3_DSP_REMOTEPROC) += ti_k3_dsp_remoteproc.o
> obj-$(CONFIG_TI_K3_R5_REMOTEPROC) += ti_k3_r5_remoteproc.o
> obj-$(CONFIG_XLNX_R5_REMOTEPROC) += xlnx_r5_remoteproc.o
> +obj-$(CONFIG_ARM_REMOTEPROC) += arm_rproc.o
> diff --git a/drivers/remoteproc/arm_rproc.c b/drivers/remoteproc/arm_rproc.c
> new file mode 100644
> index 000000000000..6afa78ae7ad3
> --- /dev/null
> +++ b/drivers/remoteproc/arm_rproc.c
> @@ -0,0 +1,395 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Copyright 2024 Arm Limited and/or its affiliates <open-source-office@arm.com>
> + *
> + * Authors:
> + * Abdellatif El Khlifi <abdellatif.elkhlifi@arm.com>
> + */
> +
> +#include <linux/delay.h>
> +#include <linux/err.h>
> +#include <linux/firmware.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/platform_device.h>
> +#include <linux/remoteproc.h>
> +
> +#include "remoteproc_internal.h"
> +
> +/**
> + * struct arm_rproc_reset_cfg - remote processor reset configuration
> + * @ctrl_reg: address of the control register
> + * @state_reg: address of the reset status register
> + */
> +struct arm_rproc_reset_cfg {
> + void __iomem *ctrl_reg;
> + void __iomem *state_reg;
> +};
> +
> +struct arm_rproc;
> +
This is useless, please remove.
> +/**
> + * struct arm_rproc_dcfg - Arm remote processor configuration
Configuration? This looks more like operations to me. Please consider
renaming.
> + * @stop: stop callback function
> + * @start: start callback function
> + */
> +struct arm_rproc_dcfg {
> + int (*stop)(struct rproc *rproc);
> + int (*start)(struct rproc *rproc);
> +};
> +
> +/**
> + * struct arm_rproc - Arm remote processor instance
> + * @rproc: rproc handler
> + * @core_dcfg: device configuration pointer
> + * @reset_cfg: reset configuration registers
> + */
> +struct arm_rproc {
> + struct rproc *rproc;
> + const struct arm_rproc_dcfg *core_dcfg;
> + struct arm_rproc_reset_cfg reset_cfg;
> +};
> +
> +/* Definitions for Arm Corstone-1000 External System */
> +
> +#define EXTSYS_RST_CTRL_CPUWAIT BIT(0)
> +#define EXTSYS_RST_CTRL_RST_REQ BIT(1)
> +
> +#define EXTSYS_RST_ACK_MASK GENMASK(2, 1)
> +#define EXTSYS_RST_ST_RST_ACK(x) \
> + ((u8)(FIELD_GET(EXTSYS_RST_ACK_MASK, (x))))
> +
> +#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)
> +
> +#define EXTSYS_RST_ACK_POLL_TRIES (3)
> +#define EXTSYS_RST_ACK_POLL_TIMEOUT (1000)
On my side most of the values above came out misaligned.
> +
> +/**
> + * arm_rproc_start_cs1000_extsys() - custom start function
> + * @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 arm_rproc_start_cs1000_extsys(struct rproc *rproc)
> +{
> + struct arm_rproc *priv = rproc->priv;
> + u32 ctrl_reg;
> +
> + /* CPUWAIT signal of the External System is de-asserted */
> + ctrl_reg = readl(priv->reset_cfg.ctrl_reg);
> + ctrl_reg &= ~EXTSYS_RST_CTRL_CPUWAIT;
> + writel(ctrl_reg, priv->reset_cfg.ctrl_reg);
> +
> + return 0;
> +}
> +
> +/**
> + * arm_rproc_cs1000_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 arm_rproc_cs1000_extsys_poll_rst_ack(struct rproc *rproc,
> + u8 exp_ack, u8 *rst_ack)
> +{
> + struct arm_rproc *priv = rproc->priv;
> + struct device *dev = rproc->dev.parent;
> + u32 state_reg;
> + int tries = EXTSYS_RST_ACK_POLL_TRIES;
> + unsigned long timeout;
> +
struct device *dev = rproc->dev.parent;
struct arm_rproc *priv = rproc->priv;
int tries = EXTSYS_RST_ACK_POLL_TRIES;
unsigned long timeout;
u32 state_reg;
> + do {
> + state_reg = readl(priv->reset_cfg.state_reg);
> + *rst_ack = 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;
> + }
> +
> + /* expected ACK value read */
> + if ((*rst_ack & exp_ack) || (*rst_ack == exp_ack))
I'm not sure why the second condition in this if() statement is needed. As far
as I can tell the first condition will trigger and the second one won't be
reached.
> + 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;
> +}
> +
> +/**
> + * arm_rproc_stop_cs1000_extsys() - custom stop function
> + * @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 arm_rproc_stop_cs1000_extsys(struct rproc *rproc)
> +{
> + struct arm_rproc *priv = rproc->priv;
> + struct device *dev = rproc->dev.parent;
> + u32 ctrl_reg;
> + u8 rst_ack, req_status;
> + int ret;
> +
struct device *dev = rproc->dev.parent;
struct arm_rproc *priv = rproc->priv;
u8 rst_ack, req_status;
u32 ctrl_reg;
int ret;
> + ctrl_reg = readl(priv->reset_cfg.ctrl_reg);
> + ctrl_reg |= EXTSYS_RST_CTRL_RST_REQ;
> + writel(ctrl_reg, priv->reset_cfg.ctrl_reg);
> +
> + ret = arm_rproc_cs1000_extsys_poll_rst_ack(rproc,
> + EXTSYS_RST_ACK_COMPLETE |
> + EXTSYS_RST_ACK_NOT_COMPLETE,
> + &rst_ack);
> + if (ret)
> + return ret;
> +
> + req_status = rst_ack;
> +
> + ctrl_reg = readl(priv->reset_cfg.ctrl_reg);
> + ctrl_reg &= ~EXTSYS_RST_CTRL_RST_REQ;
> + writel(ctrl_reg, priv->reset_cfg.ctrl_reg);
> +
> + ret = arm_rproc_cs1000_extsys_poll_rst_ack(rproc, 0, &rst_ack);
> + if (ret)
> + return ret;
> +
> + if (req_status == EXTSYS_RST_ACK_COMPLETE) {
> + dev_dbg(dev, "the requested reset has been accepted\n");
Please remove
> + return 0;
> + }
> +
> + dev_err(dev, "the requested reset has been denied\n");
There is enough error reporting in arm_rproc_cs1000_extsys_poll_rst_ack(),
please remove.
> + return -EACCES;
> +}
> +
> +static const struct arm_rproc_dcfg arm_rproc_cfg_corstone1000_extsys = {
> + .stop = arm_rproc_stop_cs1000_extsys,
> + .start = arm_rproc_start_cs1000_extsys,
> +};
> +
> +/**
> + * arm_rproc_stop() - Stop function for rproc_ops
> + * @rproc: pointer to the remote processor object
> + *
> + * Calls the stop() callback of the remote core
> + *
> + * Return:
> + *
> + * 0 on success. Otherwise, failure
> + */
> +static int arm_rproc_stop(struct rproc *rproc)
> +{
> + struct arm_rproc *priv = rproc->priv;
> +
> + return priv->core_dcfg->stop(rproc);
> +}
> +
> +/**
> + * arm_rproc_start() - Start function for rproc_ops
> + * @rproc: pointer to the remote processor object
> + *
> + * Calls the start() callback of the remote core
> + *
> + * Return:
> + *
> + * 0 on success. Otherwise, failure
> + */
> +static int arm_rproc_start(struct rproc *rproc)
> +{
> + struct arm_rproc *priv = rproc->priv;
> +
> + return priv->core_dcfg->start(rproc);
> +}
> +
> +/**
> + * arm_rproc_parse_fw() - Parse firmware function for rproc_ops
> + * @rproc: pointer to the remote processor object
> + * @fw: pointer to the firmware
> + *
> + * Does nothing currently.
> + *
> + * Return:
> + *
> + * 0 for success.
> + */
> +static int arm_rproc_parse_fw(struct rproc *rproc, const struct firmware *fw)
> +{
> + return 0;
> +}
> +
> +/**
> + * arm_rproc_load() - Load firmware to memory function for rproc_ops
> + * @rproc: pointer to the remote processor object
> + * @fw: pointer to the firmware
> + *
> + * Does nothing currently.
> + *
> + * Return:
> + *
> + * 0 for success.
> + */
> +static int arm_rproc_load(struct rproc *rproc, const struct firmware *fw)
> +{
What is the point of doing rproc_of_parse_firmware() if the firmware image is
not loaded to memory? Does the remote processor have some kind of default ROM
image to run if it doesn't find anything in memory?
> + return 0;
> +}
> +
> +static const struct rproc_ops arm_rproc_ops = {
> + .start = arm_rproc_start,
> + .stop = arm_rproc_stop,
> + .load = arm_rproc_load,
> + .parse_fw = arm_rproc_parse_fw,
> +};
> +
> +/**
> + * arm_rproc_probe() - the platform device probe
> + * @pdev: the platform device
> + *
> + * Read from the device tree the properties needed to setup
> + * the reset and comms for the remote processor.
> + * Also, allocate a rproc device and register it with the remoteproc subsystem.
> + *
> + * Return:
> + *
> + * 0 on success. Otherwise, failure
> + */
> +static int arm_rproc_probe(struct platform_device *pdev)
> +{
> + const struct arm_rproc_dcfg *core_dcfg;
> + struct device *dev = &pdev->dev;
> + struct device_node *np = dev->of_node;
> + struct arm_rproc *priv;
> + struct rproc *rproc;
> + const char *fw_name;
> + int ret;
> + struct resource *res;
> +
> + core_dcfg = of_device_get_match_data(dev);
> + if (!core_dcfg)
> + return -ENODEV;
> +
> + ret = rproc_of_parse_firmware(dev, 0, &fw_name);
> + if (ret) {
> + dev_err(dev,
> + "can't parse firmware-name from device tree (%pe)\n",
> + ERR_PTR(ret));
> + return ret;
Please replace with dev_err_probe().
> + }
> +
> + dev_dbg(dev, "firmware-name: %s\n", fw_name);
> +
> + rproc = rproc_alloc(dev, np->name, &arm_rproc_ops, fw_name,
> + sizeof(*priv));
Using devm_rproc_alloc() would make this driver even more simple.
> + if (!rproc)
> + return -ENOMEM;
> +
> + priv = rproc->priv;
> + priv->rproc = rproc;
> + priv->core_dcfg = core_dcfg;
> +
> + res = platform_get_resource_byname(pdev,
> + IORESOURCE_MEM, "reset-control");
> + priv->reset_cfg.ctrl_reg = devm_ioremap_resource(&pdev->dev, res);
> + if (IS_ERR(priv->reset_cfg.ctrl_reg)) {
> + ret = PTR_ERR(priv->reset_cfg.ctrl_reg);
> + dev_err(dev,
> + "can't map the reset-control register (%pe)\n",
> + ERR_PTR((unsigned long)priv->reset_cfg.ctrl_reg));
dev_err_probe()
> + goto err_free_rproc;
This isn't needed after moving to devm_rproc_alloc().
> + } else {
> + dev_dbg(dev, "reset-control: %p\n", priv->reset_cfg.ctrl_reg);
> + }
> +
> + res = platform_get_resource_byname(pdev,
> + IORESOURCE_MEM, "reset-status");
> + priv->reset_cfg.state_reg = devm_ioremap_resource(&pdev->dev, res);
> + if (IS_ERR(priv->reset_cfg.state_reg)) {
> + ret = PTR_ERR(priv->reset_cfg.state_reg);
> + dev_err(dev,
> + "can't map the reset-status register (%pe)\n",
> + ERR_PTR((unsigned long)priv->reset_cfg.state_reg));
> + goto err_free_rproc;
Same comments as above.
> + } else {
> + dev_dbg(dev, "reset-status: %p\n",
> + priv->reset_cfg.state_reg);
This isn't adding anything valuable, please remove.
> + }
> +
> + platform_set_drvdata(pdev, rproc);
> +
> + ret = rproc_add(rproc);
devm_rproc_add()
> + if (ret) {
> + dev_err(dev, "can't add remote processor (%pe)\n",
> + ERR_PTR(ret));
> + goto err_free_rproc;
> + } else {
> + dev_dbg(dev, "remote processor added\n");
Please remove.
> + }
> +
> + return 0;
> +
> +err_free_rproc:
> + rproc_free(rproc);
> +
> + return ret;
> +}
> +
> +/**
> + * arm_rproc_remove() - the platform device remove
> + * @pdev: the platform device
> + *
> + * Delete and free the resources used.
> + */
> +static void arm_rproc_remove(struct platform_device *pdev)
> +{
> + struct rproc *rproc = platform_get_drvdata(pdev);
> +
> + rproc_del(rproc);
> + rproc_free(rproc);
> +}
This shouldn't be needed anymore after using devm_rproc_alloc() and
devm_rproc_add().
> +
> +static const struct of_device_id arm_rproc_of_match[] = {
> + { .compatible = "arm,corstone1000-extsys", .data = &arm_rproc_cfg_corstone1000_extsys },
> + {},
> +};
> +MODULE_DEVICE_TABLE(of, arm_rproc_of_match);
> +
> +static struct platform_driver arm_rproc_driver = {
> + .probe = arm_rproc_probe,
> + .remove_new = arm_rproc_remove,
> + .driver = {
> + .name = "arm-rproc",
> + .of_match_table = arm_rproc_of_match,
> + },
> +};
> +module_platform_driver(arm_rproc_driver);
> +
I am echoing Krzysztof view about how generic this driver name is. This has to
be related to a family of processors or be made less generic in some way. Have
a look at what TI did for their K3 lineup [1] - I would like to see the same
thing done here.
Thanks,
Mathieu
[1]. https://elixir.bootlin.com/linux/latest/source/drivers/remoteproc/ti_k3_dsp_remoteproc.c#L898
> +MODULE_LICENSE("GPL");
> +MODULE_DESCRIPTION("Arm Remote Processor Control Driver");
> +MODULE_AUTHOR("Abdellatif El Khlifi <abdellatif.elkhlifi@arm.com>");
> --
> 2.25.1
>
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
next prev parent reply other threads:[~2024-03-04 18:42 UTC|newest]
Thread overview: 92+ messages / expand[flat|nested] mbox.gz Atom feed top
2024-03-01 16:42 [PATCH 0/3] remoteproc: introduce Arm remoteproc support abdellatif.elkhlifi
2024-03-01 16:42 ` abdellatif.elkhlifi
2024-03-01 16:42 ` [PATCH 1/3] remoteproc: Add Arm remoteproc driver abdellatif.elkhlifi
2024-03-01 16:42 ` abdellatif.elkhlifi
2024-03-04 18:30 ` Rob Herring
2024-03-04 18:30 ` Rob Herring
2024-03-04 18:42 ` Mathieu Poirier [this message]
2024-03-04 18:42 ` Mathieu Poirier
2024-03-07 19:40 ` Abdellatif El Khlifi
2024-03-07 19:40 ` Abdellatif El Khlifi
2024-03-08 16:44 ` Mathieu Poirier
2024-03-08 16:44 ` Mathieu Poirier
2024-03-11 11:44 ` Abdellatif El Khlifi
2024-03-11 11:44 ` Abdellatif El Khlifi
2024-03-12 16:29 ` Mathieu Poirier
2024-03-12 16:29 ` Mathieu Poirier
2024-03-12 17:32 ` Abdellatif El Khlifi
2024-03-12 17:32 ` Abdellatif El Khlifi
2024-03-13 16:25 ` Mathieu Poirier
2024-03-13 16:25 ` Mathieu Poirier
2024-03-13 17:17 ` Abdellatif El Khlifi
2024-03-13 17:17 ` Abdellatif El Khlifi
2024-03-14 14:52 ` Mathieu Poirier
2024-03-14 14:52 ` Mathieu Poirier
2024-03-14 14:59 ` Sudeep Holla
2024-03-14 14:59 ` Sudeep Holla
2024-03-14 15:16 ` Abdellatif El Khlifi
2024-03-14 15:16 ` Abdellatif El Khlifi
2024-03-14 15:24 ` Sudeep Holla
2024-03-14 15:24 ` Sudeep Holla
2024-03-14 16:29 ` Mathieu Poirier
2024-03-14 16:29 ` Mathieu Poirier
2024-03-25 17:13 ` Abdellatif El Khlifi
2024-03-25 17:13 ` Abdellatif El Khlifi
2024-03-26 14:20 ` Mathieu Poirier
2024-03-26 14:20 ` Mathieu Poirier
2024-03-26 17:14 ` Abdellatif El Khlifi
2024-03-26 17:14 ` Abdellatif El Khlifi
2024-08-22 17:09 ` [PATCH v2 0/5] remoteproc: arm64: Introduce remoteproc support for Corstone-1000 External Systems Abdellatif El Khlifi
2024-08-22 17:09 ` [PATCH v2 1/5] dt-bindings: remoteproc: sse710: Add the External Systems remote processors Abdellatif El Khlifi
2024-08-22 18:25 ` Rob Herring (Arm)
2024-08-23 6:23 ` Krzysztof Kozlowski
2024-09-19 9:35 ` Abdellatif El Khlifi
2024-09-19 10:04 ` Krzysztof Kozlowski
2024-09-19 14:57 ` Abdellatif El Khlifi
2024-09-20 12:42 ` Krzysztof Kozlowski
2024-09-20 14:19 ` Abdellatif El Khlifi
2024-09-20 14:56 ` Krzysztof Kozlowski
2024-09-20 16:38 ` Abdellatif El Khlifi
2024-09-21 18:20 ` Krzysztof Kozlowski
2024-09-23 11:49 ` Abdellatif El Khlifi
2024-09-23 15:29 ` Krzysztof Kozlowski
2024-09-23 17:19 ` Abdellatif El Khlifi
2024-09-27 7:57 ` Krzysztof Kozlowski
2024-09-22 18:58 ` Krzysztof Kozlowski
2024-09-27 17:54 ` Robin Murphy
2024-10-09 9:46 ` Abdellatif El Khlifi
2024-08-22 17:09 ` [PATCH v2 2/5] dt-bindings: arm: sse710: Add Host Base System Control Abdellatif El Khlifi
2024-08-23 6:25 ` Krzysztof Kozlowski
2024-08-22 17:09 ` [PATCH v2 3/5] arm64: dts: corstone1000: Add MHU nodes used by the External System Abdellatif El Khlifi
2024-08-22 17:09 ` [PATCH v2 4/5] arm64: dts: corstone1000: Add External System support Abdellatif El Khlifi
2024-08-22 17:09 ` [PATCH v2 5/5] remoteproc: arm64: corstone1000: Add the External Systems driver Abdellatif El Khlifi
2024-09-18 15:40 ` Abdellatif El Khlifi
2024-09-19 8:37 ` Mathieu Poirier
2024-03-01 16:42 ` [PATCH 2/3] arm64: dts: Add corstone1000 external system device node abdellatif.elkhlifi
2024-03-01 16:42 ` abdellatif.elkhlifi
2024-03-01 19:27 ` Krzysztof Kozlowski
2024-03-01 19:27 ` Krzysztof Kozlowski
2024-03-08 12:21 ` Sudeep Holla
2024-03-08 12:21 ` Sudeep Holla
2024-03-08 14:25 ` Abdellatif El Khlifi
2024-03-08 14:25 ` Abdellatif El Khlifi
2024-03-01 16:42 ` [PATCH 3/3] dt-bindings: remoteproc: Add Arm remoteproc abdellatif.elkhlifi
2024-03-01 16:42 ` abdellatif.elkhlifi
2024-03-01 19:30 ` Krzysztof Kozlowski
2024-03-01 19:30 ` Krzysztof Kozlowski
2024-03-08 12:29 ` Sudeep Holla
2024-03-08 12:29 ` Sudeep Holla
2024-03-08 13:54 ` Abdellatif El Khlifi
2024-03-08 13:54 ` Abdellatif El Khlifi
2024-03-13 19:59 ` Robin Murphy
2024-03-13 19:59 ` Robin Murphy
2024-03-14 13:49 ` Abdellatif El Khlifi
2024-03-14 13:49 ` Abdellatif El Khlifi
2024-03-14 13:56 ` Krzysztof Kozlowski
2024-03-14 13:56 ` Krzysztof Kozlowski
2024-03-14 15:20 ` Abdellatif El Khlifi
2024-03-14 15:20 ` Abdellatif El Khlifi
2024-03-14 15:19 ` Sudeep Holla
2024-03-14 15:19 ` Sudeep Holla
2024-03-15 14:22 ` Abdellatif El Khlifi
2024-03-15 14:22 ` Abdellatif El Khlifi
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=ZeYWKVpeFm1+4mlT@p14s \
--to=mathieu.poirier@linaro.org \
--cc=Adam.Johnston@arm.com \
--cc=Drew.Reed@arm.com \
--cc=abdellatif.elkhlifi@arm.com \
--cc=andersson@kernel.org \
--cc=conor+dt@kernel.org \
--cc=devicetree@vger.kernel.org \
--cc=krzysztof.kozlowski+dt@linaro.org \
--cc=linux-arm-kernel@lists.infradead.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-remoteproc@vger.kernel.org \
--cc=liviu.dudau@arm.com \
--cc=lpieralisi@kernel.org \
--cc=robh+dt@kernel.org \
--cc=sudeep.holla@arm.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.