* [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