[PATCH 2/3] soc: renesas: Add Renesas R-Car MFIS driver

Wolfram Sang posted 3 patches 2 weeks, 6 days ago
There is a newer version of this series
[PATCH 2/3] soc: renesas: Add Renesas R-Car MFIS driver
Posted by Wolfram Sang 2 weeks, 6 days ago
Renesas R-Car MFIS offers multiple features but most importantly
mailboxes and hwspinlocks. Because they share a common register space
and a common register unprotection mechanism, a single driver was chosen
to handle all dependencies. (MFD and auxiliary bus have been tried as
well, but they failed because of circular dependencies.)

In this first step, the driver implements common register access and a
mailbox controller. hwspinlock support will be added incrementally, once
the subsystem allows out-of-directory drivers.

Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
Signed-off-by: Wolfram Sang <wsa+renesas@sang-engineering.com>
---

This driver is already prepared for upcoming r8a78001 support which will
break away from the IICR/EICR register pair. Actual support will be
added later once HW is available.

 drivers/soc/renesas/Kconfig     |   9 +
 drivers/soc/renesas/Makefile    |   1 +
 drivers/soc/renesas/rcar-mfis.c | 325 ++++++++++++++++++++++++++++++++
 3 files changed, 335 insertions(+)
 create mode 100644 drivers/soc/renesas/rcar-mfis.c

diff --git a/drivers/soc/renesas/Kconfig b/drivers/soc/renesas/Kconfig
index 26bed0fdceb0..6c472c951ab3 100644
--- a/drivers/soc/renesas/Kconfig
+++ b/drivers/soc/renesas/Kconfig
@@ -465,6 +465,15 @@ config ARCH_R9A07G043
 
 endif # RISCV
 
+config RCAR_MFIS
+	tristate "Renesas R-Car MFIS driver"
+	depends on ARCH_RENESAS || COMPILE_TEST
+	depends on MAILBOX
+	help
+	  Select this option to enable the Renesas R-Car MFIS core driver for
+	  the MFIS device found on SoCs like R-Car. On families like Gen5, this
+	  is needed to communicate with the SCP.
+
 config PWC_RZV2M
 	bool "Renesas RZ/V2M PWC support" if COMPILE_TEST
 
diff --git a/drivers/soc/renesas/Makefile b/drivers/soc/renesas/Makefile
index 655dbcb08747..21eb78a05fc0 100644
--- a/drivers/soc/renesas/Makefile
+++ b/drivers/soc/renesas/Makefile
@@ -13,6 +13,7 @@ obj-$(CONFIG_SYS_R9A09G056)	+= r9a09g056-sys.o
 obj-$(CONFIG_SYS_R9A09G057)	+= r9a09g057-sys.o
 
 # Family
+obj-$(CONFIG_RCAR_MFIS)		+= rcar-mfis.o
 obj-$(CONFIG_PWC_RZV2M)		+= pwc-rzv2m.o
 obj-$(CONFIG_RST_RCAR)		+= rcar-rst.o
 obj-$(CONFIG_RZN1_IRQMUX)	+= rzn1_irqmux.o
