From: Ben Levinsky <ben.levinsky@amd.com>
To: <jassisinghbrar@gmail.com>, <andersson@kernel.org>,
<mathieu.poirier@linaro.org>, <robh@kernel.org>,
<krzk+dt@kernel.org>, <conor+dt@kernel.org>
Cc: <michal.simek@amd.com>, <linux-remoteproc@vger.kernel.org>,
<devicetree@vger.kernel.org>,
<linux-arm-kernel@lists.infradead.org>,
<linux-kernel@vger.kernel.org>, <ben.levinsky@amd.com>,
<tanmay.shah@amd.com>
Subject: [PATCH 6/6] remoteproc: amd_mbv_bram: Add RPMsg support
Date: Wed, 14 Oct 2026 14:34:26 -0700 [thread overview]
Message-ID: <20261014213426.2957480-7-ben.levinsky@amd.com> (raw)
In-Reply-To: <20261014213426.2957480-1-ben.levinsky@amd.com>
Register named reserved-memory carveouts and expose the firmware
resource table after it has been loaded into BRAM. This lets remoteproc
create virtqueues backed by reserved shared DDR without deriving
carveout roles from DT node names. Map the shared-memory carveouts with
the common rproc_mem_entry_ioremap_wc() and rproc_mem_entry_iounmap()
callbacks.
Use the mailbox framework to send virtqueue notification IDs. Defer
received notifications to process context before dispatching them to
the remoteproc virtqueues. Keep mailbox payload storage valid until
transmit completion and order write-combined vring updates before each
kick.
For designs where the mailbox and remote processor share a clock, request
the mailbox channels only after enabling that clock, while the processor
is still held in reset. Release the channels before disabling the clock
during stop.
The mailboxes are optional. Without them the driver keeps supporting
load, start, and stop only.
Select the mailbox framework, the AMD PL Mailbox controller and the
virtio RPMsg bus, which the driver uses for notifications and RPMsg
channels. Selecting MAILBOX while depending on GPIOLIB creates a
Kconfig recursive dependency, because GPIOLIB is selected by drivers
that are reachable from MAILBOX, so select GPIOLIB as well instead of
depending on it. The driver always needs GPIO for the reset line.
Depend on HAS_IOMEM, which the selected mailbox controller and the
write-combined carveout mappings require.
Assisted-by: LLM sparse
Signed-off-by: Ben Levinsky <ben.levinsky@amd.com>
---
drivers/remoteproc/Kconfig | 6 +-
drivers/remoteproc/amd_mbv_bram_rproc.c | 231 +++++++++++++++++++++++-
2 files changed, 235 insertions(+), 2 deletions(-)
diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig
index 5b56b2dcc725..9ebbbf76289f 100644
--- a/drivers/remoteproc/Kconfig
+++ b/drivers/remoteproc/Kconfig
@@ -25,7 +25,11 @@ config REMOTEPROC_CDEV
config AMD_MBV_BRAM_REMOTEPROC
tristate "AMD MicroBlaze/V BRAM-based remoteproc support"
- depends on OF && COMMON_CLK && (GPIOLIB || COMPILE_TEST)
+ depends on OF && COMMON_CLK && HAS_IOMEM
+ select GPIOLIB
+ select MAILBOX
+ select AMD_PL_MAILBOX
+ select RPMSG_VIRTIO
help
Say y or m here to support a MicroBlaze/V BRAM-based remote
processor managed through the remoteproc framework.
diff --git a/drivers/remoteproc/amd_mbv_bram_rproc.c b/drivers/remoteproc/amd_mbv_bram_rproc.c
index da621f62f7df..65d2c3946325 100644
--- a/drivers/remoteproc/amd_mbv_bram_rproc.c
+++ b/drivers/remoteproc/amd_mbv_bram_rproc.c
@@ -15,15 +15,20 @@
* Reset is controlled via GPIO connected to Processor System Reset IP.
*/
+#include <linux/bitmap.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/dma-mapping.h>
#include <linux/gpio/consumer.h>
+#include <linux/mailbox/zynqmp-ipi-message.h>
+#include <linux/mailbox_client.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_address.h>
+#include <linux/of_reserved_mem.h>
#include <linux/platform_device.h>
#include <linux/remoteproc.h>
+#include <linux/workqueue.h>
#include "remoteproc_internal.h"
@@ -32,13 +37,207 @@
* @dev: device pointer
* @reset: GPIO descriptor for reset control (active-low)
* @clk: processor clock
+ * @rproc: remote processor instance
+ * @tx_client: mailbox client used for virtqueue kicks
+ * @rx_client: mailbox client used for remote notifications
+ * @tx_chan: transmit mailbox channel
+ * @rx_chan: receive mailbox channel
+ * @notify_msgs: persistent mailbox payload storage indexed by notify ID
+ * @pending_notify_ids: notifications awaiting process-context dispatch
+ * @rx_work: work item that dispatches received virtqueue notifications
*/
struct amd_bram_rproc {
struct device *dev;
struct gpio_desc *reset;
struct clk *clk;
+ struct rproc *rproc;
+ struct mbox_client tx_client;
+ struct mbox_client rx_client;
+ struct mbox_chan *tx_chan;
+ struct mbox_chan *rx_chan;
+ struct zynqmp_ipi_message *notify_msgs[256];
+ DECLARE_BITMAP(pending_notify_ids, 256);
+ struct work_struct rx_work;
};
+static void amd_bram_rproc_rx_work(struct work_struct *work)
+{
+ struct amd_bram_rproc *priv =
+ container_of(work, struct amd_bram_rproc, rx_work);
+ unsigned long notifyid;
+
+ for_each_set_bit(notifyid, priv->pending_notify_ids,
+ ARRAY_SIZE(priv->notify_msgs)) {
+ clear_bit(notifyid, priv->pending_notify_ids);
+ if (rproc_vq_interrupt(priv->rproc, notifyid) == IRQ_NONE)
+ dev_dbg(priv->dev, "no virtqueue for notifyid %lu\n",
+ notifyid);
+ }
+}
+
+static void amd_bram_rproc_rx_callback(struct mbox_client *client, void *msg)
+{
+ struct amd_bram_rproc *priv =
+ container_of(client, struct amd_bram_rproc, rx_client);
+ struct zynqmp_ipi_message *mb_msg = msg;
+ size_t offset;
+ u32 notifyid;
+
+ if (!msg) {
+ dev_warn_ratelimited(priv->dev,
+ "received mailbox notification without data\n");
+ return;
+ }
+
+ /* Each notification is one 32-bit notify ID. */
+ if (!mb_msg->len || mb_msg->len % sizeof(u32)) {
+ dev_warn_ratelimited(priv->dev,
+ "malformed mailbox notification: %zu bytes\n",
+ mb_msg->len);
+ return;
+ }
+
+ for (offset = 0; offset < mb_msg->len; offset += sizeof(u32)) {
+ memcpy(¬ifyid, &mb_msg->data[offset], sizeof(notifyid));
+ if (notifyid >= ARRAY_SIZE(priv->notify_msgs)) {
+ dev_warn_ratelimited(priv->dev, "invalid notify id %u\n",
+ notifyid);
+ continue;
+ }
+
+ set_bit(notifyid, priv->pending_notify_ids);
+ }
+
+ schedule_work(&priv->rx_work);
+}
+
+static void amd_bram_rproc_kick(struct rproc *rproc, int vqid)
+{
+ struct amd_bram_rproc *priv = rproc->priv;
+ struct zynqmp_ipi_message *msg;
+ int ret;
+
+ if (!priv->tx_chan) {
+ dev_warn_ratelimited(priv->dev,
+ "cannot kick vq%d: no TX mailbox\n", vqid);
+ return;
+ }
+
+ if (vqid < 0 || vqid >= ARRAY_SIZE(priv->notify_msgs)) {
+ dev_err_ratelimited(priv->dev, "invalid notify id %d\n", vqid);
+ return;
+ }
+
+ /*
+ * The mailbox core queues message pointers. Keep each notification word
+ * in persistent storage until the controller reports TX completion.
+ */
+ msg = priv->notify_msgs[vqid];
+ msg->len = sizeof(u32);
+ memcpy(msg->data, &vqid, sizeof(u32));
+ /* Order writes to WC vring mappings before notifying the remote. */
+ wmb();
+ ret = mbox_send_message(priv->tx_chan, msg);
+ if (ret < 0)
+ dev_err_ratelimited(priv->dev, "failed to kick vq%d: %d\n",
+ vqid, ret);
+}
+
+static void amd_bram_rproc_free_mboxes(struct amd_bram_rproc *priv)
+{
+ if (priv->rx_chan) {
+ mbox_free_channel(priv->rx_chan);
+ priv->rx_chan = NULL;
+ }
+ if (priv->tx_chan) {
+ mbox_free_channel(priv->tx_chan);
+ priv->tx_chan = NULL;
+ }
+}
+
+static int amd_bram_rproc_request_mboxes(struct amd_bram_rproc *priv)
+{
+ int ret;
+
+ /* Without mailboxes the driver supports load, start, and stop only. */
+ if (!of_property_present(priv->dev->of_node, "mboxes"))
+ return 0;
+
+ priv->tx_chan = mbox_request_channel_byname(&priv->tx_client, "tx");
+ if (IS_ERR(priv->tx_chan)) {
+ ret = PTR_ERR(priv->tx_chan);
+ priv->tx_chan = NULL;
+ dev_err(priv->dev, "failed to request tx mailbox: %d\n", ret);
+ return ret;
+ }
+
+ priv->rx_chan = mbox_request_channel_byname(&priv->rx_client, "rx");
+ if (IS_ERR(priv->rx_chan)) {
+ ret = PTR_ERR(priv->rx_chan);
+ priv->rx_chan = NULL;
+ mbox_free_channel(priv->tx_chan);
+ priv->tx_chan = NULL;
+ dev_err(priv->dev, "failed to request rx mailbox: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int amd_bram_rproc_add_reserved_mem(struct rproc *rproc)
+{
+ struct amd_bram_rproc *priv = rproc->priv;
+ struct device_node *np = priv->dev->of_node;
+ struct rproc_mem_entry *mem;
+ int count;
+ int ret;
+ int i;
+
+ if (!of_property_present(np, "memory-region"))
+ return 0;
+
+ count = of_reserved_mem_region_count(np);
+ if (count < 0)
+ return count;
+
+ for (i = 0; i < count; i++) {
+ struct resource res;
+ const char *name;
+
+ ret = of_property_read_string_index(np, "memory-region-names",
+ i, &name);
+ if (ret)
+ return dev_err_probe(priv->dev, ret,
+ "failed to read memory-region name %d\n",
+ i);
+
+ ret = of_reserved_mem_region_to_resource(np, i, &res);
+ if (ret)
+ return dev_err_probe(priv->dev, ret,
+ "failed to parse memory-region %d\n", i);
+
+ if (!strcmp(name, "vdev0buffer"))
+ mem = rproc_of_resm_mem_entry_init(priv->dev, i,
+ resource_size(&res),
+ res.start, name);
+ else
+ mem = rproc_mem_entry_init(priv->dev, NULL,
+ (dma_addr_t)res.start,
+ resource_size(&res), res.start,
+ rproc_mem_entry_ioremap_wc,
+ rproc_mem_entry_iounmap,
+ "%s", name);
+ if (!mem)
+ return -ENOMEM;
+
+ rproc_add_carveout(rproc, mem);
+ rproc_coredump_add_segment(rproc, res.start, resource_size(&res));
+ dev_dbg(priv->dev, "reserved memory carveout %pR\n", &res);
+ }
+
+ return 0;
+}
+
static int amd_bram_rproc_prepare(struct rproc *rproc)
{
struct amd_bram_rproc *priv = rproc->priv;
@@ -80,7 +279,7 @@ static int amd_bram_rproc_prepare(struct rproc *rproc)
rproc_add_carveout(rproc, mem);
rproc_coredump_add_segment(rproc, da, resource_size(&res));
- return 0;
+ return amd_bram_rproc_add_reserved_mem(rproc);
}
static int amd_bram_rproc_start(struct rproc *rproc)
@@ -111,10 +310,17 @@ static int amd_bram_rproc_start(struct rproc *rproc)
/* Hold reset asserted long enough to provide a clean reset pulse. */
usleep_range(10, 20);
+ ret = amd_bram_rproc_request_mboxes(priv);
+ if (ret) {
+ clk_disable_unprepare(priv->clk);
+ return ret;
+ }
+
/* Deassert reset and let the processor run. */
ret = gpiod_direction_output(priv->reset, 0);
if (ret) {
dev_err(priv->dev, "failed to deassert reset: %d\n", ret);
+ amd_bram_rproc_free_mboxes(priv);
clk_disable_unprepare(priv->clk);
return ret;
}
@@ -133,6 +339,10 @@ static int amd_bram_rproc_stop(struct rproc *rproc)
dev_err(priv->dev, "failed to assert reset: %d\n", ret);
return ret;
}
+ cancel_work_sync(&priv->rx_work);
+ bitmap_zero(priv->pending_notify_ids,
+ ARRAY_SIZE(priv->notify_msgs));
+ amd_bram_rproc_free_mboxes(priv);
/* Disable clock after asserting reset */
clk_disable_unprepare(priv->clk);
@@ -156,6 +366,8 @@ static const struct rproc_ops amd_bram_rproc_ops = {
.sanity_check = rproc_elf_sanity_check,
.get_boot_addr = rproc_elf_get_boot_addr,
.parse_fw = amd_bram_rproc_parse_fw,
+ .find_loaded_rsc_table = rproc_elf_find_loaded_rsc_table,
+ .kick = amd_bram_rproc_kick,
};
static int amd_bram_rproc_probe(struct platform_device *pdev)
@@ -165,6 +377,7 @@ static int amd_bram_rproc_probe(struct platform_device *pdev)
const char *fw_name = NULL;
struct rproc *rproc;
int ret;
+ int i;
ret = rproc_of_parse_firmware(dev, 0, &fw_name);
if (ret < 0 && ret != -EINVAL)
@@ -178,6 +391,8 @@ static int amd_bram_rproc_probe(struct platform_device *pdev)
priv = rproc->priv;
priv->dev = dev;
+ priv->rproc = rproc;
+ INIT_WORK(&priv->rx_work, amd_bram_rproc_rx_work);
/* Get the processor clock */
priv->clk = devm_clk_get(dev, NULL);
@@ -195,6 +410,20 @@ static int amd_bram_rproc_probe(struct platform_device *pdev)
return dev_err_probe(dev, PTR_ERR(priv->reset),
"failed to get reset gpio\n");
+ priv->tx_client.dev = dev;
+ priv->tx_client.tx_block = false;
+ priv->tx_client.knows_txdone = false;
+ priv->rx_client.dev = dev;
+ priv->rx_client.rx_callback = amd_bram_rproc_rx_callback;
+ for (i = 0; i < ARRAY_SIZE(priv->notify_msgs); i++) {
+ priv->notify_msgs[i] =
+ devm_kzalloc(dev, struct_size(priv->notify_msgs[i],
+ data, sizeof(u32)),
+ GFP_KERNEL);
+ if (!priv->notify_msgs[i])
+ return -ENOMEM;
+ }
+
rproc->auto_boot = false;
ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64));
--
2.34.1
next prev parent reply other threads:[~2026-10-14 21:34 UTC|newest]
Thread overview: 11+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-14 21:34 [PATCH 0/6] Add AMD PL Mailbox and RPMsg support for the AMD BRAM remoteproc Ben Levinsky
2026-10-14 21:34 ` [PATCH 1/6] remoteproc: amd_mbv_bram: Bound device name formatting Ben Levinsky
2026-10-14 21:48 ` sashiko-bot
2026-10-14 21:34 ` [PATCH 2/6] remoteproc: amd_mbv_bram: Restore reset GPIO direction after PL reconfig Ben Levinsky
2026-10-14 21:44 ` sashiko-bot
2026-10-14 21:34 ` [PATCH 3/6] dt-bindings: mailbox: Add AMD PL Mailbox IP Ben Levinsky
2026-10-14 21:34 ` [PATCH 4/6] mailbox: Add AMD PL Mailbox soft IP controller Ben Levinsky
2026-10-14 21:41 ` sashiko-bot
2026-10-14 21:34 ` [PATCH 5/6] dt-bindings: remoteproc: Add RPMsg resources to AMD BRAM rproc Ben Levinsky
2026-10-14 21:34 ` Ben Levinsky [this message]
2026-10-14 21:45 ` [PATCH 6/6] remoteproc: amd_mbv_bram: Add RPMsg support sashiko-bot
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=20261014213426.2957480-7-ben.levinsky@amd.com \
--to=ben.levinsky@amd.com \
--cc=andersson@kernel.org \
--cc=conor+dt@kernel.org \
--cc=devicetree@vger.kernel.org \
--cc=jassisinghbrar@gmail.com \
--cc=krzk+dt@kernel.org \
--cc=linux-arm-kernel@lists.infradead.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-remoteproc@vger.kernel.org \
--cc=mathieu.poirier@linaro.org \
--cc=michal.simek@amd.com \
--cc=robh@kernel.org \
--cc=tanmay.shah@amd.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.