DMA Engine development
 help / color / mirror / Atom feed
* [RFC PATCH 0/3] soc: renesas: Add address extension support for RZ/V2H
@ 2026-09-09 19:36 Prabhakar
  2026-09-09 19:36 ` [RFC PATCH 1/3] soc: renesas: rz-sysc: Configure AOF registers from dma-ranges Prabhakar
                   ` (2 more replies)
  0 siblings, 3 replies; 5+ messages in thread
From: Prabhakar @ 2026-09-09 19:36 UTC (permalink / raw)
  To: Geert Uytterhoeven, Magnus Damm, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley
  Cc: linux-renesas-soc, devicetree, linux-kernel, dmaengine,
	linux-arm-kernel, Prabhakar, Biju Das, Fabrizio Castro,
	Lad Prabhakar

From: Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>

Hi all,

This series adds support for the SYS_AOF (Address Offset Function)
address extension mechanism on RZ/V2H, and enables it for the SDHI
internal DMAC so SD/eMMC DMA buffers are no longer confined to the
low 4 GiB of physical memory.

Background
==========

RZ/V2H contains bus masters (SDHI, USB, GBETH, VCD, DSI, LCDC, ISP,
DMAC, ...) that can only generate a 32-bit address on their own
bus. The SYS controller's SYS_AOFn registers let bits [31:30] of the
address such a master issues select one of four independently
configurable 6-bit fields; the selected field supplies the
corresponding upper bits ([34:30] or [35:30]) actually driven onto
the AXI interconnect, with bits [29:0] passed straight through. This
splits each master's native 4 GiB space into four relocatable 1 GiB
windows, letting it reach a 35-bit (DDR0/DDR1) or 36-bit (+PCIe0/
PCIe1) physical address space. See RZ/V2H Group Hardware User's
Manual, Figure 1.7-2 and Sections 1.7.3.2 and 4.3.3.2.88-90,
reproduced here in text form:

                     +--------------------+
                     |       Master       |
                     |  32-bit address    |
                     |      [31:0]        |
                     +----------+---------+
                                |
              +-----------------+-------------------+
              | bits [31:30]                        | bits [29:0]
              | select field                        | passed through
              v                                     |
    +---------------------------------+             |
    |         SYS_AOFn register       |             |
    |  +------+------+------+------+  |             |
    |  | Reg3 | Reg2 | Reg1 | Reg0 |  |             |
    |  |  11  |  10  |  01  |  00  |  |             |
    |  +--+---+--+---+--+---+--+---+  |             |
    +-----|------|------|------|------+             |
          +------+------+------+ supplies           |
                     |    [35:30] or [34:30]        |
                     v                              v
              +-------------------------------------+
              |          AXI interconnect           |
              |        35- or 36-bit address        |
              +--------------------+----------------+
                                   |
                                   v
     +-----------------------------------------------------+
     |               Physical address space                |
     |  F_FFFF_FFFFh +-----------------------------------+ |
     |               |          PCIe1 - 23 GiB           | |
     |  A_4000_0000h +-----------------------------------+ |
     |               |          PCIe0 - 24 GiB           | |
     |  4_4000_0000h +-----------------------------------+ |
     |               |           DDR1 - 8 GiB            | |
     |  2_4000_0000h +-----------------------------------+ |
     |               |           DDR0 - 8 GiB            | |
     |  0_4000_0000h +-----------------------------------+ |
     |               |  SRAM, peripheral, xSPI - 1 GiB   | |
     |  0_0000_0000h +-----------------------------------+ |
     +-----------------------------------------------------+

     Four 1 GiB windows, each independently redirectable to
     any 1 GiB-aligned target above; 35-bit-limited masters
     must keep bit [5] of every field clear (1.7.3.2.1).

In case the image isnt formatted correctly please refer the link
https://raw.githubusercontent.com/prabhakarlad/img/refs/heads/main/Screenshot%202026-08-12%20162004.png

Without this, SDHI (and other masters in the same category) are
limited to buffers in the low 4 GiB.

Approach
========

Rather than hardcoding SYS_AOFn programming per board, this series
derives it from the devicetree dma-ranges property already used to
describe DMA address translation:

 - Patch 1 adds generic AOF parsing/programming infrastructure to
   the rz-sysc driver. Each SoC lists its AOF-capable masters as
   (MMIO base, SYS_AOFn index, address width) tuples; at probe time,
   the driver walks "simple-bus" wrapper nodes carrying a dma-ranges
   property, matches the wrapped device against that table by MMIO
   base, and programs the corresponding SYS_AOFn fields directly
   from the dma-ranges translation. Devices with status = "disabled"
   are skipped. Malformed or out-of-range entries (misaligned,
   exceeding the master's own 4 GiB space, or requiring bit [5] on a
   35-bit-limited master) are rejected.

 - Patch 2 moves the SDHI nodes in r9a09g057.dtsi into per-instance
   simple-bus wrapper nodes with a default, no-op (DDR0-targeting)
   dma-ranges -- no functional change for boards that don't further
   override it.

 - Patch 3 demonstrates a non-default window on the RZ/V2H EVK:
   SDHI1's dma-ranges is pointed at an 8-12 GiB DDR1 window, backed
   by a restricted-dma-pool reserved-memory region so buffers handed
   to SDHI1 are guaranteed to physically land inside the range the
   programmed SYS_AOF17 fields can actually reach.

Boards that do not override an SDHI instance's dma-ranges see no
functional change: the default wrapper mapping added in patch 3 is
an identity remap onto DDR0's native address.

Trade-offs
==========