diff --git a/drivers/soc/renesas/rcar-mfis.c b/drivers/soc/renesas/rcar-mfis.c
new file mode 100644
index 000000000000..5fb9cd352b76
--- /dev/null
+++ b/drivers/soc/renesas/rcar-mfis.c
@@ -0,0 +1,325 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Renesas R-Car MFIS (Multifunctional Interface) driver
+ *
+ * Copyright (C) Renesas Solutions Corp.
+ * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+ * Wolfram Sang <wsa+renesas@sang-engineering.com>
+ */
+#include <dt-bindings/mailbox/renesas,r8a78000-mfis.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/mailbox_controller.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+
+#define MFISWPCNTR	0x0900
+#define MFISWACNTR	0x0904
+
+#define MFIS_UNPROTECT_KEY 0xACCE0000
+
+struct mfis_priv;
+
+struct mfis_reg {
+	void __iomem *base;
+	resource_size_t start;
+	struct mfis_priv *priv;
+};
+
+struct mfis_info {
+	u32 unprotect_mask;
+	unsigned int mb_num_channels;
+	unsigned int mb_reg_comes_from_dt:1;
+	unsigned int mb_tx_uses_eicr:1;
+	unsigned int mb_channels_are_unidir:1;
+};
+
+struct mfis_per_chan_priv {
+	u32 reg;
+	int irq;
+};
+
+struct mfis_priv {
+	struct device *dev;
+	struct mfis_reg common_reg;
+	struct mfis_reg mbox_reg;
+	const struct mfis_info *info;
+
+	/* mailbox private data */
+	struct mbox_controller mbox;
+	struct mfis_per_chan_priv *per_chan_privs;
+};
+
+static u32 mfis_read(struct mfis_reg *mreg, unsigned int reg)
+{
+	return ioread32(mreg->base + reg);
+}
+
+static void mfis_write(struct mfis_reg *mreg, u32 reg, u32 val)
+{
+	struct mfis_priv *priv = mreg->priv;
+	u32 unprotect_mask = priv->info->unprotect_mask;
+	u32 unprotect_code;
+
+	/*
+	 * [Gen4] key: 0xACCE0000, mask: 0x0000FFFF
+	 * [Gen5] key: 0xACC00000, mask: 0x000FFFFF
+	 */
+	unprotect_code = (MFIS_UNPROTECT_KEY & ~unprotect_mask) |
+			 ((mreg->start | reg) & unprotect_mask);
+
+	iowrite32(unprotect_code, priv->common_reg.base + MFISWACNTR);
+	iowrite32(val, mreg->base + reg);
+}
+
+/****************************************************
+ *			Mailbox
+ ****************************************************/
+
+#define mfis_mb_mbox_to_priv(_m) container_of((_m), struct mfis_priv, mbox)
+
+static irqreturn_t mfis_mb_iicr_interrupt(int irq, void *data)
+{
+	struct mbox_chan *chan = data;
+	struct mfis_priv *priv = mfis_mb_mbox_to_priv(chan->mbox);
+	struct mfis_per_chan_priv *per_chan_priv = chan->con_priv;
+
+	mbox_chan_received_data(chan, NULL);
+	/* Stop remote(!) doorbell */
+	mfis_write(&priv->mbox_reg, per_chan_priv->reg, 0);
+
+	return IRQ_HANDLED;
+}
+
+static int mfis_mb_startup(struct mbox_chan *chan)
+{
+	struct mfis_per_chan_priv *per_chan_priv = chan->con_priv;
+	int ret = 0;
+
+	if (per_chan_priv->irq)
+		ret = request_irq(per_chan_priv->irq, mfis_mb_iicr_interrupt, 0,
+				  dev_name(chan->mbox->dev), chan);
+
+	return ret;
+}
+
+static void mfis_mb_shutdown(struct mbox_chan *chan)
+{
+	struct mfis_per_chan_priv *per_chan_priv = chan->con_priv;
+
+	free_irq(per_chan_priv->irq, chan);
+}
+
+static int mfis_mb_iicr_send_data(struct mbox_chan *chan, void *data)
+{
+	struct mfis_priv *priv = mfis_mb_mbox_to_priv(chan->mbox);
+	struct mfis_per_chan_priv *per_chan_priv = chan->con_priv;
+
+	/* Our doorbell still active? */
+	if (mfis_read(&priv->mbox_reg, per_chan_priv->reg) & 1)
+		return -EBUSY;
+
+	/* Start our doorbell */
+	mfis_write(&priv->mbox_reg, per_chan_priv->reg, 1);
+
+	return 0;
+}
+
+static bool mfis_mb_iicr_last_tx_done(struct mbox_chan *chan)
+{
+	struct mfis_priv *priv = mfis_mb_mbox_to_priv(chan->mbox);
+	struct mfis_per_chan_priv *per_chan_priv = chan->con_priv;
+
+	/* Our doorbell still active? */
+	return !(mfis_read(&priv->mbox_reg, per_chan_priv->reg) & 1);
+}
+
+/* For MFIS variants using the IICR/EICR register pair */
+static const struct mbox_chan_ops mfis_iicr_ops = {
+	.startup = mfis_mb_startup,
+	.shutdown = mfis_mb_shutdown,
+	.send_data = mfis_mb_iicr_send_data,
+	.last_tx_done = mfis_mb_iicr_last_tx_done,
+};
+
+static struct mbox_chan *mfis_mb_of_xlate(struct mbox_controller *mbox,
+					  const struct of_phandle_args *sp)
+{
+	struct mfis_priv *priv = mfis_mb_mbox_to_priv(mbox);
+	struct mfis_per_chan_priv *per_chan_priv;
+	struct mbox_chan *chan;
+	u32 chan_num, chan_flags;
+	bool tx_uses_eicr, is_only_rx;
+
+	if (sp->args_count != 2)
+		return ERR_PTR(-EINVAL);
+
+	chan_num = sp->args[0];
+	chan_flags = sp->args[1];
+
+	/* Channel layout is described in mfis_mb_probe() */
+	if (priv->info->mb_channels_are_unidir) {
+		is_only_rx = chan_flags & MFIS_CHANNEL_RX;
+		chan = mbox->chans + 2 * chan_num + is_only_rx;
+	} else {
+		is_only_rx = false;
+		chan = mbox->chans + chan_num;
+	}
+
+	if (priv->info->mb_reg_comes_from_dt) {
+		tx_uses_eicr = chan_flags & MFIS_CHANNEL_EICR;
+		if (tx_uses_eicr)
+			chan += mbox->num_chans / 2;
+	} else {
+		tx_uses_eicr = priv->info->mb_tx_uses_eicr;
+	}
+
+	per_chan_priv = chan->con_priv;
+	per_chan_priv->reg = chan_num * 0x1000 + (tx_uses_eicr ^ is_only_rx) * 4;
+
+	if (!priv->info->mb_channels_are_unidir || is_only_rx) {
+		char irqname[8];
+		char suffix = tx_uses_eicr ? 'i' : 'e';
+
+		/* "ch0i" or "ch0e" */
+		scnprintf(irqname, sizeof(irqname), "ch%d%c", chan_num, suffix);
+
+		per_chan_priv->irq = of_irq_get_byname(mbox->dev->of_node, irqname);
+		if (per_chan_priv->irq < 0)
+			return ERR_PTR(per_chan_priv->irq);
+		else if (per_chan_priv->irq == 0)
+			return ERR_PTR(-ENOENT);
+	}
+
+	return chan;
+}
+
+static int mfis_mb_probe(struct mfis_priv *priv)
+{
+	struct device *dev = priv->dev;
+	struct mbox_chan *chan;
+	struct mbox_controller *mbox;
+	unsigned int i, num_chan = priv->info->mb_num_channels;
+
+	if (priv->info->mb_channels_are_unidir)
+		/* Channel layout: Ch0-TX, Ch0-RX, Ch1-TX... */
+		num_chan *= 2;
+
+	if (priv->info->mb_reg_comes_from_dt)
+		/* Channel layout: <n> IICR channels, <n> EICR channels */
+		num_chan *= 2;
+
+	chan  = devm_kcalloc(dev, num_chan, sizeof(*chan), GFP_KERNEL);
+	if (!chan)
+		return -ENOMEM;
+
+	priv->per_chan_privs = devm_kcalloc(dev, num_chan, sizeof(*priv->per_chan_privs),
+					    GFP_KERNEL);
+	if (!priv->per_chan_privs)
+		return -ENOMEM;
+
+	mbox = &priv->mbox;
+
+	for (i = 0; i < num_chan; i++)
+		chan[i].con_priv = &priv->per_chan_privs[i];
+
+	mbox->chans = chan;
+	mbox->num_chans = num_chan;
+	mbox->txdone_poll = true;
+	mbox->ops = &mfis_iicr_ops;
+	mbox->dev = dev;
+	mbox->of_xlate = mfis_mb_of_xlate;
+
+	return mbox_controller_register(mbox);
+}
+
+/****************************************************
+ *		Common
+ ****************************************************/
+static int mfis_reg_probe(struct platform_device *pdev, struct mfis_priv *priv,
+			  struct mfis_reg *mreg, const char *name, bool required)
+{
+	struct resource *res;
+	void __iomem *base;
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, name);
+
+	/* If there is no mailbox resource, registers are in the common space */
+	if (!res && !required) {
+		priv->mbox_reg = priv->common_reg;
+	} else {
+		base = devm_ioremap_resource(&pdev->dev, res);
+		if (IS_ERR(base))
+			return PTR_ERR(base);
+
+		mreg->base = base;
+		mreg->start = res->start;
+		mreg->priv = priv;
+	}
+
+	return 0;
+}
+
+static int mfis_probe(struct platform_device *pdev)
+{
+	struct mfis_priv *priv;
+	struct device *dev = &pdev->dev;
+	int ret;
+
+	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	priv->dev = dev;
+	priv->info = of_device_get_match_data(dev);
+
+	ret = mfis_reg_probe(pdev, priv, &priv->common_reg, "common", true);
+	if (ret)
+		return ret;
+
+	ret = mfis_reg_probe(pdev, priv, &priv->mbox_reg, "mboxes", false);
+	if (ret)
+		return ret;
+
+	return mfis_mb_probe(priv);
+}
+
+static const struct mfis_info mfis_info_r8a78000 = {
+	.unprotect_mask	= 0x000fffff,
+	.mb_num_channels = 64,
+	.mb_reg_comes_from_dt = true,
+	.mb_channels_are_unidir = true,
+};
+
+static const struct mfis_info mfis_info_r8a78000_scp = {
+	.unprotect_mask	= 0x000fffff,
+	.mb_num_channels = 32,
+	.mb_tx_uses_eicr = true,
+	.mb_channels_are_unidir = true,
+};
+
+static const struct of_device_id mfis_mfd_of_match[] = {
+	{ .compatible = "renesas,r8a78000-mfis", .data = &mfis_info_r8a78000, },
+	{ .compatible = "renesas,r8a78000-mfis-scp", .data = &mfis_info_r8a78000_scp, },
+	{}
+};
+MODULE_DEVICE_TABLE(of, mfis_mfd_of_match);
+
+static struct platform_driver mfis_driver = {
+	.driver = {
+		.name = "rcar-mfis",
+		.of_match_table = mfis_mfd_of_match,
+	},
+	.probe	= mfis_probe,
+};
+module_platform_driver(mfis_driver);
+
+MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>");
+MODULE_AUTHOR("Wolfram Sang <wsa+renesas@sang-engineering.com>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Renesas R-Car MFIS driver");
-- 
2.51.0
Re: [PATCH 2/3] soc: renesas: Add Renesas R-Car MFIS driver
Posted by Jassi Brar 1 week, 1 day ago
On Tue, Mar 17, 2026 at 8:06 AM Wolfram Sang
<wsa+renesas@sang-engineering.com> wrote:
>
> Renesas R-Car MFIS offers multiple features but most importantly
> mailboxes and hwspinlocks. Because they share a common register space
> and a common register unprotection mechanism, a single driver was chosen
> to handle all dependencies. (MFD and auxiliary bus have been tried as
> well, but they failed because of circular dependencies.)
>
> In this first step, the driver implements common register access and a
> mailbox controller. hwspinlock support will be added incrementally, once
> the subsystem allows out-of-directory drivers.
>
> Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
> Signed-off-by: Wolfram Sang <wsa+renesas@sang-engineering.com>
> ---
>
The mailbox part seems reasonable to me, apart from the nits already
pointed out.
Acked-by: Jassi Brar <jassisinghbrar@gmail.com>
Re: [PATCH 2/3] soc: renesas: Add Renesas R-Car MFIS driver
Posted by Wolfram Sang 6 days, 14 hours ago
> The mailbox part seems reasonable to me, apart from the nits already
> pointed out.
> Acked-by: Jassi Brar <jassisinghbrar@gmail.com>

Great, thank you!

Re: [PATCH 2/3] soc: renesas: Add Renesas R-Car MFIS driver
Posted by Geert Uytterhoeven 2 weeks, 4 days ago
Hi Wolfram,

On Tue, 17 Mar 2026 at 14:06, Wolfram Sang
<wsa+renesas@sang-engineering.com> wrote:
> Renesas R-Car MFIS offers multiple features but most importantly
> mailboxes and hwspinlocks. Because they share a common register space
> and a common register unprotection mechanism, a single driver was chosen
> to handle all dependencies. (MFD and auxiliary bus have been tried as
> well, but they failed because of circular dependencies.)
>
> In this first step, the driver implements common register access and a
> mailbox controller. hwspinlock support will be added incrementally, once
> the subsystem allows out-of-directory drivers.
>
> Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
> Signed-off-by: Wolfram Sang <wsa+renesas@sang-engineering.com>

Thanks for your patch!

> --- /dev/null
> +++ b/drivers/soc/renesas/rcar-mfis.c

> +struct mfis_info {
> +       u32 unprotect_mask;
> +       unsigned int mb_num_channels;
> +       unsigned int mb_reg_comes_from_dt:1;
> +       unsigned int mb_tx_uses_eicr:1;
> +       unsigned int mb_channels_are_unidir:1;
> +};
> +
> +struct mfis_per_chan_priv {

I would drop the "per_".

> +       u32 reg;
> +       int irq;
> +};
> +
> +struct mfis_priv {
> +       struct device *dev;
> +       struct mfis_reg common_reg;
> +       struct mfis_reg mbox_reg;
> +       const struct mfis_info *info;
> +
> +       /* mailbox private data */
> +       struct mbox_controller mbox;
> +       struct mfis_per_chan_priv *per_chan_privs;

Likewise.
This could be a flexible array, if it weren't for the hwspinlock array
you will have to add later, right?

> +};
> +
> +static u32 mfis_read(struct mfis_reg *mreg, unsigned int reg)
> +{
> +       return ioread32(mreg->base + reg);
> +}
> +
> +static void mfis_write(struct mfis_reg *mreg, u32 reg, u32 val)

Both writel() and iowrite32() use the inverse order of "reg" and "val".
But I can understand you want to keep "mreg" and "reg" together.

> +{
> +       struct mfis_priv *priv = mreg->priv;
> +       u32 unprotect_mask = priv->info->unprotect_mask;
> +       u32 unprotect_code;
> +
> +       /*
> +        * [Gen4] key: 0xACCE0000, mask: 0x0000FFFF
> +        * [Gen5] key: 0xACC00000, mask: 0x000FFFFF
> +        */
> +       unprotect_code = (MFIS_UNPROTECT_KEY & ~unprotect_mask) |
> +                        ((mreg->start | reg) & unprotect_mask);
> +
> +       iowrite32(unprotect_code, priv->common_reg.base + MFISWACNTR);
> +       iowrite32(val, mreg->base + reg);
> +}
> +
> +/****************************************************
> + *                     Mailbox

Missing closing asterisk ;-)

> + ****************************************************/
> +
> +#define mfis_mb_mbox_to_priv(_m) container_of((_m), struct mfis_priv, mbox)

Both "mb" and "mbox", so perhaps mfis_mbox_to_priv()?
And perhaps s/mb_/mbox_/ everywhere?

> +static int mfis_mb_startup(struct mbox_chan *chan)
> +{
> +       struct mfis_per_chan_priv *per_chan_priv = chan->con_priv;
> +       int ret = 0;
> +
> +       if (per_chan_priv->irq)
> +               ret = request_irq(per_chan_priv->irq, mfis_mb_iicr_interrupt, 0,
> +                                 dev_name(chan->mbox->dev), chan);
> +
> +       return ret;

You can reduce indentation, and get rid of ret, by inverting the
conditional.

> +}
> +
> +static void mfis_mb_shutdown(struct mbox_chan *chan)
> +{
> +       struct mfis_per_chan_priv *per_chan_priv = chan->con_priv;
> +
> +       free_irq(per_chan_priv->irq, chan);

if (per_chan_priv->irq) ...

free_irq() seems to support zero, but irq_to_desc() still has to
traverse the mtree.

> +}

> +static struct mbox_chan *mfis_mb_of_xlate(struct mbox_controller *mbox,
> +                                         const struct of_phandle_args *sp)
> +{
> +       struct mfis_priv *priv = mfis_mb_mbox_to_priv(mbox);
> +       struct mfis_per_chan_priv *per_chan_priv;
> +       struct mbox_chan *chan;
> +       u32 chan_num, chan_flags;
> +       bool tx_uses_eicr, is_only_rx;
> +
> +       if (sp->args_count != 2)
> +               return ERR_PTR(-EINVAL);
> +
> +       chan_num = sp->args[0];

"chan_num" is the hardware channel number, and should be validated
against mpriv->info->mb_num_channels.

> +       chan_flags = sp->args[1];
> +
> +       /* Channel layout is described in mfis_mb_probe() */

Given you already use "chan += ..." below, perhaps:

    chan = mbox->chans + chan_num;

> +       if (priv->info->mb_channels_are_unidir) {
> +               is_only_rx = chan_flags & MFIS_CHANNEL_RX;
> +               chan = mbox->chans + 2 * chan_num + is_only_rx;

...and:

    chan += chan_num + is_only_rx;

> +       } else {
> +               is_only_rx = false;
> +               chan = mbox->chans + chan_num;

... and drop this line?
With a proper preinitalization of is_only_rx, the whole "else" branch
can be dropped.

> +       }
> +
> +       if (priv->info->mb_reg_comes_from_dt) {
> +               tx_uses_eicr = chan_flags & MFIS_CHANNEL_EICR;
> +               if (tx_uses_eicr)
> +                       chan += mbox->num_chans / 2;
> +       } else {
> +               tx_uses_eicr = priv->info->mb_tx_uses_eicr;
> +       }

"chan - mbox->chans" is the logical channel number, and should be
validated against mbox_num_chans, to avoid out-of-bound accesses.

> +
> +       per_chan_priv = chan->con_priv;
> +       per_chan_priv->reg = chan_num * 0x1000 + (tx_uses_eicr ^ is_only_rx) * 4;

I think it would be good to document this register is either an IICR
or EICR register offset, through:
  1. A comment, or
  2. Definitions and code, e.g

         #define MFISIICR 0x00
         #define MFISEICR 0x04

         if (tx_uses_eicr ^ is_only_rx)
             per_chan_priv->reg = chan_num * 0x1000 + MFISEICR;
         else
             per_chan_priv->reg = chan_num * 0x1000 + MFISIICR;

     Or:

         #define MFISIICR(i) ((i) * 0x1000 + 0x00)
         #define MFISEICR(i) ((i) * 0x1000 + 0x04)

         per_chan_priv->reg = (tx_uses_eicr ^ is_only_rx) ? MFISEICR(chan_num)
                                                          : MFISIICR(chan_num);

> +
> +       if (!priv->info->mb_channels_are_unidir || is_only_rx) {
> +               char irqname[8];
> +               char suffix = tx_uses_eicr ? 'i' : 'e';
> +
> +               /* "ch0i" or "ch0e" */
> +               scnprintf(irqname, sizeof(irqname), "ch%d%c", chan_num, suffix);

"%u" for unsigned chan_num.

> +
> +               per_chan_priv->irq = of_irq_get_byname(mbox->dev->of_node, irqname);
> +               if (per_chan_priv->irq < 0)
> +                       return ERR_PTR(per_chan_priv->irq);
> +               else if (per_chan_priv->irq == 0)

No need for "else" after "return".

> +                       return ERR_PTR(-ENOENT);
> +       }
> +
> +       return chan;
> +}
> +
> +static int mfis_mb_probe(struct mfis_priv *priv)
> +{
> +       struct device *dev = priv->dev;
> +       struct mbox_chan *chan;
> +       struct mbox_controller *mbox;
> +       unsigned int i, num_chan = priv->info->mb_num_channels;

"i" is only used in the for-loop below, so you could declare it in the
for-statement. As a bonus, that would avoid mixing the declaration of
uninitialized and initialized variables.

> +
> +       if (priv->info->mb_channels_are_unidir)
> +               /* Channel layout: Ch0-TX, Ch0-RX, Ch1-TX... */
> +               num_chan *= 2;
> +
> +       if (priv->info->mb_reg_comes_from_dt)
> +               /* Channel layout: <n> IICR channels, <n> EICR channels */
> +               num_chan *= 2;

Curly braces around multi-line if-branches (even if they are comments)?

> +
> +       chan  = devm_kcalloc(dev, num_chan, sizeof(*chan), GFP_KERNEL);
> +       if (!chan)
> +               return -ENOMEM;
> +
> +       priv->per_chan_privs = devm_kcalloc(dev, num_chan, sizeof(*priv->per_chan_privs),
> +                                           GFP_KERNEL);
> +       if (!priv->per_chan_privs)
> +               return -ENOMEM;
> +
> +       mbox = &priv->mbox;
> +
> +       for (i = 0; i < num_chan; i++)
> +               chan[i].con_priv = &priv->per_chan_privs[i];
> +
> +       mbox->chans = chan;
> +       mbox->num_chans = num_chan;
> +       mbox->txdone_poll = true;
> +       mbox->ops = &mfis_iicr_ops;
> +       mbox->dev = dev;
> +       mbox->of_xlate = mfis_mb_of_xlate;
> +
> +       return mbox_controller_register(mbox);
> +}
> +
> +/****************************************************
> + *             Common

Missing closing asterisk.

> + ****************************************************/
>
> +static int mfis_reg_probe(struct platform_device *pdev, struct mfis_priv *priv,
> +                         struct mfis_reg *mreg, const char *name, bool required)
> +{
> +       struct resource *res;
> +       void __iomem *base;
> +
> +       res = platform_get_resource_byname(pdev, IORESOURCE_MEM, name);
> +
> +       /* If there is no mailbox resource, registers are in the common space */
> +       if (!res && !required) {

Given you only test for the negated "!required", perhaps invert the
logic, and replace "required" by "optional"?

> +               priv->mbox_reg = priv->common_reg;

This left me looking for an assignment to priv->mbox_reg in the "else"
branch ;-) Then I realized "&priv->mbox_reg" is passed as the "mreg"
parameter.  Hence perhaps:

    *mreg = priv->common_reg;

?

> +       } else {
> +               base = devm_ioremap_resource(&pdev->dev, res);
> +               if (IS_ERR(base))
> +                       return PTR_ERR(base);
> +
> +               mreg->base = base;
> +               mreg->start = res->start;
> +               mreg->priv = priv;
> +       }
> +
> +       return 0;
> +}