- Attaching a restricted-dma-pool to a device makes every DMA transfer
for that device bounce unconditionally (is_swiotlb_force_bounce()),
regardless of whether the buffer's physical address would otherwise
have been directly usable.
- Each master will require a restricted-dma-pool.

Testing
=======

Tested on the RZ/V2H EVK (r9a09g057h44-rzv2h-evk) with SDHI1 remapped
to an 8-12 GiB DDR1 window, running repeated large-file dd read/write
and mmc_test against an SD card, with no functional errors observed.
While running mmc_test a loss of ~4% was seen for read/write tests as
the mmc_test module allocated the memory using GFP_DMA flag.

This series does not yet cover the remaining AOF-capable masters
(USB, GBETH, VCD/DSI/LCDC/ISP, DMAC channels).

Request for feedback
=====================
The ~4% loss above comes from restricted-dma-pool's unconditional
bounce policy (is_swiotlb_force_bounce()): every SDHI transfer is
copied through the pool, even when the source buffer's physical
address would already have been usable without any copy at all.
This is a direct consequence of using restricted-dma-pool as the
correctness mechanism for a device whose real addressing constraint
doesn't map onto the flat dma_mask/dma-ranges model the generic DMA
layer otherwise assumes.

Before optimizing this further, I'd like inputs on whether
restricted-dma-pool is the right tool here. Suggestions and pointers
to prior art for similar SoC address-extension/aperture hardware would
be very welcome.

Thanks for reviewing.

Cheers,
Prabhakar

Lad Prabhakar (3):
  soc: renesas: rz-sysc: Configure AOF registers from dma-ranges
  arm64: dts: renesas: r9a09g057: Move SDHI nodes into bus nodes
  arm64: dts: renesas: r9a09g057h44-rzv2h-evk: Move SDHI1 dma range from
    8-12 GiB

 arch/arm64/boot/dts/renesas/r9a09g057.dtsi    | 123 +++++++++++-------
 .../dts/renesas/r9a09g057h44-rzv2h-evk.dts    |  16 +++
 drivers/soc/renesas/r9a09g057-sys.c           |   9 ++
 drivers/soc/renesas/rz-sysc.c                 | 118 +++++++++++++++++
 drivers/soc/renesas/rz-sysc.h                 |  22 ++++
 5 files changed, 240 insertions(+), 48 deletions(-)

-- 
2.55.0


^ permalink raw reply	[flat|nested] 5+ messages in thread

* [RFC PATCH 1/3] soc: renesas: rz-sysc: Configure AOF registers from dma-ranges
  2026-09-09 19:36 [RFC PATCH 0/3] soc: renesas: Add address extension support for RZ/V2H Prabhakar
@ 2026-09-09 19:36 ` Prabhakar
  2026-09-09 19:51   ` sashiko-bot
  2026-09-09 19:36 ` [RFC PATCH 2/3] arm64: dts: renesas: r9a09g057: Move SDHI nodes into bus nodes Prabhakar
  2026-09-09 19:36 ` [RFC PATCH 3/3] arm64: dts: renesas: r9a09g057h44-rzv2h-evk: Move SDHI1 dma range from 8-12 GiB Prabhakar
  2 siblings, 1 reply; 5+ messages in thread
From: Prabhakar @ 2026-09-09 19:36 UTC (permalink / raw)
  To: Geert Uytterhoeven, Magnus Damm, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley
  Cc: linux-renesas-soc, devicetree, linux-kernel, dmaengine,
	linux-arm-kernel, Prabhakar, Biju Das, Fabrizio Castro,
	Lad Prabhakar

From: Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>

Some RZ SoCs contain bus masters that can only generate 32-bit addresses
but need to access memory located above the 4 GiB boundary. The System
Controller (SYSC) provides Address Offset Function (AOF) registers that
translate the upper address bits on a per-master basis to extend the
accessible physical address range.

Each such master only ever places bits [29:0] of its own address
directly onto the interconnect. Bits [31:30] of that same 32-bit
address are not used as address bits at all -- instead they select
one of four 6-bit fields (Reg0-Reg3) in the master's SYS_AOFn
register. The selected field supplies the corresponding bits [34:30]
or [35:30] of the actual bus transaction, effectively splitting the
master's native 4 GiB address space into four independently
relocatable 1 GiB windows and letting each be redirected anywhere in
a 35-bit (masters limited to DDR0/DDR1 access) or 36-bit (masters
that can also reach PCIe0/PCIe1) physical address space.

Add generic infrastructure to configure the AOF registers from the
firmware-provided dma-ranges property instead of relying on hardcoded
register programming. Each supported bus master is described by its MMIO
base address, corresponding SYS_AOF register index, and address width.
At probe time, the driver matches the bus master against this
description -- via the addressable child node of a devicetree
"simple-bus" wrapper carrying dma-ranges -- and programs the appropriate
AOF register by decomposing each dma-ranges entry into the 1 GiB-aligned
segments it maps to, translating the child (device-side) address into
the field it selects and the parent (physical) address into the value
written to that field.

Validate the dma-ranges entries before programming the hardware by
checking 1 GiB alignment on both the child and parent addresses,
ensuring the described child range does not exceed the master's own
4 GiB address space (i.e. does not require more than the four available
1 GiB translation windows), and verifying that the requested physical
target is representable within the bus master's address width.

Populate the AOF description table for the RZ/V2H SDHI controllers,
which use SYS_AOF16-18.

Signed-off-by: Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>
---
 drivers/soc/renesas/r9a09g057-sys.c |   9 +++
 drivers/soc/renesas/rz-sysc.c       | 118 ++++++++++++++++++++++++++++
 drivers/soc/renesas/rz-sysc.h       |  22 ++++++
 3 files changed, 149 insertions(+)