Gr{oetje,eeting}s,

                        Geert

--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org

In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
                                -- Linus Torvalds
Re: [PATCH 2/3] soc: renesas: Add Renesas R-Car MFIS driver
Posted by Wolfram Sang 2 weeks, 1 day ago
Hi Geert,

thank you for the review!

> > +
> > +struct mfis_per_chan_priv {
> 
> I would drop the "per_".

Probably OK, will think about it.

> 
> > +       u32 reg;
> > +       int irq;
> > +};
> > +
> > +struct mfis_priv {
> > +       struct device *dev;
> > +       struct mfis_reg common_reg;
> > +       struct mfis_reg mbox_reg;
> > +       const struct mfis_info *info;
> > +
> > +       /* mailbox private data */
> > +       struct mbox_controller mbox;
> > +       struct mfis_per_chan_priv *per_chan_privs;
> 
> Likewise.
> This could be a flexible array, if it weren't for the hwspinlock array
> you will have to add later, right?

No, hwspinlock doesn't need any private data here. But something else
could come in the future maybe? I also don't see a big advantage of the
flexible array, too. Maybe it's too late in the evening...

> > +static void mfis_write(struct mfis_reg *mreg, u32 reg, u32 val)
> 
> Both writel() and iowrite32() use the inverse order of "reg" and "val".
> But I can understand you want to keep "mreg" and "reg" together.

Yes, please!

> > +/****************************************************
> > + *                     Mailbox
> 
> Missing closing asterisk ;-)

OK.

> 
> > + ****************************************************/
> > +
> > +#define mfis_mb_mbox_to_priv(_m) container_of((_m), struct mfis_priv, mbox)
> 
> Both "mb" and "mbox", so perhaps mfis_mbox_to_priv()?
> And perhaps s/mb_/mbox_/ everywhere?

Well, not sure here. 'mb' marks the mailbox part of the driver. 'mbox'
is the variable we work on. So, it has a pattern.

> > +       struct mfis_per_chan_priv *per_chan_priv = chan->con_priv;
> > +       int ret = 0;
> > +
> > +       if (per_chan_priv->irq)
> > +               ret = request_irq(per_chan_priv->irq, mfis_mb_iicr_interrupt, 0,
> > +                                 dev_name(chan->mbox->dev), chan);
> > +
> > +       return ret;
> 
> You can reduce indentation, and get rid of ret, by inverting the
> conditional.

I like this.

> > +static void mfis_mb_shutdown(struct mbox_chan *chan)
> > +{
> > +       struct mfis_per_chan_priv *per_chan_priv = chan->con_priv;
> > +
> > +       free_irq(per_chan_priv->irq, chan);
> 
> if (per_chan_priv->irq) ...
> 
> free_irq() seems to support zero, but irq_to_desc() still has to
> traverse the mtree.

I checked it and it prints a warning, so 'if' is good.

> > +       chan_num = sp->args[0];
> 
> "chan_num" is the hardware channel number, and should be validated
> against mpriv->info->mb_num_channels.

Ack!

> 
> > +       chan_flags = sp->args[1];
> > +
> > +       /* Channel layout is described in mfis_mb_probe() */
> 
> Given you already use "chan += ..." below, perhaps:
> 
>     chan = mbox->chans + chan_num;
> 
> > +       if (priv->info->mb_channels_are_unidir) {
> > +               is_only_rx = chan_flags & MFIS_CHANNEL_RX;
> > +               chan = mbox->chans + 2 * chan_num + is_only_rx;
> 
> ...and:
> 
>     chan += chan_num + is_only_rx;
> 
> > +       } else {
> > +               is_only_rx = false;
> > +               chan = mbox->chans + chan_num;
> 
> ... and drop this line?
> With a proper preinitalization of is_only_rx, the whole "else" branch
> can be dropped.

I agree it saves a few lines, but I really think the original code is
easier to understand. Channel layout is already wickes and doing 'channel
+=' twice is harder to understand than a simple assignment IMO.

> 
> > +       }
> > +
> > +       if (priv->info->mb_reg_comes_from_dt) {
> > +               tx_uses_eicr = chan_flags & MFIS_CHANNEL_EICR;
> > +               if (tx_uses_eicr)
> > +                       chan += mbox->num_chans / 2;
> > +       } else {
> > +               tx_uses_eicr = priv->info->mb_tx_uses_eicr;
> > +       }
> 
> "chan - mbox->chans" is the logical channel number, and should be
> validated against mbox_num_chans, to avoid out-of-bound accesses.

"chan - ..."? You mean "chan + ..."?

> 
> > +
> > +       per_chan_priv = chan->con_priv;
> > +       per_chan_priv->reg = chan_num * 0x1000 + (tx_uses_eicr ^ is_only_rx) * 4;
> 
> I think it would be good to document this register is either an IICR
> or EICR register offset, through:
>   1. A comment, or
>   2. Definitions and code, e.g
> 
>          #define MFISIICR 0x00
>          #define MFISEICR 0x04
> 
>          if (tx_uses_eicr ^ is_only_rx)
>              per_chan_priv->reg = chan_num * 0x1000 + MFISEICR;
>          else
>              per_chan_priv->reg = chan_num * 0x1000 + MFISIICR;
> 
>      Or:
> 
>          #define MFISIICR(i) ((i) * 0x1000 + 0x00)
>          #define MFISEICR(i) ((i) * 0x1000 + 0x04)
> 
>          per_chan_priv->reg = (tx_uses_eicr ^ is_only_rx) ? MFISEICR(chan_num)
>                                                           : MFISIICR(chan_num);
> 

I like the latter one. Let my try this. But maybe it will be just a
comment in the end ;)

> > +
> > +       if (!priv->info->mb_channels_are_unidir || is_only_rx) {
> > +               char irqname[8];
> > +               char suffix = tx_uses_eicr ? 'i' : 'e';
> > +
> > +               /* "ch0i" or "ch0e" */
> > +               scnprintf(irqname, sizeof(irqname), "ch%d%c", chan_num, suffix);
> 
> "%u" for unsigned chan_num.

OK:

> 
> > +
> > +               per_chan_priv->irq = of_irq_get_byname(mbox->dev->of_node, irqname);
> > +               if (per_chan_priv->irq < 0)
> > +                       return ERR_PTR(per_chan_priv->irq);
> > +               else if (per_chan_priv->irq == 0)
> 
> No need for "else" after "return".

OK.

> 
> > +                       return ERR_PTR(-ENOENT);
> > +       }
> > +
> > +       return chan;
> > +}
> > +
> > +static int mfis_mb_probe(struct mfis_priv *priv)
> > +{
> > +       struct device *dev = priv->dev;
> > +       struct mbox_chan *chan;
> > +       struct mbox_controller *mbox;
> > +       unsigned int i, num_chan = priv->info->mb_num_channels;
> 
> "i" is only used in the for-loop below, so you could declare it in the
> for-statement. As a bonus, that would avoid mixing the declaration of
> uninitialized and initialized variables.

Yes!

> 
> > +
> > +       if (priv->info->mb_channels_are_unidir)
> > +               /* Channel layout: Ch0-TX, Ch0-RX, Ch1-TX... */
> > +               num_chan *= 2;
> > +
> > +       if (priv->info->mb_reg_comes_from_dt)
> > +               /* Channel layout: <n> IICR channels, <n> EICR channels */
> > +               num_chan *= 2;
> 
> Curly braces around multi-line if-branches (even if they are comments)?

OK? I don't mind.

> > +/****************************************************
> > + *             Common
> 
> Missing closing asterisk.

OK.

> 
> > + ****************************************************/
> >
> > +static int mfis_reg_probe(struct platform_device *pdev, struct mfis_priv *priv,
> > +                         struct mfis_reg *mreg, const char *name, bool required)
> > +{
> > +       struct resource *res;
> > +       void __iomem *base;
> > +
> > +       res = platform_get_resource_byname(pdev, IORESOURCE_MEM, name);
> > +
> > +       /* If there is no mailbox resource, registers are in the common space */
> > +       if (!res && !required) {
> 
> Given you only test for the negated "!required", perhaps invert the
> logic, and replace "required" by "optional"?

I had this first and it looked weird that the first call had "false" and
the second one "true". I liked it better this way but don't really mind.

> 
> > +               priv->mbox_reg = priv->common_reg;
> 
> This left me looking for an assignment to priv->mbox_reg in the "else"
> branch ;-) Then I realized "&priv->mbox_reg" is passed as the "mreg"
> parameter.  Hence perhaps:
> 
>     *mreg = priv->common_reg;
> 
> ?

Yup, that should work.

I will work on the next version tomorrow. Thank you for your input!

Happy hacking,

   Wolfram

Re: [PATCH 2/3] soc: renesas: Add Renesas R-Car MFIS driver
Posted by Geert Uytterhoeven 2 weeks ago
Hi Wolfram,

On Sun, 22 Mar 2026 at 20:58, Wolfram Sang
<wsa+renesas@sang-engineering.com> wrote:
> > > +struct mfis_priv {
> > > +       struct device *dev;
> > > +       struct mfis_reg common_reg;
> > > +       struct mfis_reg mbox_reg;
> > > +       const struct mfis_info *info;
> > > +
> > > +       /* mailbox private data */
> > > +       struct mbox_controller mbox;
> > > +       struct mfis_per_chan_priv *per_chan_privs;
> >
> > This could be a flexible array, if it weren't for the hwspinlock array
> > you will have to add later, right?
>
> No, hwspinlock doesn't need any private data here. But something else
> could come in the future maybe? I also don't see a big advantage of the
> flexible array, too. Maybe it's too late in the evening...

You're right. Somehow I thought you were allocating priv and
per_chan_privs next to each other.

> > > +       chan_num = sp->args[0];
> >
> > "chan_num" is the hardware channel number, and should be validated
> > against mpriv->info->mb_num_channels.
>
> Ack!
>
> > > +       chan_flags = sp->args[1];
> > > +
> > > +       /* Channel layout is described in mfis_mb_probe() */
> >
> > Given you already use "chan += ..." below, perhaps:
> >
> >     chan = mbox->chans + chan_num;
> >
> > > +       if (priv->info->mb_channels_are_unidir) {
> > > +               is_only_rx = chan_flags & MFIS_CHANNEL_RX;
> > > +               chan = mbox->chans + 2 * chan_num + is_only_rx;
> >
> > ...and:
> >
> >     chan += chan_num + is_only_rx;
> >
> > > +       } else {
> > > +               is_only_rx = false;
> > > +               chan = mbox->chans + chan_num;
> >
> > ... and drop this line?
> > With a proper preinitalization of is_only_rx, the whole "else" branch
> > can be dropped.
>
> I agree it saves a few lines, but I really think the original code is
> easier to understand. Channel layout is already wickes and doing 'channel
> +=' twice is harder to understand than a simple assignment IMO.
>
> >
> > > +       }
> > > +
> > > +       if (priv->info->mb_reg_comes_from_dt) {
> > > +               tx_uses_eicr = chan_flags & MFIS_CHANNEL_EICR;
> > > +               if (tx_uses_eicr)
> > > +                       chan += mbox->num_chans / 2;
> > > +       } else {
> > > +               tx_uses_eicr = priv->info->mb_tx_uses_eicr;
> > > +       }
> >
> > "chan - mbox->chans" is the logical channel number, and should be
> > validated against mbox_num_chans, to avoid out-of-bound accesses.
>
> "chan - ..."? You mean "chan + ..."?

No, I did mean "-": you do have a pointer "chan" to the channel,
instead of an index into the mbox->chans[] array.
Using a  index would  make validation easier to read, though.

Gr{oetje,eeting}s,

                        Geert

-- 
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org

In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
                                -- Linus Torvalds
Re: [PATCH 2/3] soc: renesas: Add Renesas R-Car MFIS driver
Posted by Wolfram Sang 2 weeks ago
> > > > +       if (priv->info->mb_reg_comes_from_dt) {
> > > > +               tx_uses_eicr = chan_flags & MFIS_CHANNEL_EICR;
> > > > +               if (tx_uses_eicr)
> > > > +                       chan += mbox->num_chans / 2;
> > > > +       } else {
> > > > +               tx_uses_eicr = priv->info->mb_tx_uses_eicr;
> > > > +       }
> > >
> > > "chan - mbox->chans" is the logical channel number, and should be
> > > validated against mbox_num_chans, to avoid out-of-bound accesses.
> >
> > "chan - ..."? You mean "chan + ..."?
> 
> No, I did mean "-": you do have a pointer "chan" to the channel,
> instead of an index into the mbox->chans[] array.
> Using a  index would  make validation easier to read, though.

Sorry, I still don't get it: If 'chan_num' has been sanitized above to
be in the range of 0..priv->info->mb_num_channels - 1, then how can a
OOB happen here? "chan += mbox->num_chans / 2" only happens when
'mb_reg_comes_from_dt' is set. But when it is set, the array of chans is
also always doubled to have the "<n> IICR, then <n> EICR" layout.

Re: [PATCH 2/3] soc: renesas: Add Renesas R-Car MFIS driver
Posted by Geert Uytterhoeven 2 weeks ago
Hi Wolfram,

On Mon, 23 Mar 2026 at 10:29, Wolfram Sang
<wsa+renesas@sang-engineering.com> wrote:
> > > > > +       if (priv->info->mb_reg_comes_from_dt) {
> > > > > +               tx_uses_eicr = chan_flags & MFIS_CHANNEL_EICR;
> > > > > +               if (tx_uses_eicr)
> > > > > +                       chan += mbox->num_chans / 2;
> > > > > +       } else {
> > > > > +               tx_uses_eicr = priv->info->mb_tx_uses_eicr;
> > > > > +       }
> > > >
> > > > "chan - mbox->chans" is the logical channel number, and should be
> > > > validated against mbox_num_chans, to avoid out-of-bound accesses.
> > >
> > > "chan - ..."? You mean "chan + ..."?
> >
> > No, I did mean "-": you do have a pointer "chan" to the channel,
> > instead of an index into the mbox->chans[] array.
> > Using a  index would  make validation easier to read, though.
>
> Sorry, I still don't get it: If 'chan_num' has been sanitized above to
> be in the range of 0..priv->info->mb_num_channels - 1, then how can a
> OOB happen here? "chan += mbox->num_chans / 2" only happens when
> 'mb_reg_comes_from_dt' is set. But when it is set, the array of chans is
> also always doubled to have the "<n> IICR, then <n> EICR" layout.

Sorry, you are right.
I guess I was thinking the flags specified in DT could cause an invalid
index doubling, but those can indeed not happen due to the
priv->info->mb_* flag checks.

Gr{oetje,eeting}s,

                        Geert

-- 
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org

In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
                                -- Linus Torvalds
Sashiko review feedback (was Re: [PATCH 2/3] soc: renesas: Add Renesas R-Car MFIS driver)
Posted by Wolfram Sang 2 weeks, 1 day ago
Hi all,

I am usually not into AI but I can definitely see value in its review
assistance. So, I checked what Sashiko thinks of my series and found the
comments to be largely reasonable. I'll copy and answer them here, a)
for a more complete review process and b) to give feedback to the
Sashiko-devs (CCed). Thank you for the service and efforts, I hope we
can justify the additional energy consumption here. Please let me know
if such replies are actually helpful for you.

> > +static void mfis_write(struct mfis_reg *mreg, u32 reg, u32 val)
> > +{
> > +	struct mfis_priv *priv = mreg->priv;
> > +	u32 unprotect_mask = priv->info->unprotect_mask;
> > +	u32 unprotect_code;
> > +
> > +	/*
> > +	 * [Gen4] key: 0xACCE0000, mask: 0x0000FFFF
> > +	 * [Gen5] key: 0xACC00000, mask: 0x000FFFFF
> > +	 */
> > +	unprotect_code = (MFIS_UNPROTECT_KEY & ~unprotect_mask) |
> > +			 ((mreg->start | reg) & unprotect_mask);
> 
> The mreg->start and reg variables are combined with a bitwise OR. If
> mreg->start has overlapping bits with the reg offset, could this yield an
> incorrect physical address for the unprotect code? Should this use
> addition like mreg->start + reg instead?

No, if a device tree is so wrongly setup that the bits overlap, then '+'
instead of '|' will only lead to a different wrong address.

> > +
> > +	iowrite32(unprotect_code, priv->common_reg.base + MFISWACNTR);
> > +	iowrite32(val, mreg->base + reg);
> > +}

> Can mfis_write() be called concurrently by different processors or
> interrupted by an interrupt handler like mfis_mb_iicr_interrupt()?

Yes, it can. It even needs a hwspinlock instead of a regular spinlock
because it needs protection against cores which may not run Linux.
hwspinlock support will only be added later because of dependency
issues. My plan was to add the protection once we got hwspinlocks. But
maybe I should add just spinlocks now and convert them to hwspinlocks
once their support is in.

> > +static void mfis_mb_shutdown(struct mbox_chan *chan)
> > +{
> > +	struct mfis_per_chan_priv *per_chan_priv = chan->con_priv;
> > +
> > +	free_irq(per_chan_priv->irq, chan);
> > +}

> Since mfis_mb_of_xlate() skips interrupt lookup for transmit-only channels,
> leaving per_chan_priv->irq as 0, and mfis_mb_startup() guards the request
> with if (per_chan_priv->irq), will this unconditional free_irq() trigger a
> kernel warning when shutting down a transmit-only channel?

Yes, I seemed to remember that free_irq() can handle this, but it will
in-deed trigger a warning. Also, the reasons Geert mentioned in his
review apply.

> [ ... ]
> > +static struct mbox_chan *mfis_mb_of_xlate(struct mbox_controller *mbox,
> > +					  const struct of_phandle_args *sp)
> > +{
> > +	struct mfis_priv *priv = mfis_mb_mbox_to_priv(mbox);
> > +	struct mfis_per_chan_priv *per_chan_priv;
> > +	struct mbox_chan *chan;
> > +	u32 chan_num, chan_flags;
> > +	bool tx_uses_eicr, is_only_rx;
> > +
> > +	if (sp->args_count != 2)
> > +		return ERR_PTR(-EINVAL);
> > +
> > +	chan_num = sp->args[0];
> > +	chan_flags = sp->args[1];
> > +
> > +	/* Channel layout is described in mfis_mb_probe() */
> > +	if (priv->info->mb_channels_are_unidir) {
> > +		is_only_rx = chan_flags & MFIS_CHANNEL_RX;
> > +		chan = mbox->chans + 2 * chan_num + is_only_rx;

> Does this code allow out-of-bounds access if a malformed device tree
> provides an arbitrarily large chan_num? Should chan_num be checked against
> priv->info->mb_num_channels before computing the chan pointer?

Yes, Geert pointed this out as well.

> [ ... ]
> > +static int mfis_mb_probe(struct mfis_priv *priv)
> > +{
> [ ... ]
> > +	mbox->chans = chan;
> > +	mbox->num_chans = num_chan;
> > +	mbox->txdone_poll = true;
> > +	mbox->ops = &mfis_iicr_ops;
> > +	mbox->dev = dev;
> > +	mbox->of_xlate = mfis_mb_of_xlate;
> > +
> > +	return mbox_controller_register(mbox);
> > +}
> The mailbox controller is registered using mbox_controller_register(), but
> the driver lacks a remove callback to unregister it.
> If the driver is unbound, devres will free the devm_kzalloc structures
> backing the mailbox. Will this result in a use-after-free regression since
> the mailbox subsystem still retains the controller in its global lists?
> Should devm_mbox_controller_register() be used instead?
> [ ... ]

Yes, this was overlooked so far.

> > +static int mfis_probe(struct platform_device *pdev)
> > +{
> > +	struct mfis_priv *priv;
> > +	struct device *dev = &pdev->dev;
> > +	int ret;
> > +
> > +	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
> > +	if (!priv)
> > +		return -ENOMEM;
> > +
> > +	priv->dev = dev;
> > +	priv->info = of_device_get_match_data(dev);
> Can of_device_get_match_data() return a null pointer here?
> If the driver is bound manually via sysfs or instantiated in an environment
> lacking device tree match data, would this lead to a null pointer
> dereference regression later when accessing priv->info->mb_num_channels in
> mfis_mb_probe()?

The latter case would be clearly a driver bug. In addition with the
former case, it probably makes sense to handle this more gracefully,
though.

Happy hacking,

   Wolfram

Re: Sashiko review feedback (was Re: [PATCH 2/3] soc: renesas: Add Renesas R-Car MFIS driver)
Posted by Roman Gushchin 1 week, 6 days ago
Wolfram Sang <wsa+renesas@sang-engineering.com> writes:

> Hi all,
>
> I am usually not into AI but I can definitely see value in its review
> assistance. So, I checked what Sashiko thinks of my series and found the
> comments to be largely reasonable. I'll copy and answer them here, a)
> for a more complete review process and b) to give feedback to the
> Sashiko-devs (CCed). Thank you for the service and efforts, I hope we
> can justify the additional energy consumption here. Please let me know
> if such replies are actually helpful for you.

Hi Wolfram!

These replies are definitely helpful! I can't realistically look into
all cases across all subsystems (I simple have not enough expertise),
but I'll try to look into most cases and hope that other engineers will
help here.

In your case it looks like most findings were real?

Thank you!

>
>> > +static void mfis_write(struct mfis_reg *mreg, u32 reg, u32 val)
>> > +{
>> > +	struct mfis_priv *priv = mreg->priv;
>> > +	u32 unprotect_mask = priv->info->unprotect_mask;
>> > +	u32 unprotect_code;
>> > +
>> > +	/*
>> > +	 * [Gen4] key: 0xACCE0000, mask: 0x0000FFFF
>> > +	 * [Gen5] key: 0xACC00000, mask: 0x000FFFFF
>> > +	 */
>> > +	unprotect_code = (MFIS_UNPROTECT_KEY & ~unprotect_mask) |
>> > +			 ((mreg->start | reg) & unprotect_mask);
>> 
>> The mreg->start and reg variables are combined with a bitwise OR. If
>> mreg->start has overlapping bits with the reg offset, could this yield an
>> incorrect physical address for the unprotect code? Should this use
>> addition like mreg->start + reg instead?
>
> No, if a device tree is so wrongly setup that the bits overlap, then '+'
> instead of '|' will only lead to a different wrong address.
>
>> > +
>> > +	iowrite32(unprotect_code, priv->common_reg.base + MFISWACNTR);
>> > +	iowrite32(val, mreg->base + reg);
>> > +}
>
>> Can mfis_write() be called concurrently by different processors or
>> interrupted by an interrupt handler like mfis_mb_iicr_interrupt()?
>
> Yes, it can. It even needs a hwspinlock instead of a regular spinlock
> because it needs protection against cores which may not run Linux.
> hwspinlock support will only be added later because of dependency
> issues. My plan was to add the protection once we got hwspinlocks. But
> maybe I should add just spinlocks now and convert them to hwspinlocks
> once their support is in.
>
>> > +static void mfis_mb_shutdown(struct mbox_chan *chan)
>> > +{
>> > +	struct mfis_per_chan_priv *per_chan_priv = chan->con_priv;
>> > +
>> > +	free_irq(per_chan_priv->irq, chan);
>> > +}
>
>> Since mfis_mb_of_xlate() skips interrupt lookup for transmit-only channels,
>> leaving per_chan_priv->irq as 0, and mfis_mb_startup() guards the request
>> with if (per_chan_priv->irq), will this unconditional free_irq() trigger a
>> kernel warning when shutting down a transmit-only channel?
>
> Yes, I seemed to remember that free_irq() can handle this, but it will
> in-deed trigger a warning. Also, the reasons Geert mentioned in his
> review apply.
>
>> [ ... ]
>> > +static struct mbox_chan *mfis_mb_of_xlate(struct mbox_controller *mbox,
>> > +					  const struct of_phandle_args *sp)
>> > +{
>> > +	struct mfis_priv *priv = mfis_mb_mbox_to_priv(mbox);
>> > +	struct mfis_per_chan_priv *per_chan_priv;
>> > +	struct mbox_chan *chan;
>> > +	u32 chan_num, chan_flags;
>> > +	bool tx_uses_eicr, is_only_rx;
>> > +
>> > +	if (sp->args_count != 2)
>> > +		return ERR_PTR(-EINVAL);
>> > +
>> > +	chan_num = sp->args[0];
>> > +	chan_flags = sp->args[1];
>> > +
>> > +	/* Channel layout is described in mfis_mb_probe() */
>> > +	if (priv->info->mb_channels_are_unidir) {
>> > +		is_only_rx = chan_flags & MFIS_CHANNEL_RX;
>> > +		chan = mbox->chans + 2 * chan_num + is_only_rx;
>
>> Does this code allow out-of-bounds access if a malformed device tree
>> provides an arbitrarily large chan_num? Should chan_num be checked against
>> priv->info->mb_num_channels before computing the chan pointer?
>
> Yes, Geert pointed this out as well.
>
>> [ ... ]
>> > +static int mfis_mb_probe(struct mfis_priv *priv)
>> > +{
>> [ ... ]
>> > +	mbox->chans = chan;
>> > +	mbox->num_chans = num_chan;
>> > +	mbox->txdone_poll = true;
>> > +	mbox->ops = &mfis_iicr_ops;
>> > +	mbox->dev = dev;
>> > +	mbox->of_xlate = mfis_mb_of_xlate;
>> > +
>> > +	return mbox_controller_register(mbox);
>> > +}
>> The mailbox controller is registered using mbox_controller_register(), but
>> the driver lacks a remove callback to unregister it.
>> If the driver is unbound, devres will free the devm_kzalloc structures
>> backing the mailbox. Will this result in a use-after-free regression since
>> the mailbox subsystem still retains the controller in its global lists?
>> Should devm_mbox_controller_register() be used instead?
>> [ ... ]
>
> Yes, this was overlooked so far.
>
>> > +static int mfis_probe(struct platform_device *pdev)
>> > +{
>> > +	struct mfis_priv *priv;
>> > +	struct device *dev = &pdev->dev;
>> > +	int ret;
>> > +
>> > +	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
>> > +	if (!priv)
>> > +		return -ENOMEM;
>> > +
>> > +	priv->dev = dev;
>> > +	priv->info = of_device_get_match_data(dev);
>> Can of_device_get_match_data() return a null pointer here?
>> If the driver is bound manually via sysfs or instantiated in an environment
>> lacking device tree match data, would this lead to a null pointer
>> dereference regression later when accessing priv->info->mb_num_channels in
>> mfis_mb_probe()?
>
> The latter case would be clearly a driver bug. In addition with the
> former case, it probably makes sense to handle this more gracefully,
> though.
>
> Happy hacking,
>
>    Wolfram
Re: Sashiko review feedback (was Re: [PATCH 2/3] soc: renesas: Add Renesas R-Car MFIS driver)
Posted by Wolfram Sang 1 week, 6 days ago
Hi Roman,

> These replies are definitely helpful! I can't realistically look into
> all cases across all subsystems (I simple have not enough expertise),
> but I'll try to look into most cases and hope that other engineers will
> help here.

Sure thing. Is there a dedicated mailing-list or better email address I
can add?

> In your case it looks like most findings were real?

To my surprise, yes :)

Happy hacking,

   Wolfram

Re: Sashiko review feedback (was Re: [PATCH 2/3] soc: renesas: Add Renesas R-Car MFIS driver)
Posted by Roman Gushchin 1 week, 6 days ago
Wolfram Sang <wsa+renesas@sang-engineering.com> writes:

> Hi Roman,
>
>> These replies are definitely helpful! I can't realistically look into
>> all cases across all subsystems (I simple have not enough expertise),
>> but I'll try to look into most cases and hope that other engineers will
>> help here.
>
> Sure thing. Is there a dedicated mailing-list or better email address I
> can add?

Not yet, but I think of creating one.

Thanks!
Re: Sashiko review feedback (was Re: [PATCH 2/3] soc: renesas: Add Renesas R-Car MFIS driver)
Posted by Wolfram Sang 1 week ago
> > Sure thing. Is there a dedicated mailing-list or better email address I
> > can add?
> 
> Not yet, but I think of creating one.

Until that exists, shall I use your email to add Reported-by tags? In
another of my series, Sashiko found valid issues which already existed
before my series. A tag would be proper, I'd think?

Re: Sashiko review feedback (was Re: [PATCH 2/3] soc: renesas: Add Renesas R-Car MFIS driver)
Posted by Theodore Tso 1 week ago
On Mon, Mar 30, 2026 at 10:57:44AM +0200, Wolfram Sang wrote:
> 
> > > Sure thing. Is there a dedicated mailing-list or better email address I
> > > can add?
> > 
> > Not yet, but I think of creating one.
> 
> Until that exists, shall I use your email to add Reported-by tags? In
> another of my series, Sashiko found valid issues which already existed
> before my series. A tag would be proper, I'd think?

I was thinking about proposing some tagging convention such as:

   Suggested-by: Sashiko:Gemini 3.1 Pro
or
   Reviewed-by: Sashiko:Gemini 3.1 Pro

to Documentation/process/coding-assistants.rst.  Alas, neither is
perfect.

Suggested-by: is generlly used when someone inspires a particular
commit.  This might apply if Sashiko found a problem as an incidental
finding, which we then fixed in a subsequent commit.  An example of
this might be[1], or in the case which you suggested above.  But what
if we just fixed one of the issues raised by Sashiko in an earlier
version of the commit.  In that case, Suggested-by: doesn't seem to be
a perfect fit.

[1] https://lore.kernel.org/r/20260327063330.1312426-1-tytso@mit.edu/

Reviewed-by: is generally only applicable once *all* of the issues
identified by the reviewer has been resolved, and it's not clear this
is applicable if not all of the issues rasied by Sashiko were
resolved.  In some cases, these might be false positives, but in the
case of a human reviewer, the human reviewer agrees before saying,
"You can add Reviewed-by: ..." to the commit.  Unfortunately, it will
probably be a while before LLM's have that kind of agency / consciousness.  :-)

What do folks think?  How should we codify a way of giving Sashiko
credit for issues that it has raised?  (Assuming we should, but
hopefully that's not controversial.)

          	     		       	      - Ted
Re: Sashiko review feedback (was Re: [PATCH 2/3] soc: renesas: Add Renesas R-Car MFIS driver)
Posted by Wolfram Sang 6 days, 16 hours ago
Hi Ted,

> I was thinking about proposing some tagging convention such as:
> 
>    Suggested-by: Sashiko:Gemini 3.1 Pro
> or
>    Reviewed-by: Sashiko:Gemini 3.1 Pro
> 
> to Documentation/process/coding-assistants.rst.  Alas, neither is
> perfect.

Yes, maybe an email is not so much needed and the information about
Sashiko and Gemini is much more relevant.

> Suggested-by: is generlly used when someone inspires a particular
> commit.  This might apply if Sashiko found a problem as an incidental
> finding, which we then fixed in a subsequent commit.  An example of
> this might be[1], or in the case which you suggested above.  But what

What about Reported-by? The required closes by would then link to the
actual report:

Reported-by: Sashiko:Gemini 3.1 Pro
Closes: https://sashiko.dev/#/patchset/20260319105947.6237-1-wsa%2Brenesas%40sang-engineering.com

The drawback currently is that only the whole report for the patchset
can be linked. But probably Sashiko-reports could have some more HTML
tags to reference only the paragraph needed.

Happy hacking,

   Wolfram

Re: Sashiko review feedback (was Re: [PATCH 2/3] soc: renesas: Add Renesas R-Car MFIS driver)
Posted by Geert Uytterhoeven 6 days, 15 hours ago
Hi Wolfram,

On Tue, 31 Mar 2026 at 09:18, Wolfram Sang
<wsa+renesas@sang-engineering.com> wrote:
> > I was thinking about proposing some tagging convention such as:
> >
> >    Suggested-by: Sashiko:Gemini 3.1 Pro
> > or
> >    Reviewed-by: Sashiko:Gemini 3.1 Pro
> >
> > to Documentation/process/coding-assistants.rst.  Alas, neither is
> > perfect.
>
> Yes, maybe an email is not so much needed and the information about
> Sashiko and Gemini is much more relevant.
>
> > Suggested-by: is generlly used when someone inspires a particular
> > commit.  This might apply if Sashiko found a problem as an incidental
> > finding, which we then fixed in a subsequent commit.  An example of
> > this might be[1], or in the case which you suggested above.  But what
>
> What about Reported-by? The required closes by would then link to the
> actual report:
>
> Reported-by: Sashiko:Gemini 3.1 Pro
> Closes: https://sashiko.dev/#/patchset/20260319105947.6237-1-wsa%2Brenesas%40sang-engineering.com
>
> The drawback currently is that only the whole report for the patchset
> can be linked. But probably Sashiko-reports could have some more HTML
> tags to reference only the paragraph needed.

And the Closes-tag can only be used when the issue is closed in full.
You can still add a normal Link-tag.

Gr{oetje,eeting}s,

                        Geert

-- 
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org

In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
                                -- Linus Torvalds
Re: Sashiko review feedback (was Re: [PATCH 2/3] soc: renesas: Add Renesas R-Car MFIS driver)
Posted by Wolfram Sang 6 days, 15 hours ago
> > What about Reported-by? The required closes by would then link to the
> > actual report:
> >
> > Reported-by: Sashiko:Gemini 3.1 Pro
> > Closes: https://sashiko.dev/#/patchset/20260319105947.6237-1-wsa%2Brenesas%40sang-engineering.com
> >
> > The drawback currently is that only the whole report for the patchset
> > can be linked. But probably Sashiko-reports could have some more HTML
> > tags to reference only the paragraph needed.
> 
> And the Closes-tag can only be used when the issue is closed in full.
> You can still add a normal Link-tag.

We would need to update checkpatch for that. Doable. I prefer more HTML
tags in the Sashiko report, still, because it will be handy anyhow. The
reports can get quite large and it will make it easier to point people
to a specific item.

Re: Sashiko review feedback (was Re: [PATCH 2/3] soc: renesas: Add Renesas R-Car MFIS driver)
Posted by Geert Uytterhoeven 2 weeks ago
Hi Wolfram,

On Sun, 22 Mar 2026 at 09:59, Wolfram Sang
<wsa+renesas@sang-engineering.com> wrote:
> I am usually not into AI but I can definitely see value in its review
> assistance. So, I checked what Sashiko thinks of my series and found the
> comments to be largely reasonable. I'll copy and answer them here, a)
> for a more complete review process and b) to give feedback to the
> Sashiko-devs (CCed). Thank you for the service and efforts, I hope we
> can justify the additional energy consumption here. Please let me know
> if such replies are actually helpful for you.
>
> > > +static void mfis_write(struct mfis_reg *mreg, u32 reg, u32 val)
> > > +{
> > > +   struct mfis_priv *priv = mreg->priv;
> > > +   u32 unprotect_mask = priv->info->unprotect_mask;
> > > +   u32 unprotect_code;
> > > +
> > > +   /*
> > > +    * [Gen4] key: 0xACCE0000, mask: 0x0000FFFF
> > > +    * [Gen5] key: 0xACC00000, mask: 0x000FFFFF
> > > +    */
> > > +   unprotect_code = (MFIS_UNPROTECT_KEY & ~unprotect_mask) |
> > > +                    ((mreg->start | reg) & unprotect_mask);
> >
> > The mreg->start and reg variables are combined with a bitwise OR. If
> > mreg->start has overlapping bits with the reg offset, could this yield an
> > incorrect physical address for the unprotect code? Should this use
> > addition like mreg->start + reg instead?
>
> No, if a device tree is so wrongly setup that the bits overlap, then '+'
> instead of '|' will only lead to a different wrong address.

While there are no overlapping bits, and thus both OR and PLUS have
the same effect, I agree PLUS is More Correct(TM), as that matches
what you are using in the second iowrite32() below.

> > > +
> > > +   iowrite32(unprotect_code, priv->common_reg.base + MFISWACNTR);
> > > +   iowrite32(val, mreg->base + reg);
> > > +}
>
> > Can mfis_write() be called concurrently by different processors or
> > interrupted by an interrupt handler like mfis_mb_iicr_interrupt()?
>
> Yes, it can. It even needs a hwspinlock instead of a regular spinlock
> because it needs protection against cores which may not run Linux.
> hwspinlock support will only be added later because of dependency
> issues. My plan was to add the protection once we got hwspinlocks. But
> maybe I should add just spinlocks now and convert them to hwspinlocks
> once their support is in.

Have you ever triggered this race condition when running the mbox
test? Or is that impossible due to locking elsewhere?

> > > +static struct mbox_chan *mfis_mb_of_xlate(struct mbox_controller *mbox,
> > > +                                     const struct of_phandle_args *sp)
> > > +{
> > > +   struct mfis_priv *priv = mfis_mb_mbox_to_priv(mbox);
> > > +   struct mfis_per_chan_priv *per_chan_priv;
> > > +   struct mbox_chan *chan;
> > > +   u32 chan_num, chan_flags;
> > > +   bool tx_uses_eicr, is_only_rx;
> > > +
> > > +   if (sp->args_count != 2)
> > > +           return ERR_PTR(-EINVAL);
> > > +
> > > +   chan_num = sp->args[0];
> > > +   chan_flags = sp->args[1];
> > > +
> > > +   /* Channel layout is described in mfis_mb_probe() */
> > > +   if (priv->info->mb_channels_are_unidir) {
> > > +           is_only_rx = chan_flags & MFIS_CHANNEL_RX;
> > > +           chan = mbox->chans + 2 * chan_num + is_only_rx;
>
> > Does this code allow out-of-bounds access if a malformed device tree
> > provides an arbitrarily large chan_num? Should chan_num be checked against
> > priv->info->mb_num_channels before computing the chan pointer?
>
> Yes, Geert pointed this out as well.
>
> > [ ... ]
> > > +static int mfis_mb_probe(struct mfis_priv *priv)
> > > +{
> > [ ... ]
> > > +   mbox->chans = chan;
> > > +   mbox->num_chans = num_chan;
> > > +   mbox->txdone_poll = true;
> > > +   mbox->ops = &mfis_iicr_ops;
> > > +   mbox->dev = dev;
> > > +   mbox->of_xlate = mfis_mb_of_xlate;
> > > +
> > > +   return mbox_controller_register(mbox);
> > > +}
> > The mailbox controller is registered using mbox_controller_register(), but
> > the driver lacks a remove callback to unregister it.
> > If the driver is unbound, devres will free the devm_kzalloc structures
> > backing the mailbox. Will this result in a use-after-free regression since
> > the mailbox subsystem still retains the controller in its global lists?
> > Should devm_mbox_controller_register() be used instead?
> > [ ... ]
>
> Yes, this was overlooked so far.

.suppress_bind_attrs to the rescue? ;-)

> > > +static int mfis_probe(struct platform_device *pdev)
> > > +{
> > > +   struct mfis_priv *priv;
> > > +   struct device *dev = &pdev->dev;
> > > +   int ret;
> > > +
> > > +   priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
> > > +   if (!priv)
> > > +           return -ENOMEM;
> > > +
> > > +   priv->dev = dev;
> > > +   priv->info = of_device_get_match_data(dev);
> > Can of_device_get_match_data() return a null pointer here?
> > If the driver is bound manually via sysfs or instantiated in an environment
> > lacking device tree match data, would this lead to a null pointer
> > dereference regression later when accessing priv->info->mb_num_channels in
> > mfis_mb_probe()?
>
> The latter case would be clearly a driver bug. In addition with the
> former case, it probably makes sense to handle this more gracefully,
> though.

That is an interesting one I never considered before!
So far we always assumed of_device_get_match_data() cannot return
NULL if all match table entries have their .data members filled in.
However, you can indeed still trigger this by using driver_override
to bind to the wrong device, which obviously has no match entry at all:

On Salvator-XS:

# echo fe960000.vsp > /sys/bus/platform/drivers/vsp1/unbind
# echo renesas_spi > /sys/bus/platform/devices/fe960000.vsp/driver_override
# echo fe960000.vsp > /sys/bus/platform/drivers/renesas_spi/bind

BOOM!

Do we care about that?

Gr{oetje,eeting}s,

                        Geert

-- 
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org

In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
                                -- Linus Torvalds
Re: Sashiko review feedback (was Re: [PATCH 2/3] soc: renesas: Add Renesas R-Car MFIS driver)
Posted by Wolfram Sang 2 weeks ago
Hi Geert,

> While there are no overlapping bits, and thus both OR and PLUS have
> the same effect, I agree PLUS is More Correct(TM), as that matches
> what you are using in the second iowrite32() below.

Yes, it is more correct, yes I will change it, yes I don't like
bike-shedding ;)

> > Yes, it can. It even needs a hwspinlock instead of a regular spinlock
> > because it needs protection against cores which may not run Linux.
> > hwspinlock support will only be added later because of dependency
> > issues. My plan was to add the protection once we got hwspinlocks. But
> > maybe I should add just spinlocks now and convert them to hwspinlocks
> > once their support is in.
> 
> Have you ever triggered this race condition when running the mbox
> test? Or is that impossible due to locking elsewhere?

Haven't encountered that yet. But since we are talking about generic
mfis_write() here which can be used in further ways in the future, we
should not rely on external serialization here IMO.

> > Yes, this was overlooked so far.
> 
> .suppress_bind_attrs to the rescue? ;-)

I was considering this for sure, on top of the suggested improvement. I
guess it can be argued for a mailbox and hwspinlock driver, or?

> That is an interesting one I never considered before!
> So far we always assumed of_device_get_match_data() cannot return
> NULL if all match table entries have their .data members filled in.
> However, you can indeed still trigger this by using driver_override
> to bind to the wrong device, which obviously has no match entry at all:

...

> Do we care about that?

I tend to say: yes. On the one hand, using "driver_override" is a
you-better-know-what-you-do command. On the other hand, failing
gracefully is not hard to do and not much code.

Happy hacking,

   Wolfram