diff --git a/drivers/soc/renesas/r9a09g057-sys.c b/drivers/soc/renesas/r9a09g057-sys.c
index 308492c31acb..960fcba668b3 100644
--- a/drivers/soc/renesas/r9a09g057-sys.c
+++ b/drivers/soc/renesas/r9a09g057-sys.c
@@ -82,6 +82,12 @@ static void rzv2h_sys_print_id(struct device *dev,
 		dev_warn(dev, "CA55 PLL is not set to 1.7GHz\n");
 }
 
+static const struct rz_aof_unit rzv2h_aof_units[] = {
+	{ .base = 0x15c00000, .index = 16, .addr_bits = 35 }, /* SDHI0 -> SYS_AOF16 */
+	{ .base = 0x15c10000, .index = 17, .addr_bits = 35 }, /* SDHI1 -> SYS_AOF17 */
+	{ .base = 0x15c20000, .index = 18, .addr_bits = 35 }, /* SDHI2 -> SYS_AOF18 */
+};
+
 static const struct rz_sysc_soc_id_init_data rzv2h_sys_soc_id_init_data __initconst = {
 	.family = "RZ/V2H",
 	.id = 0x847a447,
@@ -89,6 +95,9 @@ static const struct rz_sysc_soc_id_init_data rzv2h_sys_soc_id_init_data __initco
 	.revision_mask = GENMASK(31, 28),
 	.specific_id_mask = GENMASK(27, 0),
 	.print_id = rzv2h_sys_print_id,
+	.aof_units = rzv2h_aof_units,
+	.num_aof_units = ARRAY_SIZE(rzv2h_aof_units),
+	.aof_base = 0x0500,
 };
 
 static bool rzv2h_regmap_readable_writeable_reg(unsigned int reg)
diff --git a/drivers/soc/renesas/rz-sysc.c b/drivers/soc/renesas/rz-sysc.c
index 0e92c415d4be..2dae4a0de99b 100644
--- a/drivers/soc/renesas/rz-sysc.c
+++ b/drivers/soc/renesas/rz-sysc.c
@@ -11,6 +11,7 @@
 #include <linux/io.h>
 #include <linux/mfd/syscon.h>
 #include <linux/of.h>
+#include <linux/of_address.h>
 #include <linux/platform_device.h>
 #include <linux/regmap.h>
 #include <linux/slab.h>
@@ -28,6 +29,121 @@ struct rz_sysc {
 	struct device *dev;
 };
 
+static const struct rz_aof_unit *
+rz_aof_lookup(const struct rz_sysc_soc_id_init_data *soc_data, u32 base)
+{
+	unsigned int i;
+
+	for (i = 0; i < soc_data->num_aof_units; i++)
+		if (soc_data->aof_units[i].base == base)
+			return &soc_data->aof_units[i];
+
+	return NULL;
+}
+
+static int rz_aof_parse_bus(struct rz_sysc *sysc,
+			    const struct rz_sysc_soc_id_init_data *soc_data,
+			    struct device_node *bus_np)
+{
+	const struct rz_aof_unit *unit;
+	struct device_node *child;
+	struct resource res;
+	const __be32 *ranges;
+	int naddr, nsize, cell_sz, len, nentries, i;
+	u32 reg_val = 0;
+
+	child = NULL;
+	for_each_child_of_node(bus_np, child) {
+		if (of_address_to_resource(child, 0, &res) == 0)
+			break;
+	}
+	if (!child) {
+		dev_warn(sysc->dev, "%pOF: no addressable child found\n", bus_np);
+		return -EINVAL;
+	}
+
+	if (!of_device_is_available(child)) {
+		dev_dbg(sysc->dev, "%pOF: %pOF is disabled, skipping AOF setup\n",
+			bus_np, child);
+		of_node_put(child);
+		return 0;
+	}
+
+	unit = rz_aof_lookup(soc_data, (u32)res.start);
+	of_node_put(child);
+	if (!unit) {
+		dev_warn(sysc->dev, "%pOF: no AOF unit for base %pa\n", bus_np, &res.start);
+		return -ENODEV;
+	}
+
+	naddr = of_n_addr_cells(bus_np);
+	nsize = of_n_size_cells(bus_np);
+	cell_sz = (naddr * 2 + nsize) * sizeof(u32);
+
+	ranges = of_get_property(bus_np, "dma-ranges", &len);
+	if (!ranges || !cell_sz || len % cell_sz) {
+		dev_err(sysc->dev, "%pOF: malformed dma-ranges\n", bus_np);
+		return -EINVAL;
+	}
+	nentries = len / cell_sz;
+
+	for (i = 0; i < nentries; i++) {
+		const __be32 *p = ranges + i * (naddr * 2 + nsize);
+		u64 child_addr = of_read_number(p, naddr);
+		u64 parent_addr = of_read_number(p + naddr, naddr);
+		u64 size = of_read_number(p + 2 * naddr, nsize);
+		unsigned int nseg = DIV_ROUND_UP_ULL(size, SZ_1G);
+		unsigned int sel0 = (child_addr >> 30) & 0x3;
+		u64 tgt0 = parent_addr >> 30;
+		unsigned int s;
+
+		if (child_addr & (SZ_1G - 1) || parent_addr & (SZ_1G - 1)) {
+			dev_err(sysc->dev, "%pOF: entry %d not 1GiB aligned\n", bus_np, i);
+			return -EINVAL;
+		}
+
+		for (s = 0; s < nseg; s++) {
+			unsigned int sel = sel0 + s;
+			u64 tgt = tgt0 + s;
+
+			if (sel > 3) {
+				dev_err(sysc->dev, "%pOF: entry %d overflows the 4 fields\n",
+					bus_np, i);
+				return -EINVAL;
+			}
+			if (unit->addr_bits == 35 && (tgt & BIT(5))) {
+				dev_err(sysc->dev,
+					"%pOF: target 0x%llx needs bit[5], unit is 35-bit only\n",
+					bus_np, tgt << 30);
+				return -EINVAL;
+			}
+			reg_val &= ~(0x3fu << (sel * 8));
+			reg_val |= (tgt & 0x3f) << (sel * 8);
+		}
+	}
+
+	writel(reg_val, sysc->base + SYS_AOF_OFFSET(soc_data->aof_base, unit->index));
+
+	return 0;
+}
+
+static void rz_sysc_setup_aof(struct rz_sysc *sysc,
+			      const struct rz_sysc_soc_id_init_data *soc_data)
+{
+	struct device_node *np;
+
+	if (!soc_data->aof_units || !soc_data->num_aof_units)
+		return;
+
+	for_each_node_with_property(np, "dma-ranges") {
+		if (!of_device_is_compatible(np, "simple-bus"))
+			continue;
+
+		if (rz_aof_parse_bus(sysc, soc_data, np))
+			dev_warn(sysc->dev, "AOF setup failed for %pOF\n", np);
+	}
+}
+
 static int rz_sysc_soc_init(struct rz_sysc *sysc, const struct of_device_id *match)
 {
 	const struct rz_sysc_init_data *sysc_data = match->data;
@@ -137,6 +253,8 @@ static int rz_sysc_probe(struct platform_device *pdev)
 	if (ret)
 		return ret;
 
+	rz_sysc_setup_aof(sysc, data->soc_id_init_data);
+
 	regmap_cfg->name = "rz_sysc_regs";
 	regmap_cfg->reg_bits = 32;
 	regmap_cfg->reg_stride = 4;
diff --git a/drivers/soc/renesas/rz-sysc.h b/drivers/soc/renesas/rz-sysc.h
index e55f3258d703..481d5d5a0577 100644
--- a/drivers/soc/renesas/rz-sysc.h
+++ b/drivers/soc/renesas/rz-sysc.h
@@ -12,6 +12,18 @@
 #include <linux/sys_soc.h>
 #include <linux/types.h>
 
+/**
+ * struct rz_aof_unit - RZ AOF unit description
+ * @base: MMIO base of the master IP (low 32 bits; all < 4G)
+ * @index: index of SYS_AOFn (0-based)
+ * @addr_bits: address width of the master IP (35 or 36 bits)
+ */
+struct rz_aof_unit {
+	u32 base;
+	u8 index;
+	u8 addr_bits;
+};
+
 /**
  * struct rz_syc_soc_id_init_data - RZ SYSC SoC identification initialization data
  * @family: RZ SoC family
@@ -21,6 +33,10 @@
  * @specific_id_mask: SYSC SoC ID specific ID mask
  * @print_id: print SoC-specific extended device identification
  * @pwrrdy_pwrseq: has pwrrdy register controlled through power sequencer
+ * @aof_units: array of AOF units for this SoC, or NULL if it has none
+ * @num_aof_units: number of entries in @aof_units
+ * @aof_base: offset of SYS_AOF0 from the SYSC base; ignored if @aof_units is NULL
+ *            (this offset is SoC-specific, unlike the fixed inter-register stride)
  */
 struct rz_sysc_soc_id_init_data {
 	const char * const family;
@@ -31,8 +47,14 @@ struct rz_sysc_soc_id_init_data {
 	void (*print_id)(struct device *dev, void __iomem *sysc_base,
 			 struct soc_device_attribute *soc_dev_attr);
 	bool pwrrdy_pwrseq;
+	const struct rz_aof_unit *aof_units;
+	unsigned int num_aof_units;
+	u32 aof_base;
 };
 
+#define SYS_AOF_STRIDE			0x0004
+#define SYS_AOF_OFFSET(aof_base, n)	((aof_base) + ((n) * SYS_AOF_STRIDE))
+
 /**
  * struct rz_sysc_init_data - RZ SYSC initialization data
  * @soc_id_init_data: RZ SYSC SoC ID initialization data
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [RFC PATCH 2/3] arm64: dts: renesas: r9a09g057: Move SDHI nodes into bus nodes
  2026-09-09 19:36 [RFC PATCH 0/3] soc: renesas: Add address extension support for RZ/V2H Prabhakar
  2026-09-09 19:36 ` [RFC PATCH 1/3] soc: renesas: rz-sysc: Configure AOF registers from dma-ranges Prabhakar
@ 2026-09-09 19:36 ` Prabhakar
  2026-09-09 19:36 ` [RFC PATCH 3/3] arm64: dts: renesas: r9a09g057h44-rzv2h-evk: Move SDHI1 dma range from 8-12 GiB Prabhakar
  2 siblings, 0 replies; 5+ messages in thread
From: Prabhakar @ 2026-09-09 19:36 UTC (permalink / raw)
  To: Geert Uytterhoeven, Magnus Damm, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley
  Cc: linux-renesas-soc, devicetree, linux-kernel, dmaengine,
	linux-arm-kernel, Prabhakar, Biju Das, Fabrizio Castro,
	Lad Prabhakar

From: Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>

Wrap sdhi0, sdhi1, and sdhi2 in dedicated "simple-bus" wrapper nodes
(aof_sdhi0_bus, aof_sdhi1_bus, aof_sdhi2_bus), each carrying a dma-ranges
property.

Add dma-ranges to the enclosing bus node instead of the SDHI node itself,
since of_dma_configure() applies a dma-ranges property found on a device's
parent bus node, not one placed directly on the device. Wrapping each SDHI
instance in its own simple-bus node lets each one carry an independent
dma-ranges without affecting its siblings.

The same dma-ranges also let the rz-sysc driver derive the SYS_AOFn address
extension register programming for each SDHI instance, identified by its
MMIO base, per the SoC's address space extension mechanism.

Set each wrapper's dma-ranges to:

    dma-ranges = <0x0 0x40000000  0x0 0x40000000  0x0 0xc0000000>;

Leave the corresponding SDHI's low 1 GiB device-side segment
(0x0 - 0x3fffffff, SRAM/peripheral/xSPI/PCIe space) at its
identity-mapped reset default, and remap its remaining 3 GiB device-side
space (0x40000000-0xffffffff) 1:1 onto the same physical range. DDR on
this SoC spans up to 16 GiB starting at 0x40000000 (DDR0 and DDR1
combined), so this 3 GiB range stays entirely within DDR0's native base
and changes nothing for any of the three SDHI instances.

Allow boards that need a different target window (e.g. DDR1, or a
dedicated reserved-memory pool) to override dma-ranges per instance, as
done for SDHI1 on the RZ/V2H EVK in a subsequent commit.

Signed-off-by: Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>
---
 arch/arm64/boot/dts/renesas/r9a09g057.dtsi | 123 +++++++++++++--------
 1 file changed, 75 insertions(+), 48 deletions(-)

diff --git a/arch/arm64/boot/dts/renesas/r9a09g057.dtsi b/arch/arm64/boot/dts/renesas/r9a09g057.dtsi
index bbaab75681c2..34639106d2cc 100644
--- a/arch/arm64/boot/dts/renesas/r9a09g057.dtsi
+++ b/arch/arm64/boot/dts/renesas/r9a09g057.dtsi
@@ -1577,63 +1577,90 @@ usb3_phy1: usb-phy@15880000 {
 			status = "disabled";
 		};
 
-		sdhi0: mmc@15c00000 {
-			compatible = "renesas,sdhi-r9a09g057";
-			reg = <0x0 0x15c00000 0 0x10000>;
-			interrupts = <GIC_SPI 735 IRQ_TYPE_LEVEL_HIGH>,
-				     <GIC_SPI 736 IRQ_TYPE_LEVEL_HIGH>;
-			clocks = <&cpg CPG_MOD 0xa3>, <&cpg CPG_MOD 0xa5>,
-				 <&cpg CPG_MOD 0xa4>, <&cpg CPG_MOD 0xa6>;
-			clock-names = "core", "clkh", "cd", "aclk";
-			resets = <&cpg 0xa7>;
-			power-domains = <&cpg>;
-			status = "disabled";
-
-			sdhi0_vqmmc: vqmmc-regulator {
-				regulator-name = "SDHI0-VQMMC";
-				regulator-min-microvolt = <1800000>;
-				regulator-max-microvolt = <3300000>;
+		aof_sdhi0_bus: sdhi0-bus {
+			compatible = "simple-bus";
+			#address-cells = <2>;
+			#size-cells = <2>;
+			ranges;
+
+			dma-ranges = <0x0 0x40000000 0x0 0x40000000 0x0 0xc0000000>;
+
+			sdhi0: mmc@15c00000 {
+				compatible = "renesas,sdhi-r9a09g057";
+				reg = <0x0 0x15c00000 0 0x10000>;
+				interrupts = <GIC_SPI 735 IRQ_TYPE_LEVEL_HIGH>,
+					<GIC_SPI 736 IRQ_TYPE_LEVEL_HIGH>;
+				clocks = <&cpg CPG_MOD 0xa3>, <&cpg CPG_MOD 0xa5>,
+					<&cpg CPG_MOD 0xa4>, <&cpg CPG_MOD 0xa6>;
+				clock-names = "core", "clkh", "cd", "aclk";
+				resets = <&cpg 0xa7>;
+				power-domains = <&cpg>;
 				status = "disabled";
+
+				sdhi0_vqmmc: vqmmc-regulator {
+					regulator-name = "SDHI0-VQMMC";
+					regulator-min-microvolt = <1800000>;
+					regulator-max-microvolt = <3300000>;
+					status = "disabled";
+				};
 			};
 		};
 
-		sdhi1: mmc@15c10000 {
-			compatible = "renesas,sdhi-r9a09g057";
-			reg = <0x0 0x15c10000 0 0x10000>;
-			interrupts = <GIC_SPI 737 IRQ_TYPE_LEVEL_HIGH>,
-				     <GIC_SPI 738 IRQ_TYPE_LEVEL_HIGH>;
-			clocks = <&cpg CPG_MOD 0xa7>, <&cpg CPG_MOD 0xa9>,
-				 <&cpg CPG_MOD 0xa8>, <&cpg CPG_MOD 0xaa>;
-			clock-names = "core", "clkh", "cd", "aclk";
-			resets = <&cpg 0xa8>;
-			power-domains = <&cpg>;
-			status = "disabled";
-
-			sdhi1_vqmmc: vqmmc-regulator {
-				regulator-name = "SDHI1-VQMMC";
-				regulator-min-microvolt = <1800000>;
-				regulator-max-microvolt = <3300000>;
+		aof_sdhi1_bus: sdhi1-bus {
+			compatible = "simple-bus";
+			#address-cells = <2>;
+			#size-cells = <2>;
+			ranges;
+
+			dma-ranges = <0x0 0x40000000 0x0 0x40000000 0x0 0xc0000000>;
+
+			sdhi1: mmc@15c10000 {
+				compatible = "renesas,sdhi-r9a09g057";
+				reg = <0x0 0x15c10000 0 0x10000>;
+				interrupts = <GIC_SPI 737 IRQ_TYPE_LEVEL_HIGH>,
+					<GIC_SPI 738 IRQ_TYPE_LEVEL_HIGH>;
+				clocks = <&cpg CPG_MOD 0xa7>, <&cpg CPG_MOD 0xa9>,
+					<&cpg CPG_MOD 0xa8>, <&cpg CPG_MOD 0xaa>;
+				clock-names = "core", "clkh", "cd", "aclk";
+				resets = <&cpg 0xa8>;
+				power-domains = <&cpg>;
 				status = "disabled";
+
+				sdhi1_vqmmc: vqmmc-regulator {
+					regulator-name = "SDHI1-VQMMC";
+					regulator-min-microvolt = <1800000>;
+					regulator-max-microvolt = <3300000>;
+					status = "disabled";
+				};
 			};
 		};
 
-		sdhi2: mmc@15c20000 {
-			compatible = "renesas,sdhi-r9a09g057";
-			reg = <0x0 0x15c20000 0 0x10000>;
-			interrupts = <GIC_SPI 739 IRQ_TYPE_LEVEL_HIGH>,
-				     <GIC_SPI 740 IRQ_TYPE_LEVEL_HIGH>;
-			clocks = <&cpg CPG_MOD 0xab>, <&cpg CPG_MOD 0xad>,
-				 <&cpg CPG_MOD 0xac>, <&cpg CPG_MOD 0xae>;
-			clock-names = "core", "clkh", "cd", "aclk";
-			resets = <&cpg 0xa9>;
-			power-domains = <&cpg>;
-			status = "disabled";
-
-			sdhi2_vqmmc: vqmmc-regulator {
-				regulator-name = "SDHI2-VQMMC";
-				regulator-min-microvolt = <1800000>;
-				regulator-max-microvolt = <3300000>;
+		aof_sdhi2_bus: sdhi2-bus {
+			compatible = "simple-bus";
+			#address-cells = <2>;
+			#size-cells = <2>;
+			ranges;
+
+			dma-ranges = <0x0 0x40000000 0x0 0x40000000 0x0 0xc0000000>;
+
+			sdhi2: mmc@15c20000 {
+				compatible = "renesas,sdhi-r9a09g057";
+				reg = <0x0 0x15c20000 0 0x10000>;
+				interrupts = <GIC_SPI 739 IRQ_TYPE_LEVEL_HIGH>,
+					<GIC_SPI 740 IRQ_TYPE_LEVEL_HIGH>;
+				clocks = <&cpg CPG_MOD 0xab>, <&cpg CPG_MOD 0xad>,
+					<&cpg CPG_MOD 0xac>, <&cpg CPG_MOD 0xae>;
+				clock-names = "core", "clkh", "cd", "aclk";
+				resets = <&cpg 0xa9>;
+				power-domains = <&cpg>;
 				status = "disabled";
+
+				sdhi2_vqmmc: vqmmc-regulator {
+					regulator-name = "SDHI2-VQMMC";
+					regulator-min-microvolt = <1800000>;
+					regulator-max-microvolt = <3300000>;
+					status = "disabled";
+				};
 			};
 		};
 
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [RFC PATCH 3/3] arm64: dts: renesas: r9a09g057h44-rzv2h-evk: Move SDHI1 dma range from 8-12 GiB
  2026-09-09 19:36 [RFC PATCH 0/3] soc: renesas: Add address extension support for RZ/V2H Prabhakar
  2026-09-09 19:36 ` [RFC PATCH 1/3] soc: renesas: rz-sysc: Configure AOF registers from dma-ranges Prabhakar
  2026-09-09 19:36 ` [RFC PATCH 2/3] arm64: dts: renesas: r9a09g057: Move SDHI nodes into bus nodes Prabhakar
@ 2026-09-09 19:36 ` Prabhakar
  2 siblings, 0 replies; 5+ messages in thread
From: Prabhakar @ 2026-09-09 19:36 UTC (permalink / raw)
  To: Geert Uytterhoeven, Magnus Damm, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley
  Cc: linux-renesas-soc, devicetree, linux-kernel, dmaengine,
	linux-arm-kernel, Prabhakar, Biju Das, Fabrizio Castro,
	Lad Prabhakar

From: Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>

Relocate SDHI1's AOF-remapped DMA window to physical 8-12 GiB
(0x2_0000_0000-0x2_ffff_ffff), by updating the dma-ranges entry on
aof_sdhi1_bus so that SDHI1's full 4 GiB device-side address space
(child base 0x0, size 4 GiB) is linearly remapped starting at parent
base 0x2_0000_0000.

Since SDHI1's DMA-visible addresses no longer correspond 1:1 to
ordinary system RAM, buffers handed to it must be guaranteed to
physically reside within this window. Add a restricted-dma-pool
reserved-memory region covering the same 8-12 GiB range and attach
it to sdhi1 via memory-region, so the kernel bounces SDHI1's DMA
buffers through this pool instead of allowing dma_alloc_coherent()/
dma_map_sg() to hand back arbitrary physical pages outside the range
the programmed SYS_AOF17 fields can actually reach.

Signed-off-by: Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>
---
 .../boot/dts/renesas/r9a09g057h44-rzv2h-evk.dts  | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)

diff --git a/arch/arm64/boot/dts/renesas/r9a09g057h44-rzv2h-evk.dts b/arch/arm64/boot/dts/renesas/r9a09g057h44-rzv2h-evk.dts
index 0926ab891fa5..6845d576af42 100644
--- a/arch/arm64/boot/dts/renesas/r9a09g057h44-rzv2h-evk.dts
+++ b/arch/arm64/boot/dts/renesas/r9a09g057h44-rzv2h-evk.dts
@@ -99,6 +99,17 @@ reg_3p3v: regulator-3p3v {
 		regulator-always-on;
 	};
 
+	reserved-memory {
+		#address-cells = <2>;
+		#size-cells = <2>;
+		ranges;
+
+		sdhi1_aof_pool: sdhi1-aof-pool@200000000 {
+			compatible = "restricted-dma-pool";
+			reg = <0x2 0x00000000 0x0 0x08000000>;
+		};
+	};
+
 	vqmmc_sdhi1: regulator-vccq-sdhi1 {
 		compatible = "regulator-gpio";
 		regulator-name = "SDHI1 VccQ";
@@ -130,6 +141,10 @@ y1: y1-clock {
 	};
 };
 
+&aof_sdhi1_bus {
+	dma-ranges = <0x0 0x00000000 0x2 0x00000000  0x1 0x00000000>;
+};
+
 &audio_extal_clk {
 	clock-frequency = <22579200>;
 };
@@ -542,6 +557,7 @@ &scif {
 };
 
 &sdhi1 {
+	memory-region = <&sdhi1_aof_pool>;
 	pinctrl-0 = <&sdhi1_pins>;
 	pinctrl-1 = <&sdhi1_pins>;
 	pinctrl-names = "default", "state_uhs";
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* Re: [RFC PATCH 1/3] soc: renesas: rz-sysc: Configure AOF registers from dma-ranges
  2026-09-09 19:36 ` [RFC PATCH 1/3] soc: renesas: rz-sysc: Configure AOF registers from dma-ranges Prabhakar
@ 2026-09-09 19:51   ` sashiko-bot
  0 siblings, 0 replies; 5+ messages in thread
From: sashiko-bot @ 2026-09-09 19:51 UTC (permalink / raw)
  To: Prabhakar
  Cc: conor+dt, robh, vkoul, dmaengine, wsa+renesas, devicetree,
	Frank.Li

Thank you for your contribution! Sashiko AI review found 7 potential issue(s) to consider:

New issues:
- [High] The driver incorrectly uses the parent's `#address-cells` and `#size-cells` to parse all fields of the `dma-ranges` property, which fails if the child and parent buses have different cell sizes.
- [High] An integer overflow in the `cell_sz` calculation allows a maliciously crafted Device Tree to bypass property length checks, resulting in a massive out-of-bounds read.
- [Medium] `rz_sysc_setup_aof()` globally traverses all `simple-bus` nodes with a `dma-ranges` property in the system, emitting warnings for unrelated buses.
- [Medium] The target physical address validation silently truncates addresses that exceed the maximum addressable width instead of returning an error.
- [Medium] An empty `dma-ranges` property incorrectly programs all hardware windows to physical address 0 instead of establishing a 1:1 mapping.
- [Medium] The check to ensure the described child range does not exceed the master's 4 GiB address space is completely bypassed because bits above 4 GiB are masked out before validation.

Pre-existing issues:
- [High] Missing `soc_device_unregister()` for the SoC device created in `rz_sysc_soc_init()`. If `rz_sysc_probe()` fails, devres cleans up `soc_dev_attr` leading to a Use-After-Free when sysfs is accessed, and the `soc_device` is permanently leaked.
--

commit dfb512e475d4fea533f55a96af47e3ef4b23f122
Author: Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>

soc: renesas: rz-sysc: Configure AOF registers from dma-ranges

This patch adds generic infrastructure to configure the Address Offset Function
(AOF) registers from the firmware-provided dma-ranges property. It sets up
memory windows for masters that generate 32-bit addresses but need to access
memory above the 4 GiB boundary. The appropriate SYS_AOF registers are
programmed by parsing and validating the dma-ranges.

> diff --git a/drivers/soc/renesas/rz-sysc.c b/drivers/soc/renesas/rz-sysc.c
> index 0e92c415d4bef..2dae4a0de99bc 100644
> --- a/drivers/soc/renesas/rz-sysc.c
> +++ b/drivers/soc/renesas/rz-sysc.c
[ ... ]
> @@ -28,6 +29,121 @@ struct rz_sysc {
>  	struct device *dev;
>  };
>  
[ ... ]
> +static int rz_aof_parse_bus(struct rz_sysc *sysc,
> +			    const struct rz_sysc_soc_id_init_data *soc_data,
> +			    struct device_node *bus_np)
> +{
[ ... ]
> +	naddr = of_n_addr_cells(bus_np);
> +	nsize = of_n_size_cells(bus_np);
> +	cell_sz = (naddr * 2 + nsize) * sizeof(u32);

[Severity: High]
Does this code fetch the wrong cell sizes? The standard dma-ranges format
requires the child address to be parsed using the child's address-cells,
which would be of_bus_n_addr_cells(bus_np). of_n_addr_cells(bus_np) and
of_n_size_cells(bus_np) fetch the parent's cell sizes, which corrupts the
subsequent property extraction if the child and parent have different sizes.

Also, is there an integer overflow risk here? 

of_n_addr_cells() can return an unvalidated 32-bit integer directly from
the device tree. The cell_sz calculation uses a signed 32-bit integer for
naddr, and the result expands to a 64-bit unsigned size_t but is truncated
when assigned to the int cell_sz. 

Could this truncation bypass property length checks later?

> +
> +	ranges = of_get_property(bus_np, "dma-ranges", &len);
> +	if (!ranges || !cell_sz || len % cell_sz) {
> +		dev_err(sysc->dev, "%pOF: malformed dma-ranges\n", bus_np);
> +		return -EINVAL;
> +	}

[Severity: Medium]
Will this handle an empty dma-ranges property correctly? 

An empty property is valid and indicates a 1:1 mapping, returning len = 0.
The validation len % cell_sz succeeds for 0, so the loop is skipped and
reg_val remains 0. Writing 0 configures all windows to point to physical
address 0, which breaks the 1:1 mapping.

> +	nentries = len / cell_sz;
> +
> +	for (i = 0; i < nentries; i++) {
> +		const __be32 *p = ranges + i * (naddr * 2 + nsize);
> +		u64 child_addr = of_read_number(p, naddr);
> +		u64 parent_addr = of_read_number(p + naddr, naddr);

[Severity: High]
Is it intentional to use the parent's naddr cell count to extract both the
child and parent addresses? 

Additionally, if a massive unvalidated naddr bypassed the length check earlier
due to overflow, won't of_read_number(p, naddr) cause a massive out-of-bounds
read and kernel panic?

> +		u64 size = of_read_number(p + 2 * naddr, nsize);
> +		unsigned int nseg = DIV_ROUND_UP_ULL(size, SZ_1G);
> +		unsigned int sel0 = (child_addr >> 30) & 0x3;

[Severity: Medium]
Does this mask out bits above 4 GiB? 

By using & 0x3, the overflow bits are discarded, so sel0 wraps around. This
completely bypasses the overflow check (if (sel > 3)) later in the loop,
causing invalid configurations to be silently applied to the wrong windows.

> +		u64 tgt0 = parent_addr >> 30;
> +		unsigned int s;
> +
> +		if (child_addr & (SZ_1G - 1) || parent_addr & (SZ_1G - 1)) {
> +			dev_err(sysc->dev, "%pOF: entry %d not 1GiB aligned\n", bus_np, i);
> +			return -EINVAL;
> +		}
> +
> +		for (s = 0; s < nseg; s++) {
> +			unsigned int sel = sel0 + s;
> +			u64 tgt = tgt0 + s;
> +
> +			if (sel > 3) {
> +				dev_err(sysc->dev, "%pOF: entry %d overflows the 4 fields\n",
> +					bus_np, i);
> +				return -EINVAL;
> +			}
> +			if (unit->addr_bits == 35 && (tgt & BIT(5))) {
> +				dev_err(sysc->dev,
> +					"%pOF: target 0x%llx needs bit[5], unit is 35-bit only\n",
> +					bus_np, tgt << 30);
> +				return -EINVAL;
> +			}
> +			reg_val &= ~(0x3fu << (sel * 8));
> +			reg_val |= (tgt & 0x3f) << (sel * 8);

[Severity: Medium]
Does this silently truncate target addresses >= 64 GiB? 

If parent_addr is >= 64 GiB (tgt >= 64), the 6th bit is set, meaning
tgt & BIT(5) is 0, bypassing the validation check entirely. The target is then
truncated to 6 bits by (tgt & 0x3f) << (sel * 8) instead of returning an error.

> +		}
> +	}
> +
> +	writel(reg_val, sysc->base + SYS_AOF_OFFSET(soc_data->aof_base, unit->index));
> +
> +	return 0;
> +}
> +
> +static void rz_sysc_setup_aof(struct rz_sysc *sysc,
> +			      const struct rz_sysc_soc_id_init_data *soc_data)
> +{
> +	struct device_node *np;
> +
> +	if (!soc_data->aof_units || !soc_data->num_aof_units)
> +		return;
> +
> +	for_each_node_with_property(np, "dma-ranges") {
> +		if (!of_device_is_compatible(np, "simple-bus"))
> +			continue;
> +
> +		if (rz_aof_parse_bus(sysc, soc_data, np))
> +			dev_warn(sysc->dev, "AOF setup failed for %pOF\n", np);
> +	}
> +}

[Severity: Medium]
Does this loop emit false-positive warnings for unrelated buses? 

Searching globally for any simple-bus with a dma-ranges property will match
valid buses that have nothing to do with AOF, causing rz_aof_lookup() to fail
and spitting out warning logs.

[ ... ]
> @@ -137,6 +253,8 @@ static int rz_sysc_probe(struct platform_device *pdev)
>  	if (ret)
>  		return ret;
>  
> +	rz_sysc_setup_aof(sysc, data->soc_id_init_data);
> +
>  	regmap_cfg->name = "rz_sysc_regs";
>  	regmap_cfg->reg_bits = 32;
>  	regmap_cfg->reg_stride = 4;

[Severity: High]
This is a pre-existing issue, but does rz_sysc_soc_init() leak the SoC device
and cause a use-after-free?

rz_sysc_soc_init() registers an SoC device using soc_device_register() with a
devres-managed soc_dev_attr structure, and discards the returned pointer.

If rz_sysc_probe() fails later (e.g., in devm_regmap_init_mmio()), devres
cleans up soc_dev_attr, leaving the registered soc_device with a dangling
pointer. Accessing sysfs for this device will then cause a use-after-free, and
the soc_device memory is leaked because soc_device_unregister() is never called.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260909193640.8069-1-prabhakar.mahadev-lad.rj@bp.renesas.com?part=1

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2026-09-09 19:51 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-09 19:36 [RFC PATCH 0/3] soc: renesas: Add address extension support for RZ/V2H Prabhakar
2026-09-09 19:36 ` [RFC PATCH 1/3] soc: renesas: rz-sysc: Configure AOF registers from dma-ranges Prabhakar
2026-09-09 19:51   ` sashiko-bot
2026-09-09 19:36 ` [RFC PATCH 2/3] arm64: dts: renesas: r9a09g057: Move SDHI nodes into bus nodes Prabhakar
2026-09-09 19:36 ` [RFC PATCH 3/3] arm64: dts: renesas: r9a09g057h44-rzv2h-evk: Move SDHI1 dma range from 8-12 GiB Prabhakar

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox