* [PATCH v4 0/8] R-Car X5H Ironhide CPG/MDLC SCMI remapping support
@ 2026-10-16 15:55 Geert Uytterhoeven
2026-10-16 15:55 ` [PATCH v4 1/8] clk: renesas: r8a78000: Add clk_hw to clk_map Geert Uytterhoeven
` (7 more replies)
0 siblings, 8 replies; 9+ messages in thread
From: Geert Uytterhoeven @ 2026-10-16 15:55 UTC (permalink / raw)
To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Stephen Boyd,
Brian Masney, Jerome Brunet, Ulf Hansson, Philipp Zabel,
Rafael J . Wysocki, Marek Vasut, Bartosz Golaszewski,
Konrad Dybcio, Kevin Hilman, Vinod Koul, Wolfram Sang,
Kuninori Morimoto, Laurent Pinchart, Clément Le Goffic,
Sudeep Holla, Cristian Marussi, arm-scmi, linux-arm-kernel
Cc: devicetree, linux-clk, linux-pm, linux-renesas-soc,
Geert Uytterhoeven
Hi all,
TL;DR:
Describe hardware in DT, and perform mapping to abstractions offered
by SCMI in Linux.
The Renesas R-Car X5H-based Ironhide board is the first Renesas
SoC/board combination that implements the ARM System Control and
Management Interface (SCMI).
This means Linux can no longer perform various system operations (e.g.
clock, power domain, and reset control) by accessing the hardware
directly. Instead, these operations are abstracted according to various
SCMI sub-protocols, and Linux has to send messages to an SCMI-compliant
firmware running on a System Control Processor (SCP).
More specifically, the R-Car X5H SCP FW SCMI controls access to:
1. Core clocks and module clocks,
2. Module resets,
3. Power domains,
4. System power.
The latter is not relevant for this discussion.
Using SCMI also has an impact on the board DTS: besides the addition of
a firmware/scmi node with protocol-specific subnodes, devices using
SCMI-abstracted system resources now have to refer to these resources
using resource specifiers containing phandles to the SCMI protocol
subnodes, and firmware-specific object IDs[1].
Unfortunately we encountered several issues with this:
- The clock, reset, and power domain IDs are not stable by design,
causing DT incompatibilities.
SCMI requires that the ID space exposed to an agent (i.e. Linux)
starts at zero and is contiguous. Hence any change in system
partitioning (Application CPU core vs. Realtime CPU core,
virtualization, ...) may mean a renumbering of all SCMI IDs,
implying a massive change to the DT.
- There may be different SCP firmware lineages, e.g. proprietary and
certified variants, and FLOSS variants,
- SCMI features may differ across firmware versions,
- SCMI may abstract the hardware in a different way, requiring changes
in (possibly existing) DT bindings and drivers to support the same
hardware with and without SCMI:
- E.g. the number of SCMI clocks inputs to a hardware block may
differ from the actual hardware,
- E.g. hardware clock inputs may be abstracted as SCMI power
domains (or vice versa), like on Qualcomm SA8255P[2].
Experience has taught that not describing the hardware in DT may
lead to major issues later.
- SCMI does not support the concept of a clock domain, so existing
drivers can no longer rely on Runtime PM, but need to be changed to
manage functional clocks explicitly,
- U-Boot IPL (Initial Program Loader) needs a proper hardware
description in DT, not using SCMI.
This list deliberately ignores ID instability and the need for SCMI
quirks during development of the SCMI firmware. While upstream Linux
must only support sufficiently mature firmware versions, vendor trees
(e.g. renesas-lts) may still want to support development firmware
versions.
During last OSSJ/LPC, Marek Vasut pondered if we could keep our own ID
lists stable, and perform a mapping to the IDs used by the actual SCMI
implementation running on the system. This was also briefly discussed
at LPC[3]. After some refining of the initial idea, this series takes a
slightly different approach, by:
1. Describing the actual hardware components in DT, plus a minimum
SCMI glue,
2. Mapping DT hardware descriptions to SCMI-backed objects and
abstractions (or replacements, in case they are unusable,
unavailable, or too different), in Linux drivers, based on the
detected firmware version.
This has the following advantages:
1. The DT ABI is stable,
2. DT bindings and drivers for existing hardware blocks do not need to
be modified,
2. Current and future firmware can be handled in driver code,
3. U-Boot IPL can use almost the same DT; it just needs stripping of
the SCMI node and subnodes,
4. If ever needed, the Clock Pulse Generator (CPG) and Module
Controller (MDLC) drivers can be extended to program the hardware
directly if SCMI is not present,
Last week I gave a presentation about the issues and our solution at
ELCE2026 in Prague[4].
This series consists of four parts:
A. Patches 1-2 prepare and add SCMI remapping support to the R-Car X5H
CPG clock driver,
B. Patches 3-6 fix and add new interfaces needed for remapping in the
R-Car X5H MDLC pmdomain driver,
C. Patch 7 adds SCMI remapping support to the R-Car X5H MDLC pmdomain
driver,
D. Patch 8 enables more SCMI protocols on R-Car X5H Ironhide.
Dependencies:
- C depends on B,
- D depends on A, B, C, and out-of-tree SCMI quirks.
Changes compared to v3[5]:
- Remove dependencies by making assumptions and dropping disputed
items, to move this series forward:
- Assume all SCMI IDs are stable.
This let us drop the addition of scmi_get_base_info(), and
support for handling different ID spaces.
- Drop the "renesas,scmi-firmware" DT property, and find a device
node compatible with "arm,scmi" instead.
This let us drop the addition of fw_devlink support for this
property, too, but required a new probe deferral fix in the
pmdomain code: "pmdomain: Make genpd_get_from_provider() return
-EPROBE_DEFER".
This will have to be revisited when systems with multiple SCMI
providers appear.
- Drop unneeded IS_ERR() check after __clk_get_hw(),
- Mark scmi_{clk,reset,power}_np __free(device_node),
- Include initial mapping tables for R-Car X5H SCP FW SDKv4.31.0 and
later, supporting clocks, power domains, and resets used by serial,
I2C, and SPI devices.
- Try SCMI clocks first, and fall back to the default fixed-frequency
clocks when not available, as recent firmware versions no longer
restrict access to some bus clocks,
- Fall back to the non-SCMI defaults when SCMI provider support is
disabled,
- Keep RESET_CONTROL_FLAGS_BIT_OPTIONAL clearing in
__fwnode_reset_control_get(),
- Return directly in __reset_control_get_from_provider(), instead of
assigning to a local variable, letting us drop the latter and
simplify scoped_guard() to plain guard(),
- Add Reviewed-by,
- Check for overflow when converting (u64) fwnode args to (uint32_t)
OF args,
- Document that reset_control_get_from_provider_exclusive() must not
be given a reference to a GPIO provider,
- Let the dummy return ERR_PTR(-ENOTSUPP) instead of NULL,
- Improve patch description,
- Let r8a78000_mdlc_attach_dev() return success when the MDLC instance
is not found, to avoid breaking users referring in DT to an SCMI
power-domain directly,
- Extract scmi_get_clk(),
- Add support for gateable functional clock parents,
- Propagate all reset failures, as SCP FW SDKv4.28.0 which restricted
HSCIF0 reset is no longer supported.
Changes compared to v2[6]:
- New patches:
"[PATCH v3 06/12] reset: Extract __reset_control_get_from_provider()",
"[PATCH v3 08/12] clk: renesas: r8a78000: Add clk_hw to clk_map",
- Move DT binding patches forward in series,
- Add "renesas" vendor prefix and "scmi-" function prefix to the
"firmware" property (I still think the vendor property should be
dropped, or replaced by "arm"),
- Add "of_" prefix to genpd_get_from_provider(),
- Replace reset_controller_get_provider() by
reset_control_get_from_provider_exclusive(),
- Add kerneldoc,
- Rebase,
- Consistently use /* sentinel */ for zero sentinels,
- Call reset_control_*() APIs instead of internal reset_control_ops
methods,
- Rename struct mod_map to struct mod_map_in,
- Add struct mod_map, which also contains a reset_control pointer,
- Add fill_mod_map() to obtain all SCMI reset_control structures
up-front, as one can no longer use lazy look-up,
- Prefix DEFINE_MDLC_RESET_WRAPPER() parameters with underscores.
Changes compared to v1[7]:
- Drop SCMI quirks and SCMI mapping tables,
- Move CPG/MDLC DT bindings and minimal base drivers to a separate
series,
- Add that SCMI may provide different abstractions,
- Add missing asterisk to blank kerneldoc line,
- Let scmi_get_base_info() return -EPROBE_DEFER or -EOPNOTSUPP instead
of -ENOENT, and propagate these errors in callers,
- Fall back to the default support when SCMI support or firmware is
not available, or when the detected SCMI firmware version is not
supported by the driver,
- Improve error message on scmi_get_base_info() failure,
- Check if SCMI firmware node is available,
- Use reverse xmas ordering in r8a78000_cpg_probe(),
- Replace the scmi_*_np members of struct r8a78000_*_priv by local
variables in the probe functions, and pass them to the fill_*_map()
functions,
- Join split short lines,
- Move check for supported firmware version up,
- Fix DT misindentations,
- Add scmi_clk node, as it is no longer added in earlier commits.
Notes:
- In reponse of feedback on v1, I did try to use SCMI IDs in DT, and
handle most issues in Linux SCMI drivers and SCMI quirks[8]. While
the result worked, it did not solve everything in the issues list
above.
- This series does not include actual SCMI quirks; the SCMI firmware
for R-Car X5H is still under development.
For testing, I have pushed this series plus additional work to[9].
Thanks for your comments!
[1] Documentation/devicetree/bindings/firmware/arm,scmi.yaml
[2] "[PATCH net-next v11 1/7] dt-bindings: phy: document the serdes PHY on sa8255p"
https://lore.kernel.org/20260629-qcom-sa8255p-emac-v11-1-1b7fb95b51f9@oss.qualcomm.com
[3] "Firmware ABI stability" at Linux Plumbers Conference 2025
https://lpc.events/event/19/contributions/2153/
[4] "Marrying Device Tree and SCMI" (https://sched.co/2RaWR)
[5] "[PATCH v3 00/12] R-Car X5H Ironhide CPG/MDLC SCMI remapping
support"
https://lore.kernel.org/cover.1788338320.git.geert+renesas@glider.be/
[6] "[PATCH v2 00/10] R-Car X5H Ironhide CPG/MDLC SCMI remapping
support"
https://lore.kernel.org/cover.1783505329.git.geert+renesas@glider.be
[7] "[PATCH/RFC 00/14] R-Car X5H Ironhide SCMI CPG/MDLC remapping"
https://lore.kernel.org/cover.1776793163.git.geert+renesas@glider.be
[8] "[PATCH/RFC 0/9] R-Car X5H Ironhide pure SCMI proof-of-concept"
https://lore.kernel.org/cover.1781171705.git.geert+renesas@glider.be/
[9] topic/rcar-x5h-ironhide-scmi-cpg-mdlc-remapping-v4 branch of
https://git.kernel.org/pub/scm/linux/kernel/git/geert/renesas-drivers.git
Geert Uytterhoeven (8):
clk: renesas: r8a78000: Add clk_hw to clk_map
clk: renesas: r8a78000: Add SCMI remapping support
reset: Extract __reset_control_get_from_provider()
reset: Add reset_control_get_from_provider_exclusive()
pmdomain: Make genpd_get_from_provider() return -EPROBE_DEFER
pmdomain: Make genpd_get_from_provider() public
pmdomain: renesas: r8a78000: Add SCMI remapping support
arm64: dts: renesas: ironhide: Enable SCMI devpd, sys, clock, and
reset
.../boot/dts/renesas/r8a78000-ironhide.dts | 19 +
drivers/clk/renesas/r8a78000-cpg.c | 217 +++++++--
drivers/pmdomain/core.c | 26 +-
drivers/pmdomain/renesas/r8a78000-mdlc.c | 410 +++++++++++++++++-
drivers/reset/core.c | 116 +++--
include/linux/pm_domain.h | 7 +
include/linux/reset.h | 8 +
7 files changed, 698 insertions(+), 105 deletions(-)
--
2.43.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
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH v4 1/8] clk: renesas: r8a78000: Add clk_hw to clk_map
2026-10-16 15:55 [PATCH v4 0/8] R-Car X5H Ironhide CPG/MDLC SCMI remapping support Geert Uytterhoeven
@ 2026-10-16 15:55 ` Geert Uytterhoeven
2026-10-16 15:55 ` [PATCH v4 2/8] clk: renesas: r8a78000: Add SCMI remapping support Geert Uytterhoeven
` (6 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: Geert Uytterhoeven @ 2026-10-16 15:55 UTC (permalink / raw)
To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Stephen Boyd,
Brian Masney, Jerome Brunet, Ulf Hansson, Philipp Zabel,
Rafael J . Wysocki, Marek Vasut, Bartosz Golaszewski,
Konrad Dybcio, Kevin Hilman, Vinod Koul, Wolfram Sang,
Kuninori Morimoto, Laurent Pinchart, Clément Le Goffic,
Sudeep Holla, Cristian Marussi, arm-scmi, linux-arm-kernel
Cc: devicetree, linux-clk, linux-pm, linux-renesas-soc,
Geert Uytterhoeven
Prepare for mapping to other than fixed-rate clocks by adding a clk_hw
pointer to the clk_map structure, and populating it up-front.
Move r8a78000_cpg_priv.fixed_hws into fill_clk_map(), as it is no longer
needed afterwards, and drop NUM_FIXED_CLKS.
Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
---
v4:
- No changes,
v3:
- New.
---
drivers/clk/renesas/r8a78000-cpg.c | 78 +++++++++++++++++++++---------
1 file changed, 56 insertions(+), 22 deletions(-)
diff --git a/drivers/clk/renesas/r8a78000-cpg.c b/drivers/clk/renesas/r8a78000-cpg.c
index b8024e76743ea53d..5ffd7b89cb48e41c 100644
--- a/drivers/clk/renesas/r8a78000-cpg.c
+++ b/drivers/clk/renesas/r8a78000-cpg.c
@@ -16,19 +16,24 @@
#include <dt-bindings/clock/renesas,r8a78000-cpg.h>
+struct clk_map_in {
+ int dt_id; /* DT binding clock ID or -1 sentinel */
+ u32 fw_id; /* FIXED_CLK() ID */
+};
+
struct clk_map {
int dt_id; /* DT binding clock ID or -1 sentinel */
u32 fw_id; /* FIXED_CLK() ID */
+ struct clk_hw *hw;
};
enum fixed_clk {
FIXED_CLK_66M,
FIXED_CLK_133M,
FIXED_CLK_266M,
- NUM_FIXED_CLKS
};
-static const unsigned long fixed_clk_rates[NUM_FIXED_CLKS] = {
+static const unsigned long fixed_clk_rates[] = {
[FIXED_CLK_66M] = 66666000,
[FIXED_CLK_133M] = 133330000,
[FIXED_CLK_266M] = 266660000,
@@ -41,12 +46,10 @@ static const unsigned long fixed_clk_rates[NUM_FIXED_CLKS] = {
*
* @dev: CPG device
* @map: Mapping from DT clock IDs to fixed-rate clocks
- * @fixed_hws: Fixed rate clocks
*/
struct r8a78000_cpg_priv {
struct device *dev;
const struct clk_map *map;
- struct clk_hw *fixed_hws[NUM_FIXED_CLKS];
};
static const struct clk_map *clk_map_find(const struct clk_map *map, u32 id)
@@ -83,8 +86,7 @@ static struct clk_hw *r8a78000_clk_get(struct of_phandle_args *spec,
}
dev_dbg(dev, "Mapping DT clock %u to fixed clock %u\n", id, map->fw_id);
-
- hw = priv->fixed_hws[map->fw_id];
+ hw = map->hw;
dev_dbg(dev, "clock %u is %s at %lu Hz\n", id, clk_hw_get_name(hw),
clk_hw_get_rate(hw));
@@ -92,35 +94,69 @@ static struct clk_hw *r8a78000_clk_get(struct of_phandle_args *spec,
return hw;
}
-static int register_fixed_clks(struct r8a78000_cpg_priv *priv)
+static struct clk_hw **register_fixed_clks(struct device *dev)
{
- struct device *dev = priv->dev;
+ unsigned int n = ARRAY_SIZE(fixed_clk_rates);
+ struct clk_hw **hws, *hw;
unsigned long rate;
- struct clk_hw *hw;
const char *name;
- for (unsigned int i = 0; i < ARRAY_SIZE(fixed_clk_rates); i++) {
+ hws = devm_kcalloc(dev, n, sizeof(*hws), GFP_KERNEL);
+ if (!hws)
+ return ERR_PTR(-ENOMEM);
+
+ for (unsigned int i = 0; i < n; i++) {
rate = fixed_clk_rates[i];
name = devm_kasprintf(dev, GFP_KERNEL, "cpg-%lu", rate);
if (!name)
- return -ENOMEM;
+ return ERR_PTR(-ENOMEM);
hw = devm_clk_hw_register_fixed_rate(dev, name, NULL, 0, rate);
if (IS_ERR(hw))
- return PTR_ERR(hw);
+ return ERR_CAST(hw);
- priv->fixed_hws[i] = hw;
+ hws[i] = hw;
}
- return 0;
+ return hws;
+}
+
+static const struct clk_map *fill_clk_map(struct r8a78000_cpg_priv *priv,
+ const struct clk_map_in *map_in)
+{
+ struct device *dev = priv->dev;
+ struct clk_hw **fixed_hws;
+ struct clk_map *map;
+ unsigned int i;
+
+ fixed_hws = register_fixed_clks(dev);
+ if (IS_ERR(fixed_hws))
+ return ERR_CAST(fixed_hws);
+
+ for (i = 0; map_in[i].dt_id >= 0; i++) { }
+
+ map = devm_kcalloc(dev, i + 1, sizeof(*map), GFP_KERNEL);
+ if (!map)
+ return ERR_PTR(-ENOMEM);
+
+ for (i = 0; ; i++) {
+ map[i].dt_id = map_in[i].dt_id;
+ if (map[i].dt_id < 0)
+ break;
+
+ map[i].fw_id = map_in[i].fw_id;
+ map[i].hw = fixed_hws[map[i].fw_id];
+ }
+
+ devm_kfree(dev, fixed_hws);
+ return map;
}
static int r8a78000_cpg_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct r8a78000_cpg_priv *priv;
- const struct clk_map *map;
- int ret;
+ const struct clk_map_in *map;
map = of_device_get_match_data(dev);
if (!map)
@@ -131,16 +167,14 @@ static int r8a78000_cpg_probe(struct platform_device *pdev)
return -ENOMEM;
priv->dev = dev;
- priv->map = map;
-
- ret = register_fixed_clks(priv);
- if (ret)
- return ret;
+ priv->map = fill_clk_map(priv, map);
+ if (IS_ERR(priv->map))
+ return PTR_ERR(priv->map);
return devm_of_clk_add_hw_provider(dev, r8a78000_clk_get, priv);
}
-static const struct clk_map r8a78000_cpg_default[] = {
+static const struct clk_map_in r8a78000_cpg_default[] = {
{ R8A78000_CPG_SGASYNCD4_PERW_BUS, FIXED_CLK(266M) },
{ R8A78000_CPG_SGASYNCD8_PERW_BUS, FIXED_CLK(133M) },
{ R8A78000_CPG_SGASYNCD16_PERW_BUS, FIXED_CLK(66M) },
--
2.43.0
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH v4 2/8] clk: renesas: r8a78000: Add SCMI remapping support
2026-10-16 15:55 [PATCH v4 0/8] R-Car X5H Ironhide CPG/MDLC SCMI remapping support Geert Uytterhoeven
2026-10-16 15:55 ` [PATCH v4 1/8] clk: renesas: r8a78000: Add clk_hw to clk_map Geert Uytterhoeven
@ 2026-10-16 15:55 ` Geert Uytterhoeven
2026-10-16 15:55 ` [PATCH v4 3/8] reset: Extract __reset_control_get_from_provider() Geert Uytterhoeven
` (5 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: Geert Uytterhoeven @ 2026-10-16 15:55 UTC (permalink / raw)
To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Stephen Boyd,
Brian Masney, Jerome Brunet, Ulf Hansson, Philipp Zabel,
Rafael J . Wysocki, Marek Vasut, Bartosz Golaszewski,
Konrad Dybcio, Kevin Hilman, Vinod Koul, Wolfram Sang,
Kuninori Morimoto, Laurent Pinchart, Clément Le Goffic,
Sudeep Holla, Cristian Marussi, arm-scmi, linux-arm-kernel
Cc: devicetree, linux-clk, linux-pm, linux-renesas-soc,
Geert Uytterhoeven
Extend the Clock Pulse Generator driver for R-Car X5H with SCMI support,
to remap DT clock IDs to abstractions (clocks) offered by the SCMI
firmware when running on a system with SCMI, and SCMI support is
enabled. Take into account some peculiarities of the current SCMI
firmware implementations, and fall-back to fixed-rate clocks as
replacements for SCMI clocks that are not available.
Support for bus clocks used by serial, I2C, and SPI devices is included.
This lets us avoid putting SCMI IDs in DT, and allows us to use the same
hardware description in SoC DTS for systems with and without SCMI
firmware.
For now, it is assumed SCMI clock IDs are stable, and match SCP FW
SDKv4.31.0 and later.
Suggested-by: Marek Vasut <marek.vasut+renesas@mailbox.org>
Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
---
v4:
- Drop unneeded IS_ERR() check after __clk_get_hw(),
- Mark scmi_clk_np __free(device_node),
- Assume all SCMI clock IDs are stable, and drop support for handling
different ID spaces,
- Include initial mapping table for SCP FW SDKv4.31.0 and later,
- Try SCMI clocks first, and fall back to the default fixed-frequency
clocks when not available, as recent firmware versions no longer
restrict access to some bus clocks,
- Drop renesas,scmi-firmware support, just find an arm,scmi node
instead,
- Fall back to the non-SCMI defaults when SCMI clock support is
disabled,
v3:
- Rebase,
- Consistently use /* sentinel */ for zero sentinels,
- s/firmware/renesas,scmi-firmware/,
- Reword,
v2:
- Spin off from "clk: renesas: Add R-Car X5H CPG SCMI remapping
driver",
- Use reverse xmas ordering in r8a78000_cpg_probe(),
- Propagate error codes from scmi_get_base_info() now it can return
-EPROBE_DEFER,
- Improve error message on scmi_get_base_info() failure,
- Check if SCMI firmware node is available,
- Replace the scmi_clk_np member of struct r8a78000_cpg_priv by a
local variable in r8a78000_cpg_probe(), and pass it to
fill_clk_map(),
- Join split short lines,
- Fall back to the default support when SCMI support or firmware is
not available, or when the detected SCMI firmware version is not
supported by the driver,
- Drop mapping tables.
---
drivers/clk/renesas/r8a78000-cpg.c | 149 ++++++++++++++++++++++++++---
1 file changed, 138 insertions(+), 11 deletions(-)
diff --git a/drivers/clk/renesas/r8a78000-cpg.c b/drivers/clk/renesas/r8a78000-cpg.c
index 5ffd7b89cb48e41c..758cfe78ae58e0fb 100644
--- a/drivers/clk/renesas/r8a78000-cpg.c
+++ b/drivers/clk/renesas/r8a78000-cpg.c
@@ -5,6 +5,7 @@
* Copyright (C) 2026 Glider bv
*/
+#include <linux/cleanup.h>
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/dev_printk.h>
@@ -12,21 +13,27 @@
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
+#include <linux/scmi_protocol.h>
#include <linux/slab.h>
#include <dt-bindings/clock/renesas,r8a78000-cpg.h>
struct clk_map_in {
int dt_id; /* DT binding clock ID or -1 sentinel */
- u32 fw_id; /* FIXED_CLK() ID */
+ u32 fw_id; /* SCMI firmware clock ID or FIXED_CLK() ID */
};
struct clk_map {
int dt_id; /* DT binding clock ID or -1 sentinel */
- u32 fw_id; /* FIXED_CLK() ID */
+ u32 fw_id; /* SCMI firmware clock ID or FIXED_CLK() ID */
struct clk_hw *hw;
};
+struct cpg_data {
+ const struct clk_map_in *default_map;
+ const struct clk_map_in *fw_map;
+};
+
enum fixed_clk {
FIXED_CLK_66M,
FIXED_CLK_133M,
@@ -39,13 +46,14 @@ static const unsigned long fixed_clk_rates[] = {
[FIXED_CLK_266M] = 266660000,
};
-#define FIXED_CLK(rate) FIXED_CLK_ ## rate
+#define FIXED_CLK_OFFSET 0x80000000
+#define FIXED_CLK(rate) FIXED_CLK_OFFSET + FIXED_CLK_ ## rate
/**
* struct r8a78000_cpg_priv - Clock Pulse Generator Private Data
*
* @dev: CPG device
- * @map: Mapping from DT clock IDs to fixed-rate clocks
+ * @map: Mapping from DT clock IDs to SCMI or fixed-rate clocks
*/
struct r8a78000_cpg_priv {
struct device *dev;
@@ -85,8 +93,19 @@ static struct clk_hw *r8a78000_clk_get(struct of_phandle_args *spec,
return ERR_PTR(-ENOENT);
}
- dev_dbg(dev, "Mapping DT clock %u to fixed clock %u\n", id, map->fw_id);
+ if (map->fw_id < FIXED_CLK_OFFSET)
+ dev_dbg(dev, "Mapping DT clock %u to SCMI clock %u\n", id,
+ map->fw_id);
+ else
+ dev_dbg(dev, "Mapping DT clock %u to fixed clock %u\n", id,
+ map->fw_id - FIXED_CLK_OFFSET);
+
hw = map->hw;
+ if (!hw) {
+ /* CLOCK_ATTRIBUTES is not supported */
+ dev_err(dev, "Clock %u is not available\n", id);
+ return ERR_PTR(-ENOENT);
+ }
dev_dbg(dev, "clock %u is %s at %lu Hz\n", id, clk_hw_get_name(hw),
clk_hw_get_rate(hw));
@@ -94,6 +113,21 @@ static struct clk_hw *r8a78000_clk_get(struct of_phandle_args *spec,
return hw;
}
+static struct device_node *scmi_find_proto(struct device_node *scmi, u32 proto)
+{
+ for_each_available_child_of_node_scoped(scmi, child) {
+ u32 reg;
+
+ if (of_property_read_u32(child, "reg", ®))
+ continue;
+
+ if (reg == proto)
+ return_ptr(child);
+ }
+
+ return NULL;
+}
+
static struct clk_hw **register_fixed_clks(struct device *dev)
{
unsigned int n = ARRAY_SIZE(fixed_clk_rates);
@@ -122,11 +156,16 @@ static struct clk_hw **register_fixed_clks(struct device *dev)
}
static const struct clk_map *fill_clk_map(struct r8a78000_cpg_priv *priv,
- const struct clk_map_in *map_in)
+ const struct clk_map_in *map_in,
+ const struct clk_map_in *fallback,
+ struct device_node *scmi_clk_np)
{
+ struct of_phandle_args scmi_spec;
struct device *dev = priv->dev;
struct clk_hw **fixed_hws;
struct clk_map *map;
+ struct clk_hw *hw;
+ struct clk *clk;
unsigned int i;
fixed_hws = register_fixed_clks(dev);
@@ -145,7 +184,46 @@ static const struct clk_map *fill_clk_map(struct r8a78000_cpg_priv *priv,
break;
map[i].fw_id = map_in[i].fw_id;
- map[i].hw = fixed_hws[map[i].fw_id];
+ if (map[i].fw_id >= FIXED_CLK_OFFSET) {
+ enum fixed_clk idx = map[i].fw_id - FIXED_CLK_OFFSET;
+
+ map[i].hw = fixed_hws[idx];
+ continue;
+ }
+
+ scmi_spec.np = scmi_clk_np;
+ scmi_spec.args_count = 1;
+ scmi_spec.args[0] = map[i].fw_id;
+
+ clk = of_clk_get_from_provider(&scmi_spec);
+ if (IS_ERR(clk))
+ return dev_err_cast_probe(dev, clk,
+ "Failed to get SCMI clock %u\n", map[i].fw_id);
+
+ hw = __clk_get_hw(clk);
+ if (!hw && fallback != map_in) {
+ for (unsigned int j = 0; fallback[j].dt_id >= 0; j++) {
+ if (fallback[j].dt_id == map_in[i].dt_id) {
+ enum fixed_clk idx = fallback[j].fw_id - FIXED_CLK_OFFSET;
+
+ map[i].hw = fixed_hws[idx];
+ break;
+ }
+ }
+ if (map[i].hw)
+ continue;
+ }
+
+ if (!hw) {
+ /* CLOCK_ATTRIBUTES is not supported */
+ dev_warn(dev, "SCMI clock %u is NULL\n", map[i].fw_id);
+ continue;
+ }
+
+ dev_dbg(priv->dev, "SCMI clock %u is %s at %lu Hz\n",
+ map[i].fw_id, clk_hw_get_name(hw), clk_hw_get_rate(hw));
+
+ map[i].hw = hw;
}
devm_kfree(dev, fixed_hws);
@@ -154,20 +232,55 @@ static const struct clk_map *fill_clk_map(struct r8a78000_cpg_priv *priv,
static int r8a78000_cpg_probe(struct platform_device *pdev)
{
+ struct device_node *scmi_clk_np __free(device_node) = NULL;
+ struct device_node *scmi __free(device_node) = NULL;
struct device *dev = &pdev->dev;
+ const struct cpg_data *cpg_data;
struct r8a78000_cpg_priv *priv;
const struct clk_map_in *map;
- map = of_device_get_match_data(dev);
- if (!map)
+ cpg_data = of_device_get_match_data(dev);
+ if (!cpg_data)
return -ENODEV;
+ map = cpg_data->default_map;
+
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
priv->dev = dev;
- priv->map = fill_clk_map(priv, map);
+
+ if (!IS_ENABLED(CONFIG_COMMON_CLK_SCMI)) {
+ dev_dbg(dev, "SCMI clock support is disabled\n");
+ goto fallback;
+ }
+
+ scmi = of_find_compatible_node(NULL, NULL, "arm,scmi");
+ if (!scmi) {
+ dev_dbg(dev, "Cannot find SCMI firmware node\n");
+ goto fallback;
+ }
+
+ if (!of_device_is_available(scmi)) {
+ dev_dbg(dev, "SCMI firmware node is not available\n");
+ goto fallback;
+ }
+
+ scmi_clk_np = scmi_find_proto(scmi, SCMI_PROTOCOL_CLOCK);
+ if (!scmi_clk_np) {
+ dev_dbg(dev, "Cannot find SCMI clock management protocol\n");
+ goto fallback;
+ }
+
+ map = cpg_data->fw_map;
+
+fallback:
+ /*
+ * We cannot do lazy look-up in r8a78000_clk_get(), as that function is
+ * called with of_clk_mutex already held.
+ */
+ priv->map = fill_clk_map(priv, map, cpg_data->default_map, scmi_clk_np);
if (IS_ERR(priv->map))
return PTR_ERR(priv->map);
@@ -181,10 +294,24 @@ static const struct clk_map_in r8a78000_cpg_default[] = {
{ -1 }
};
+static const struct clk_map_in r8a78000_cpg_fw[] = {
+ { R8A78000_CPG_SGASYNCD4_PERW_BUS, 1661 },
+ { R8A78000_CPG_SGASYNCD8_PERW_BUS, 1662 },
+ { R8A78000_CPG_SGASYNCD16_PERW_BUS, 1663 },
+ { R8A78000_CPG_MSO_SCP_MAIN, 1459 },
+ { R8A78000_CPG_MSOCK_PERW_BUS, 1667 },
+ { -1 }
+};
+
+static const struct cpg_data r8a78000_cpg_data = {
+ .default_map = r8a78000_cpg_default,
+ .fw_map = r8a78000_cpg_fw,
+};
+
static const struct of_device_id r8a78000_cpg_match[] = {
{
.compatible = "renesas,r8a78000-cpg",
- .data = &r8a78000_cpg_default,
+ .data = &r8a78000_cpg_data,
},
{ /* sentinel */ }
};
--
2.43.0
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH v4 3/8] reset: Extract __reset_control_get_from_provider()
2026-10-16 15:55 [PATCH v4 0/8] R-Car X5H Ironhide CPG/MDLC SCMI remapping support Geert Uytterhoeven
2026-10-16 15:55 ` [PATCH v4 1/8] clk: renesas: r8a78000: Add clk_hw to clk_map Geert Uytterhoeven
2026-10-16 15:55 ` [PATCH v4 2/8] clk: renesas: r8a78000: Add SCMI remapping support Geert Uytterhoeven
@ 2026-10-16 15:55 ` Geert Uytterhoeven
2026-10-16 15:55 ` [PATCH v4 4/8] reset: Add reset_control_get_from_provider_exclusive() Geert Uytterhoeven
` (4 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: Geert Uytterhoeven @ 2026-10-16 15:55 UTC (permalink / raw)
To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Stephen Boyd,
Brian Masney, Jerome Brunet, Ulf Hansson, Philipp Zabel,
Rafael J . Wysocki, Marek Vasut, Bartosz Golaszewski,
Konrad Dybcio, Kevin Hilman, Vinod Koul, Wolfram Sang,
Kuninori Morimoto, Laurent Pinchart, Clément Le Goffic,
Sudeep Holla, Cristian Marussi, arm-scmi, linux-arm-kernel
Cc: devicetree, linux-clk, linux-pm, linux-renesas-soc,
Geert Uytterhoeven
Extract the code to create a reset_control structure from a given
provider into its own function, so it can be reused later.
Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
Reviewed-by: Philipp Zabel <p.zabel@pengutronix.de>
---
v4:
- Keep RESET_CONTROL_FLAGS_BIT_OPTIONAL clearing in
__fwnode_reset_control_get(),
- Return directly instead of assigning to a local variable, letting us
drop the latter and simplify scoped_guard() to plain guard(),
- Add Reviewed-by,
v3:
- New.
---
drivers/reset/core.c | 85 +++++++++++++++++++++++---------------------
1 file changed, 45 insertions(+), 40 deletions(-)
diff --git a/drivers/reset/core.c b/drivers/reset/core.c
index 38e189d04d09b270..0e11f4c81c10d601 100644
--- a/drivers/reset/core.c
+++ b/drivers/reset/core.c
@@ -1135,6 +1135,49 @@ __reset_find_rcdev(const struct fwnode_reference_args *args, bool gpio_fallback)
return NULL;
}
+static struct reset_control *
+__reset_control_get_from_provider(const struct fwnode_reference_args *args,
+ struct fwnode_handle *consumer, int index,
+ bool gpio_fallback,
+ enum reset_control_flags flags)
+{
+ struct reset_controller_dev *rcdev;
+ int rstc_id = -EINVAL;
+
+ guard(mutex)(&reset_list_mutex);
+
+ rcdev = __reset_find_rcdev(args, gpio_fallback);
+ if (!rcdev)
+ return ERR_PTR(-EPROBE_DEFER);
+
+ if (WARN_ON(args->nargs != rcdev->fwnode_reset_n_cells))
+ return ERR_PTR(-EINVAL);
+
+ if (rcdev->of_xlate && is_of_node(consumer)) {
+ struct device_node *np = to_of_node(consumer);
+ struct of_phandle_args of_args;
+ int ret;
+
+ ret = of_parse_phandle_with_args(np,
+ gpio_fallback ? "reset-gpios" : "resets",
+ gpio_fallback ? "#gpio-cells" : "#reset-cells",
+ gpio_fallback ? 0 : index,
+ &of_args);
+ if (ret)
+ return ERR_PTR(ret);
+
+ rstc_id = rcdev->of_xlate(rcdev, &of_args);
+ of_node_put(of_args.np);
+ } else if (rcdev->fwnode_xlate) {
+ rstc_id = rcdev->fwnode_xlate(rcdev, args);
+ }
+ if (rstc_id < 0)
+ return ERR_PTR(rstc_id);
+
+ guard(mutex)(&rcdev->lock);
+ return __reset_control_get_internal(rcdev, rstc_id, flags);
+}
+
struct reset_control *
__fwnode_reset_control_get(struct fwnode_handle *fwnode, const char *id, int index,
enum reset_control_flags flags)
@@ -1142,10 +1185,7 @@ __fwnode_reset_control_get(struct fwnode_handle *fwnode, const char *id, int ind
bool optional = flags & RESET_CONTROL_FLAGS_BIT_OPTIONAL;
bool gpio_fallback = false;
struct reset_control *rstc = ERR_PTR(-EINVAL);
- struct reset_controller_dev *rcdev;
struct fwnode_reference_args args;
- struct of_phandle_args of_args;
- int rstc_id = -EINVAL;
int ret;
if (!fwnode)
@@ -1185,46 +1225,11 @@ __fwnode_reset_control_get(struct fwnode_handle *fwnode, const char *id, int ind
}
}
- guard(mutex)(&reset_list_mutex);
-
- rcdev = __reset_find_rcdev(&args, gpio_fallback);
- if (!rcdev) {
- rstc = ERR_PTR(-EPROBE_DEFER);
- goto out_put;
- }
-
- if (WARN_ON(args.nargs != rcdev->fwnode_reset_n_cells)) {
- rstc = ERR_PTR(-EINVAL);
- goto out_put;
- }
-
- if (rcdev->of_xlate && is_of_node(fwnode)) {
- ret = of_parse_phandle_with_args(to_of_node(fwnode),
- gpio_fallback ? "reset-gpios" : "resets",
- gpio_fallback ? "#gpio-cells" : "#reset-cells",
- gpio_fallback ? 0 : index,
- &of_args);
- if (ret) {
- rstc = ERR_PTR(ret);
- goto out_put;
- }
-
- rstc_id = rcdev->of_xlate(rcdev, &of_args);
- of_node_put(of_args.np);
- } else if (rcdev->fwnode_xlate) {
- rstc_id = rcdev->fwnode_xlate(rcdev, &args);
- }
- if (rstc_id < 0) {
- rstc = ERR_PTR(rstc_id);
- goto out_put;
- }
-
flags &= ~RESET_CONTROL_FLAGS_BIT_OPTIONAL;
- scoped_guard(mutex, &rcdev->lock)
- rstc = __reset_control_get_internal(rcdev, rstc_id, flags);
+ rstc = __reset_control_get_from_provider(&args, fwnode, index,
+ gpio_fallback, flags);
-out_put:
fwnode_handle_put(args.fwnode);
return rstc;
--
2.43.0
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH v4 4/8] reset: Add reset_control_get_from_provider_exclusive()
2026-10-16 15:55 [PATCH v4 0/8] R-Car X5H Ironhide CPG/MDLC SCMI remapping support Geert Uytterhoeven
` (2 preceding siblings ...)
2026-10-16 15:55 ` [PATCH v4 3/8] reset: Extract __reset_control_get_from_provider() Geert Uytterhoeven
@ 2026-10-16 15:55 ` Geert Uytterhoeven
2026-10-16 15:55 ` [PATCH v4 5/8] pmdomain: Make genpd_get_from_provider() return -EPROBE_DEFER Geert Uytterhoeven
` (3 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: Geert Uytterhoeven @ 2026-10-16 15:55 UTC (permalink / raw)
To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Stephen Boyd,
Brian Masney, Jerome Brunet, Ulf Hansson, Philipp Zabel,
Rafael J . Wysocki, Marek Vasut, Bartosz Golaszewski,
Konrad Dybcio, Kevin Hilman, Vinod Koul, Wolfram Sang,
Kuninori Morimoto, Laurent Pinchart, Clément Le Goffic,
Sudeep Holla, Cristian Marussi, arm-scmi, linux-arm-kernel
Cc: devicetree, linux-clk, linux-pm, linux-renesas-soc,
Geert Uytterhoeven
When stacking two reset controllers, the upper one should go through the
proper channels: request exclusive reset controls and use them via the
reset_control_*() APIs. However, unlike the clock subsystem, which has
of_clk_get_from_provider(), the reset subsystem does not offer a way to
lookup and obtain a reset from a reset provider.
Hence add a public helper reset_control_get_from_provider_exclusive()
to lookup and obtain an exclusive reference to a reset controller, based
on the recently introduced __reset_control_get_from_provider() helper.
This will be used by the R-Car X5H Module Controller driver.
Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
---
v4:
- Check for overflow when converting (u64) fwnode args to (uint32_t)
OF args,
- Document that reset_control_get_from_provider_exclusive() must not
be given a reference to a GPIO provider,
- Let the dummy return ERR_PTR(-ENOTSUPP) instead of NULL,
v3:
- Replace reset_controller_get_provider() by
reset_control_get_from_provider_exclusive(),
- Add kerneldoc,
v2:
- No changes.
---
drivers/reset/core.c | 41 ++++++++++++++++++++++++++++++++++++-----
include/linux/reset.h | 8 ++++++++
2 files changed, 44 insertions(+), 5 deletions(-)
diff --git a/drivers/reset/core.c b/drivers/reset/core.c
index 0e11f4c81c10d601..421bcc4c89cc21ac 100644
--- a/drivers/reset/core.c
+++ b/drivers/reset/core.c
@@ -1141,6 +1141,7 @@ __reset_control_get_from_provider(const struct fwnode_reference_args *args,
bool gpio_fallback,
enum reset_control_flags flags)
{
+ struct fwnode_handle *fwnode = consumer ?: args->fwnode;
struct reset_controller_dev *rcdev;
int rstc_id = -EINVAL;
@@ -1153,18 +1154,30 @@ __reset_control_get_from_provider(const struct fwnode_reference_args *args,
if (WARN_ON(args->nargs != rcdev->fwnode_reset_n_cells))
return ERR_PTR(-EINVAL);
- if (rcdev->of_xlate && is_of_node(consumer)) {
- struct device_node *np = to_of_node(consumer);
+ if (rcdev->of_xlate && is_of_node(fwnode)) {
+ struct device_node *np = to_of_node(fwnode);
struct of_phandle_args of_args;
int ret;
- ret = of_parse_phandle_with_args(np,
+ if (consumer) {
+ ret = of_parse_phandle_with_args(np,
gpio_fallback ? "reset-gpios" : "resets",
gpio_fallback ? "#gpio-cells" : "#reset-cells",
gpio_fallback ? 0 : index,
&of_args);
- if (ret)
- return ERR_PTR(ret);
+ if (ret)
+ return ERR_PTR(ret);
+ } else {
+ of_args.np = of_node_get(np);
+ of_args.args_count = args->nargs;
+
+ for (unsigned int i = 0; i < args->nargs; i++) {
+ if (args->args[i] > U32_MAX)
+ return ERR_PTR(-EINVAL);
+
+ of_args.args[i] = args->args[i];
+ }
+ }
rstc_id = rcdev->of_xlate(rcdev, &of_args);
of_node_put(of_args.np);
@@ -1254,6 +1267,24 @@ struct reset_control *__reset_control_get(struct device *dev, const char *id,
}
EXPORT_SYMBOL_GPL(__reset_control_get);
+/**
+ * reset_control_get_from_provider_exclusive - Lookup and obtain an exclusive
+ * reference to a reset controller.
+ * @args: Reference to the reset controller provider with all the args like
+ * reset number. This must not be a reference to a GPIO provider.
+ *
+ * Returns a struct reset_control or IS_ERR() condition containing errno.
+ * If this function is called more than once for the same reset control it will
+ * return -EBUSY.
+ */
+struct reset_control *
+reset_control_get_from_provider_exclusive(const struct fwnode_reference_args *args)
+{
+ return __reset_control_get_from_provider(args, NULL, 0, false,
+ RESET_CONTROL_EXCLUSIVE);
+}
+EXPORT_SYMBOL_GPL(reset_control_get_from_provider_exclusive);
+
int __reset_control_bulk_get(struct device *dev, int num_rstcs,
struct reset_control_bulk_data *rstcs,
enum reset_control_flags flags)
diff --git a/include/linux/reset.h b/include/linux/reset.h
index 9c391cf0c82298a0..384273ec0c7cbef7 100644
--- a/include/linux/reset.h
+++ b/include/linux/reset.h
@@ -90,6 +90,8 @@ struct reset_control *__fwnode_reset_control_get(struct fwnode_handle *fwnode,
const char *id, int index, enum reset_control_flags flags);
struct reset_control *__reset_control_get(struct device *dev, const char *id,
int index, enum reset_control_flags flags);
+struct reset_control *reset_control_get_from_provider_exclusive(
+ const struct fwnode_reference_args *args);
void reset_control_put(struct reset_control *rstc);
int __reset_control_bulk_get(struct device *dev, int num_rstcs,
struct reset_control_bulk_data *rstcs,
@@ -173,6 +175,12 @@ static inline struct reset_control *__reset_control_get(
return optional ? NULL : ERR_PTR(-ENOTSUPP);
}
+static inline struct reset_control *reset_control_get_from_provider_exclusive(
+ const struct fwnode_reference_args *args)
+{
+ return ERR_PTR(-ENOTSUPP);
+}
+
static inline int
reset_control_bulk_reset(int num_rstcs, struct reset_control_bulk_data *rstcs)
{
--
2.43.0
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH v4 5/8] pmdomain: Make genpd_get_from_provider() return -EPROBE_DEFER
2026-10-16 15:55 [PATCH v4 0/8] R-Car X5H Ironhide CPG/MDLC SCMI remapping support Geert Uytterhoeven
` (3 preceding siblings ...)
2026-10-16 15:55 ` [PATCH v4 4/8] reset: Add reset_control_get_from_provider_exclusive() Geert Uytterhoeven
@ 2026-10-16 15:55 ` Geert Uytterhoeven
2026-10-16 15:55 ` [PATCH v4 6/8] pmdomain: Make genpd_get_from_provider() public Geert Uytterhoeven
` (2 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: Geert Uytterhoeven @ 2026-10-16 15:55 UTC (permalink / raw)
To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Stephen Boyd,
Brian Masney, Jerome Brunet, Ulf Hansson, Philipp Zabel,
Rafael J . Wysocki, Marek Vasut, Bartosz Golaszewski,
Konrad Dybcio, Kevin Hilman, Vinod Koul, Wolfram Sang,
Kuninori Morimoto, Laurent Pinchart, Clément Le Goffic,
Sudeep Holla, Cristian Marussi, arm-scmi, linux-arm-kernel
Cc: devicetree, linux-clk, linux-pm, linux-renesas-soc,
Geert Uytterhoeven
Unlike provider-based look-up functions in other subsystems[1],
genpd_get_from_provider() returns -ENOENT instead of -EPROBE_DEFER when
the provider is not found, thus not supporting probe deferral.
Currently this is worked around in of_genpd_add_subdomain() by remapping
-ENOENT[2].
Fix this for all current and future callers by returning -EPROBE_DEFER
instead, and drop the no longer needed remapping code.
[1] of_clk_get_hw_from_clkspec(), __fwnode_reset_control_get()),
[2] Commit 18027d6f392ee8d8 ("PM: domains: Make of_genpd_add_subdomain()
return -EPROBE_DEFER").
Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
---
v4:
- New.
---
drivers/pmdomain/core.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/pmdomain/core.c b/drivers/pmdomain/core.c
index 964e44c2f78f1d55..88c886814a5b9da3 100644
--- a/drivers/pmdomain/core.c
+++ b/drivers/pmdomain/core.c
@@ -3021,7 +3021,7 @@ EXPORT_SYMBOL_GPL(of_genpd_del_provider);
static struct generic_pm_domain *genpd_get_from_provider(
const struct of_phandle_args *genpdspec)
{
- struct generic_pm_domain *genpd = ERR_PTR(-ENOENT);
+ struct generic_pm_domain *genpd = ERR_PTR(-EPROBE_DEFER);
struct of_genpd_provider *provider;
if (!genpdspec)
@@ -3276,7 +3276,7 @@ int of_genpd_add_subdomain(const struct of_phandle_args *parent_spec,
out:
mutex_unlock(&gpd_list_lock);
- return ret == -ENOENT ? -EPROBE_DEFER : ret;
+ return ret;
}
EXPORT_SYMBOL_GPL(of_genpd_add_subdomain);
--
2.43.0
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH v4 6/8] pmdomain: Make genpd_get_from_provider() public
2026-10-16 15:55 [PATCH v4 0/8] R-Car X5H Ironhide CPG/MDLC SCMI remapping support Geert Uytterhoeven
` (4 preceding siblings ...)
2026-10-16 15:55 ` [PATCH v4 5/8] pmdomain: Make genpd_get_from_provider() return -EPROBE_DEFER Geert Uytterhoeven
@ 2026-10-16 15:55 ` Geert Uytterhoeven
2026-10-16 15:55 ` [PATCH v4 7/8] pmdomain: renesas: r8a78000: Add SCMI remapping support Geert Uytterhoeven
2026-10-16 15:55 ` [PATCH v4 8/8] arm64: dts: renesas: ironhide: Enable SCMI devpd, sys, clock, and reset Geert Uytterhoeven
7 siblings, 0 replies; 9+ messages in thread
From: Geert Uytterhoeven @ 2026-10-16 15:55 UTC (permalink / raw)
To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Stephen Boyd,
Brian Masney, Jerome Brunet, Ulf Hansson, Philipp Zabel,
Rafael J . Wysocki, Marek Vasut, Bartosz Golaszewski,
Konrad Dybcio, Kevin Hilman, Vinod Koul, Wolfram Sang,
Kuninori Morimoto, Laurent Pinchart, Clément Le Goffic,
Sudeep Holla, Cristian Marussi, arm-scmi, linux-arm-kernel
Cc: devicetree, linux-clk, linux-pm, linux-renesas-soc,
Geert Uytterhoeven
Unlike the corresponding function in the clock subsystem[1],
genpd_get_from_provider() is private, and thus cannot be used by PM
Domain drivers.
Make it public, so the R-Car X5H Module Controller driver can call it.
Add an "of_"-prefix for consistency with public genpd function naming.
[1] of_clk_get_from_provider().
Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
---
v4:
- Improve patch description,
v3:
- Add "of_" prefix,
v2:
- No changes.
---
drivers/pmdomain/core.c | 22 +++++++++++-----------
include/linux/pm_domain.h | 7 +++++++
2 files changed, 18 insertions(+), 11 deletions(-)
diff --git a/drivers/pmdomain/core.c b/drivers/pmdomain/core.c
index 88c886814a5b9da3..4136368456d994f7 100644
--- a/drivers/pmdomain/core.c
+++ b/drivers/pmdomain/core.c
@@ -3008,7 +3008,7 @@ void of_genpd_del_provider(struct device_node *np)
EXPORT_SYMBOL_GPL(of_genpd_del_provider);
/**
- * genpd_get_from_provider() - Look-up PM domain
+ * of_genpd_get_from_provider() - Look-up PM domain
* @genpdspec: OF phandle args to use for look-up
*
* Looks for a PM domain provider under the node specified by @genpdspec and if
@@ -3018,8 +3018,7 @@ EXPORT_SYMBOL_GPL(of_genpd_del_provider);
* Returns a valid pointer to struct generic_pm_domain on success or ERR_PTR()
* on failure.
*/
-static struct generic_pm_domain *genpd_get_from_provider(
- const struct of_phandle_args *genpdspec)
+struct generic_pm_domain *of_genpd_get_from_provider(const struct of_phandle_args *genpdspec)
{
struct generic_pm_domain *genpd = ERR_PTR(-EPROBE_DEFER);
struct of_genpd_provider *provider;
@@ -3041,6 +3040,7 @@ static struct generic_pm_domain *genpd_get_from_provider(
return genpd;
}
+EXPORT_SYMBOL_GPL(of_genpd_get_from_provider);
/**
* of_genpd_add_child_ids() - Parse power-domains-child-ids property
@@ -3113,7 +3113,7 @@ int of_genpd_add_child_ids(struct device_node *np,
goto err_unwind;
/* Get the parent domain */
- parent_genpd = genpd_get_from_provider(&parent_args);
+ parent_genpd = of_genpd_get_from_provider(&parent_args);
of_node_put(parent_args.np);
if (IS_ERR(parent_genpd)) {
pr_err("Failed to get parent domain for %pOF: %ld\n",
@@ -3196,7 +3196,7 @@ int of_genpd_remove_child_ids(struct device_node *np,
if (ret)
continue;
- parent_genpd = genpd_get_from_provider(&parent_args);
+ parent_genpd = of_genpd_get_from_provider(&parent_args);
of_node_put(parent_args.np);
if (IS_ERR(parent_genpd))
continue;
@@ -3227,7 +3227,7 @@ int of_genpd_add_device(const struct of_phandle_args *genpdspec, struct device *
mutex_lock(&gpd_list_lock);
- genpd = genpd_get_from_provider(genpdspec);
+ genpd = of_genpd_get_from_provider(genpdspec);
if (IS_ERR(genpd)) {
ret = PTR_ERR(genpd);
goto out;
@@ -3259,13 +3259,13 @@ int of_genpd_add_subdomain(const struct of_phandle_args *parent_spec,
mutex_lock(&gpd_list_lock);
- parent = genpd_get_from_provider(parent_spec);
+ parent = of_genpd_get_from_provider(parent_spec);
if (IS_ERR(parent)) {
ret = PTR_ERR(parent);
goto out;
}
- subdomain = genpd_get_from_provider(subdomain_spec);
+ subdomain = of_genpd_get_from_provider(subdomain_spec);
if (IS_ERR(subdomain)) {
ret = PTR_ERR(subdomain);
goto out;
@@ -3297,13 +3297,13 @@ int of_genpd_remove_subdomain(const struct of_phandle_args *parent_spec,
mutex_lock(&gpd_list_lock);
- parent = genpd_get_from_provider(parent_spec);
+ parent = of_genpd_get_from_provider(parent_spec);
if (IS_ERR(parent)) {
ret = PTR_ERR(parent);
goto out;
}
- subdomain = genpd_get_from_provider(subdomain_spec);
+ subdomain = of_genpd_get_from_provider(subdomain_spec);
if (IS_ERR(subdomain)) {
ret = PTR_ERR(subdomain);
goto out;
@@ -3491,7 +3491,7 @@ static int __genpd_dev_pm_attach(struct device *dev, struct device *base_dev,
return ret;
mutex_lock(&gpd_list_lock);
- pd = genpd_get_from_provider(&pd_args);
+ pd = of_genpd_get_from_provider(&pd_args);
of_node_put(pd_args.np);
if (IS_ERR(pd)) {
mutex_unlock(&gpd_list_lock);
diff --git a/include/linux/pm_domain.h b/include/linux/pm_domain.h
index 14e0e346c610a9a6..d65ee8b3725c1bcb 100644
--- a/include/linux/pm_domain.h
+++ b/include/linux/pm_domain.h
@@ -467,6 +467,7 @@ int of_genpd_add_provider_simple(struct device_node *np,
int of_genpd_add_provider_onecell(struct device_node *np,
struct genpd_onecell_data *data);
void of_genpd_del_provider(struct device_node *np);
+struct generic_pm_domain *of_genpd_get_from_provider(const struct of_phandle_args *genpdspec);
int of_genpd_add_device(const struct of_phandle_args *args, struct device *dev);
int of_genpd_add_subdomain(const struct of_phandle_args *parent_spec,
const struct of_phandle_args *subdomain_spec);
@@ -501,6 +502,12 @@ static inline int of_genpd_add_provider_onecell(struct device_node *np,
static inline void of_genpd_del_provider(struct device_node *np) {}
+static inline struct generic_pm_domain *of_genpd_get_from_provider(
+ const struct of_phandle_args *genpdspec)
+{
+ return ERR_PTR(-ENODEV);
+}
+
static inline int of_genpd_add_device(const struct of_phandle_args *args,
struct device *dev)
{
--
2.43.0
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH v4 7/8] pmdomain: renesas: r8a78000: Add SCMI remapping support
2026-10-16 15:55 [PATCH v4 0/8] R-Car X5H Ironhide CPG/MDLC SCMI remapping support Geert Uytterhoeven
` (5 preceding siblings ...)
2026-10-16 15:55 ` [PATCH v4 6/8] pmdomain: Make genpd_get_from_provider() public Geert Uytterhoeven
@ 2026-10-16 15:55 ` Geert Uytterhoeven
2026-10-16 15:55 ` [PATCH v4 8/8] arm64: dts: renesas: ironhide: Enable SCMI devpd, sys, clock, and reset Geert Uytterhoeven
7 siblings, 0 replies; 9+ messages in thread
From: Geert Uytterhoeven @ 2026-10-16 15:55 UTC (permalink / raw)
To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Stephen Boyd,
Brian Masney, Jerome Brunet, Ulf Hansson, Philipp Zabel,
Rafael J . Wysocki, Marek Vasut, Bartosz Golaszewski,
Konrad Dybcio, Kevin Hilman, Vinod Koul, Wolfram Sang,
Kuninori Morimoto, Laurent Pinchart, Clément Le Goffic,
Sudeep Holla, Cristian Marussi, arm-scmi, linux-arm-kernel
Cc: devicetree, linux-clk, linux-pm, linux-renesas-soc,
Geert Uytterhoeven
Extend the Module Controller driver for R-Car X5H with SCMI support, to
remap hardware power domain, module, and reset IDs to abstractions
(power domains, clocks, and resets) offered by the SCMI firmware when
running on a system with SCMI, and SCMI support is enabled. Take into
account some peculiarities of the current SCMI firmware implementations,
like missing handling of gateable functional clock parents.
Support for power domains, and module clocks and resets used by serial,
I2C, and SPI devices is included.
Note that SCMI clocks representing hardware modules expose access to
Module Standby control: they are fake clocks, with a zero clock rate,
that can just be enabled and disabled. Hence they are controlled from
the clock domain's start/stop callbacks, together with gateable
functional clock parents.
This lets us avoid putting SCMI IDs in DT, and allows us to use the same
hardware description in SoC DTS for systems with and without SCMI
firmware.
For now, it is assumed SCMI power domain, clock, and reset IDs are
stable, and match SCP FW SDKv4.31.0 and later.
Suggested-by: Marek Vasut <marek.vasut+renesas@mailbox.org>
Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
---
v4:
- Let r8a78000_mdlc_attach_dev() return success when the MDLC instance
is not found, to avoid breaking users referring in DT to an SCMI
power-domain directly,
- Mark scmi_{reset,power}_np __free(device_node),
- Extract scmi_get_clk(),
- Add support for gateable fck parents,
- Assume all SCMI clock IDs are stable, and drop support for handling
different ID spaces,
- Include initial mapping table for SCP FW SDKv4.31.0 and later,
- Propagate all reset failures, as SCP FW SDKv4.28.0 which restricted
HSCIF0 reset is no longer supported,
- Drop renesas,scmi-firmware support, just find an arm,scmi node
instead,
- Fall back to the non-SCMI defaults when any of SCMI power domain,
clock, or reset support is disabled,
v3:
- Rebase to v7.3-rc1,
- Consistently use /* sentinel */ for zero sentinels,
- s/genpd_get_from_provider/of_genpd_get_from_provider/,
- s/firmware/renesas,scmi-firmware/,
- Replace reset_controller_get_provider() by
reset_control_get_from_provider_exclusive(),
- Call reset_control_*() APIs instead of internal reset_control_ops
methods,
- Rename struct mod_map to struct mod_map_in,
- Add struct mod_map, which also contains a reset_control pointer,
- Add fill_mod_map() to obtain all SCMI reset_control structures
up-front, as one can no longer use lazy look-up,
- Prefix DEFINE_MDLC_RESET_WRAPPER() parameters with underscores,
- Reword,
v2:
- Spin off from "pmdomain: renesas: Add R-Car X5H MDLC SCMI remapping
driver".
- Propagate error codes from scmi_get_base_info() now it can return
-EPROBE_DEFER,
- Improve error message on scmi_get_base_info() failure,
- Check if SCMI firmware node is available,
- Replace scmi_power_np and scmi_reset_np members of struct
r8a78000_mdlc_priv by local variables in r8a78000_mdlc_probe(), and
pass the former to fill_power_map(),
- Move check for supported firmware version up,
- Fall back to the default support when SCMI support or firmware is
not available, or when the detected SCMI firmware version is not
supported by the driver,
- Drop mapping tables.
---
drivers/pmdomain/renesas/r8a78000-mdlc.c | 410 +++++++++++++++++++++--
1 file changed, 386 insertions(+), 24 deletions(-)
diff --git a/drivers/pmdomain/renesas/r8a78000-mdlc.c b/drivers/pmdomain/renesas/r8a78000-mdlc.c
index 1fd71bb3da776eb1..af36d8229030f6c0 100644
--- a/drivers/pmdomain/renesas/r8a78000-mdlc.c
+++ b/drivers/pmdomain/renesas/r8a78000-mdlc.c
@@ -5,24 +5,55 @@
* Copyright (C) 2026 Glider bv
*/
+#include <linux/cleanup.h>
+#include <linux/clk.h>
#include <linux/dev_printk.h>
#include <linux/device-id/of.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
+#include <linux/pm_clock.h>
#include <linux/pm_domain.h>
#include <linux/reset-controller.h>
+#include <linux/reset.h>
+#include <linux/scmi_protocol.h>
#include <linux/slab.h>
#include <dt-bindings/power/renesas,r8a78000-mdlc.h>
+struct power_map_in {
+ int hw_id; /* Hardware power domain ID or -1 sentinel */
+ u32 fw_id; /* SCMI firmware power domain ID */
+};
+
+struct power_map {
+ int hw_id; /* Hardware power domain ID or -1 sentinel */
+ u32 fw_id; /* SCMI firmware power domain ID */
+ struct generic_pm_domain *genpd;
+};
+
+struct mod_map_in {
+ int hw_id; /* Hardware module ID or -1 sentinel */
+ u32 fw_id; /* SCMI clock and reset IDs are identical */
+ s32 fw_fck_parent; /* SCMI functional clock parent ID or -1 */
+};
+
struct mod_map {
int hw_id; /* Hardware module ID or -1 sentinel */
+ u32 fw_id; /* SCMI clock and reset IDs are identical */
+ s32 fw_fck_parent; /* SCMI functional clock parent ID or -1 */
+ struct reset_control *rstc;
};
struct mdlc_info {
u32 base;
- const struct mod_map *mod_map;
+ const struct power_map_in *power_map;
+ const struct mod_map_in *mod_map;
+};
+
+struct mdlc_data {
+ const struct mdlc_info *default_info;
+ const struct mdlc_info *fw_info;
};
/**
@@ -33,7 +64,9 @@ struct mdlc_info {
* @rcdev: Reset controller entity
* @dev: MDLC device
* @np: Device node in DT representing the MDLC
- * @mod_map: Mapping from hardware module IDs
+ * @scmi_clk_np: Device node in DT for the SCMI firmware clock protocol
+ * @power_map: Mapping from hardware power domain IDs to SCMI power domains
+ * @mod_map: Mapping from hardware module IDs to SCMI clocks and resets
*/
struct r8a78000_mdlc_priv {
struct hlist_node link;
@@ -41,6 +74,8 @@ struct r8a78000_mdlc_priv {
struct reset_controller_dev rcdev;
struct device *dev;
struct device_node *np;
+ struct device_node *scmi_clk_np;
+ const struct power_map *power_map;
const struct mod_map *mod_map;
};
@@ -49,12 +84,28 @@ static struct generic_pm_domain *r8a78000_genpd_always_on;
static HLIST_HEAD(r8a78000_mdlc_list);
static DEFINE_MUTEX(r8a78000_mdlc_lock); /* protects the list above */
+static const struct power_map *power_map_find(const struct power_map *map,
+ u32 id)
+{
+ if (!map)
+ return NULL;
+
+ for (; map->hw_id >= 0; map++) {
+ if (map->hw_id == id)
+ return map;
+ }
+
+ return NULL;
+}
+
static struct generic_pm_domain *r8a78000_genpd_xlate(
const struct of_phandle_args *spec, void *data)
{
struct r8a78000_mdlc_priv *priv = container_of(data,
struct r8a78000_mdlc_priv, genpd_data);
+ struct generic_pm_domain *genpd;
struct device *dev = priv->dev;
+ const struct power_map *map;
u32 id;
if (spec->args_count != 2)
@@ -69,9 +120,18 @@ static struct generic_pm_domain *r8a78000_genpd_xlate(
return r8a78000_genpd_always_on;
}
- /* For now only always-on domains are supported */
- dev_err(dev, "Unknown power domain 0x%x\n", id);
- return ERR_PTR(-ENOENT);
+ map = power_map_find(priv->power_map, id);
+ if (!map) {
+ dev_err(dev, "Unknown power domain 0x%x\n", id);
+ return ERR_PTR(-ENOENT);
+ }
+
+ dev_dbg(dev, "Mapping HW power domain 0x%x to SCMI power domain %u\n",
+ id, map->fw_id);
+
+ genpd = map->genpd;
+
+ return genpd;
}
#define rcdev_to_priv(_rcdev) \
@@ -109,18 +169,22 @@ static int r8a78000_mdlc_reset_xlate(struct reset_controller_dev *rcdev,
return -ENOENT;
}
- dev_dbg(dev, "Ignoring HW reset 0x%x\n", id);
- return id;
+ if (!map->rstc)
+ dev_dbg(dev, "Ignoring HW reset 0x%x\n", id);
+ else
+ dev_dbg(dev, "Mapping HW reset 0x%x to SCMI reset %u\n", id,
+ map->fw_id);
+
+ return map - priv->mod_map;
}
-#define DEFINE_MDLC_RESET_WRAPPER(op) \
- static int r8a78000_mdlc_ ## op(struct reset_controller_dev *rcdev, \
- unsigned long id) \
- { \
- struct r8a78000_mdlc_priv *priv = rcdev_to_priv(rcdev); \
- \
- dev_dbg(priv->dev, "%s: Ignoring\n", __func__); \
- return 0; \
+#define DEFINE_MDLC_RESET_WRAPPER(_op) \
+ static int r8a78000_mdlc_ ## _op(struct reset_controller_dev *rcdev, \
+ unsigned long id) \
+ { \
+ struct r8a78000_mdlc_priv *priv = rcdev_to_priv(rcdev); \
+ \
+ return reset_control_ ## _op(priv->mod_map[id].rstc); \
}
DEFINE_MDLC_RESET_WRAPPER(reset)
@@ -135,11 +199,45 @@ static const struct reset_control_ops r8a78000_mdlc_reset_ops = {
.status = r8a78000_mdlc_status,
};
+static struct device_node *scmi_find_proto(struct device_node *scmi, u32 proto)
+{
+ for_each_available_child_of_node_scoped(scmi, child) {
+ u32 reg;
+
+ if (of_property_read_u32(child, "reg", ®))
+ continue;
+
+ if (reg == proto)
+ return_ptr(child);
+ }
+
+ return NULL;
+}
+
+static struct clk *scmi_get_clk(struct device *dev, struct device_node *np,
+ u32 id)
+{
+ struct of_phandle_args scmi_spec = {
+ .np = np,
+ .args_count = 1,
+ .args[0] = id,
+ };
+ struct clk *clk = of_clk_get_from_provider(&scmi_spec);
+
+ if (IS_ERR(clk))
+ dev_err(dev, "Cannot get SCMI clock %u: %pe\n", id, clk);
+ else
+ dev_dbg(dev, "SCMI clock %u is %pC\n", id, clk);
+
+ return clk;
+}
+
static int r8a78000_mdlc_attach_dev(struct generic_pm_domain *domain,
struct device *dev)
{
struct device_node *np = dev->of_node;
struct r8a78000_mdlc_priv *priv;
+ struct clk *clk, *parent = NULL;
struct of_phandle_args pd_spec;
const struct mod_map *map;
unsigned int id;
@@ -158,9 +256,10 @@ static int r8a78000_mdlc_attach_dev(struct generic_pm_domain *domain,
}
if (!priv) {
- dev_err(dev, "%s: MDLC %pOF not found\n", __func__, pd_spec.np);
+ dev_warn(dev, "%s: Ignoring non-MDLC %pOF\n", __func__,
+ pd_spec.np);
of_node_put(pd_spec.np);
- return -ENODEV;
+ return 0;
}
id = pd_spec.args[1];
@@ -172,8 +271,158 @@ static int r8a78000_mdlc_attach_dev(struct generic_pm_domain *domain,
return -ENOENT;
}
- dev_dbg(dev, "Ignoring HW module 0x%x\n", id);
+ if (!priv->scmi_clk_np) {
+ dev_dbg(dev, "Ignoring HW module 0x%x\n", id);
+ return 0;
+ }
+
+ dev_dbg(dev, "Mapping HW module 0x%x to SCMI clock %u fck parent %d\n",
+ id, map->fw_id, map->fw_fck_parent);
+
+ clk = scmi_get_clk(dev, priv->scmi_clk_np, map->fw_id);
+ if (IS_ERR(clk))
+ return PTR_ERR(clk);
+
+ if (!clk) {
+ /* Ignore missing SCMI module clocks */
+ return 0;
+ }
+
+ if (map->fw_fck_parent >= 0) {
+ parent = scmi_get_clk(dev, priv->scmi_clk_np,
+ map->fw_fck_parent);
+ if (IS_ERR(parent)) {
+ ret = PTR_ERR(parent);
+ goto fail_put_clk;
+ }
+ }
+
+ ret = pm_clk_create(dev);
+ if (ret)
+ goto fail_put_parent;
+
+ if (parent)
+ ret = pm_clk_add_clk(dev, parent);
+ if (!ret)
+ ret = pm_clk_add_clk(dev, clk);
+ if (ret)
+ goto fail_destroy;
+
return 0;
+
+fail_destroy:
+ pm_clk_destroy(dev);
+fail_put_parent:
+ clk_put(parent);
+fail_put_clk:
+ clk_put(clk);
+ return ret;
+}
+
+static void r8a78000_mdlc_detach_dev(struct generic_pm_domain *domain,
+ struct device *dev)
+{
+ if (!pm_clk_no_clocks(dev))
+ pm_clk_destroy(dev);
+}
+
+static const struct power_map *fill_power_map(struct r8a78000_mdlc_priv *priv,
+ const struct power_map_in *map_in,
+ struct device_node *scmi_power_np)
+{
+ struct of_phandle_args scmi_spec;
+ struct generic_pm_domain *genpd;
+ struct device *dev = priv->dev;
+ struct power_map *map;
+ unsigned int i;
+
+ if (!map_in)
+ return NULL;
+
+ for (i = 0; map_in[i].hw_id >= 0; i++) { }
+
+ map = devm_kcalloc(dev, i + 1, sizeof(*map), GFP_KERNEL);
+ if (!map)
+ return ERR_PTR(-ENOMEM);
+
+ for (i = 0; ; i++) {
+ map[i].hw_id = map_in[i].hw_id;
+ if (map[i].hw_id < 0)
+ break;
+
+ map[i].fw_id = map_in[i].fw_id;
+
+ scmi_spec.np = scmi_power_np;
+ scmi_spec.args_count = 1;
+ scmi_spec.args[0] = map[i].fw_id;
+
+ genpd = of_genpd_get_from_provider(&scmi_spec);
+ if (IS_ERR(genpd))
+ return dev_err_cast_probe(dev, genpd,
+ "Failed to get SCMI power domain %u\n",
+ map[i].fw_id);
+
+ dev_dbg(dev, "SCMI power domain %u is %s\n", map[i].fw_id,
+ genpd->name);
+
+ map[i].genpd = genpd;
+
+ /* Hook up clock domain support */
+ genpd->attach_dev = r8a78000_mdlc_attach_dev;
+ genpd->detach_dev = r8a78000_mdlc_detach_dev;
+ /* Setting flags this late has no impact, but does not hurt */
+ genpd->flags |= GENPD_FLAG_PM_CLK;
+ genpd->dev_ops.stop = pm_clk_suspend;
+ genpd->dev_ops.start = pm_clk_resume;
+ }
+
+ return map;
+}
+
+static const struct mod_map *fill_mod_map(struct r8a78000_mdlc_priv *priv,
+ const struct mod_map_in *map_in,
+ struct fwnode_handle *scmi_reset_fwnode)
+{
+ struct fwnode_reference_args scmi_spec;
+ struct device *dev = priv->dev;
+ struct reset_control *rstc;
+ struct mod_map *map;
+ unsigned int i;
+
+ if (!map_in)
+ return NULL;
+
+ for (i = 0; map_in[i].hw_id >= 0; i++) { }
+
+ map = devm_kcalloc(dev, i + 1, sizeof(*map), GFP_KERNEL);
+ if (!map)
+ return ERR_PTR(-ENOMEM);
+
+ for (i = 0; ; i++) {
+ map[i].hw_id = map_in[i].hw_id;
+ if (map[i].hw_id < 0)
+ break;
+
+ map[i].fw_id = map_in[i].fw_id;
+ map[i].fw_fck_parent = map_in[i].fw_fck_parent;
+
+ if (!scmi_reset_fwnode)
+ continue;
+
+ scmi_spec.fwnode = scmi_reset_fwnode;
+ scmi_spec.nargs = 1;
+ scmi_spec.args[0] = map[i].fw_id;
+
+ rstc = reset_control_get_from_provider_exclusive(&scmi_spec);
+ if (IS_ERR(rstc))
+ return dev_err_cast_probe(dev, rstc,
+ "Failed to get SCMI reset %u\n",
+ map[i].fw_id);
+
+ map[i].rstc = rstc;
+ }
+
+ return map;
}
static void r8a78000_mdlc_unlink(void *data)
@@ -207,6 +456,8 @@ static int r8a78000_genpd_always_on_singleton(struct device *dev)
genpd->name = "always-on";
genpd->attach_dev = r8a78000_mdlc_attach_dev;
+ genpd->detach_dev = r8a78000_mdlc_detach_dev;
+ genpd->flags |= GENPD_FLAG_PM_CLK;
ret = pm_genpd_init(genpd, &pm_domain_always_on_gov, false);
if (ret) {
@@ -221,8 +472,13 @@ static int r8a78000_genpd_always_on_singleton(struct device *dev)
static int r8a78000_mdlc_probe(struct platform_device *pdev)
{
+ struct device_node *scmi_reset_np __free(device_node) = NULL;
+ struct device_node *scmi_power_np __free(device_node) = NULL;
+ struct device_node *scmi __free(device_node) = NULL;
struct device *dev = &pdev->dev;
struct device_node *np = dev->of_node;
+ const struct mdlc_data *mdlc_data;
+ struct device_node *scmi_clk_np;
struct r8a78000_mdlc_priv *priv;
const struct mdlc_info *info;
struct resource *res;
@@ -232,10 +488,12 @@ static int r8a78000_mdlc_probe(struct platform_device *pdev)
if (ret)
return ret;
- info = of_device_get_match_data(dev);
- if (!info)
+ mdlc_data = of_device_get_match_data(dev);
+ if (!mdlc_data)
return -ENODEV;
+ info = mdlc_data->default_info;
+
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
@@ -243,6 +501,47 @@ static int r8a78000_mdlc_probe(struct platform_device *pdev)
priv->dev = dev;
priv->np = np;
+ if (!IS_ENABLED(CONFIG_ARM_SCMI_POWER_DOMAIN) ||
+ !IS_ENABLED(CONFIG_COMMON_CLK_SCMI) ||
+ !IS_ENABLED(CONFIG_RESET_SCMI)) {
+ dev_dbg(dev, "SCMI pmdomain/clock/reset support is disabled\n");
+ goto fallback;
+ }
+
+ scmi = of_find_compatible_node(NULL, NULL, "arm,scmi");
+ if (!scmi) {
+ dev_dbg(dev, "Cannot find SCMI firmware node\n");
+ goto fallback;
+ }
+
+ if (!of_device_is_available(scmi)) {
+ dev_dbg(dev, "SCMI firmware node is not available\n");
+ goto fallback;
+ }
+
+ scmi_power_np = scmi_find_proto(scmi, SCMI_PROTOCOL_POWER);
+ if (!scmi_power_np) {
+ dev_dbg(dev,
+ "Cannot find SCMI power domain management protocol\n");
+ goto fallback;
+ }
+
+ scmi_clk_np = scmi_find_proto(scmi, SCMI_PROTOCOL_CLOCK);
+ if (!scmi_clk_np) {
+ dev_dbg(dev, "Cannot find SCMI clock management protocol\n");
+ goto fallback;
+ }
+
+ scmi_reset_np = scmi_find_proto(scmi, SCMI_PROTOCOL_RESET);
+ if (!scmi_reset_np) {
+ dev_dbg(dev, "Cannot find SCMI reset management protocol\n");
+ goto fallback;
+ }
+
+ priv->scmi_clk_np = scmi_clk_np;
+ info = mdlc_data->fw_info;
+
+fallback:
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res)
return -ENODEV;
@@ -257,7 +556,22 @@ static int r8a78000_mdlc_probe(struct platform_device *pdev)
return -ENODEV;
}
- priv->mod_map = info->mod_map;
+ /*
+ * We cannot do lazy look-up in r8a78000_genpd_xlate(), as that
+ * function is called with of_genpd_mutex already held.
+ */
+ priv->power_map = fill_power_map(priv, info->power_map, scmi_power_np);
+ if (IS_ERR(priv->power_map))
+ return PTR_ERR(priv->power_map);
+
+ /*
+ * We cannot do lazy look-up in r8a78000_mdlc_reset_xlate(), as that
+ * function is called with reset_list_mutex already held.
+ */
+ priv->mod_map = fill_mod_map(priv, info->mod_map,
+ of_fwnode_handle(scmi_reset_np));
+ if (IS_ERR(priv->mod_map))
+ return PTR_ERR(priv->mod_map);
scoped_guard(mutex, &r8a78000_mdlc_lock) {
hlist_add_head(&priv->link, &r8a78000_mdlc_list);
@@ -292,8 +606,8 @@ static int r8a78000_mdlc_probe(struct platform_device *pdev)
return 0;
}
-static const struct mod_map r8a78000_mdlc_perw_mod_default[] = {
- { 0x54 }, /* HSCIF0 */
+static const struct mod_map_in r8a78000_mdlc_perw_mod_default[] = {
+ { 0x54, 0, -1 }, /* HSCIF0 */
{ -1 }
};
@@ -305,10 +619,58 @@ static const struct mdlc_info r8a78000_mdlc_default[] = {
{ /* sentinel */ }
};
+static const struct mod_map_in r8a78000_mdlc_perw_mod_fw[] = {
+ { 0x40, 205, -1 }, /* SCIF0 */
+ { 0x41, 206, -1 }, /* SCIF1 */
+ { 0x42, 207, -1 }, /* SCIF3 */
+ { 0x43, 208, -1 }, /* SCIF4 */
+ { 0x44, 209, -1 }, /* I2C1 */
+ { 0x45, 210, -1 }, /* I2C2 */
+ { 0x46, 211, -1 }, /* I2C3 */
+ { 0x47, 212, -1 }, /* I2C4 */
+ { 0x48, 213, -1 }, /* I2C5 */
+ { 0x49, 214, -1 }, /* I2C6 */
+ { 0x4a, 215, -1 }, /* I2C7 */
+ { 0x4b, 216, -1 }, /* I2C8 */
+ { 0x4f, 220, 1667 }, /* MSI4 / MSOCK_PERW_BUS */
+ { 0x50, 221, 1667 }, /* MSI5 / MSOCK_PERW_BUS */
+ { 0x51, 222, 1667 }, /* MSI6 / MSOCK_PERW_BUS */
+ { 0x52, 223, 1667 }, /* MSI7 / MSOCK_PERW_BUS */
+ { 0x54, 224, -1 }, /* HSCIF0 */
+ { 0x55, 225, -1 }, /* HSCIF1 */
+ { 0x56, 226, -1 }, /* HSCIF2 */
+ { 0x57, 227, -1 }, /* HSCIF3 */
+ { -1 }
+};
+
+static const struct mod_map_in r8a78000_mdlc_scp_mod_fw[] = {
+ { 0x50, 776, 1459 }, /* MSI0 / MSO_SCP_MAIN */
+ { 0x51, 777, 1459 }, /* MSI1 / MSO_SCP_MAIN */
+ { 0x52, 778, 1459 }, /* MSI2 / MSO_SCP_MAIN */
+ { 0x53, 779, 1459 }, /* MSI3 / MSO_SCP_MAIN */
+ { -1 }
+};
+
+static const struct mdlc_info r8a78000_mdlc_fw[] = {
+ {
+ .base = 0xc05d0000 /* mdlc_perw */,
+ .mod_map = r8a78000_mdlc_perw_mod_fw,
+ }, {
+ .base = 0xc1330000 /* mdlc_scp */,
+ .mod_map = r8a78000_mdlc_scp_mod_fw,
+ },
+ { /* sentinel */ }
+};
+
+static const struct mdlc_data r8a78000_mdlc_data = {
+ .default_info = r8a78000_mdlc_default,
+ .fw_info = r8a78000_mdlc_fw,
+};
+
static const struct of_device_id r8a78000_mdlc_match[] = {
{
.compatible = "renesas,r8a78000-mdlc",
- .data = &r8a78000_mdlc_default,
+ .data = &r8a78000_mdlc_data,
},
{ /* sentinel */ }
};
--
2.43.0
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH v4 8/8] arm64: dts: renesas: ironhide: Enable SCMI devpd, sys, clock, and reset
2026-10-16 15:55 [PATCH v4 0/8] R-Car X5H Ironhide CPG/MDLC SCMI remapping support Geert Uytterhoeven
` (6 preceding siblings ...)
2026-10-16 15:55 ` [PATCH v4 7/8] pmdomain: renesas: r8a78000: Add SCMI remapping support Geert Uytterhoeven
@ 2026-10-16 15:55 ` Geert Uytterhoeven
7 siblings, 0 replies; 9+ messages in thread
From: Geert Uytterhoeven @ 2026-10-16 15:55 UTC (permalink / raw)
To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Stephen Boyd,
Brian Masney, Jerome Brunet, Ulf Hansson, Philipp Zabel,
Rafael J . Wysocki, Marek Vasut, Bartosz Golaszewski,
Konrad Dybcio, Kevin Hilman, Vinod Koul, Wolfram Sang,
Kuninori Morimoto, Laurent Pinchart, Clément Le Goffic,
Sudeep Holla, Cristian Marussi, arm-scmi, linux-arm-kernel
Cc: devicetree, linux-clk, linux-pm, linux-renesas-soc,
Geert Uytterhoeven
SCP FW SDKv4.28.0 and later for Renesas R-Car X5H Ironhide not only
implement the SCMI base protocol, but also the SCMI power domain, system
power, clock, and reset domain management protocols.
Enable support for the latter by adding the corresponding SCMI protocol
subnodes.
Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
---
v4:
- No changes,
v3:
- No changes,
v2:
- Fix misindentations,
- Add scmi_clk node, as it is no longer added in earlier commits.
---
.../boot/dts/renesas/r8a78000-ironhide.dts | 19 +++++++++++++++++++
1 file changed, 19 insertions(+)
diff --git a/arch/arm64/boot/dts/renesas/r8a78000-ironhide.dts b/arch/arm64/boot/dts/renesas/r8a78000-ironhide.dts
index b2d6b06c63881896..ade8bcd3a0e5ee8b 100644
--- a/arch/arm64/boot/dts/renesas/r8a78000-ironhide.dts
+++ b/arch/arm64/boot/dts/renesas/r8a78000-ironhide.dts
@@ -31,6 +31,25 @@ scmi: scmi {
arm,no-completion-irq;
#address-cells = <1>;
#size-cells = <0>;
+
+ scmi_devpd: protocol@11 {
+ reg = <0x11>;
+ #power-domain-cells = <1>;
+ };
+
+ scmi_sys: protocol@12 {
+ reg = <0x12>;
+ };
+
+ scmi_clk: protocol@14 {
+ reg = <0x14>;
+ #clock-cells = <1>;
+ };
+
+ scmi_reset: protocol@16 {
+ reg = <0x16>;
+ #reset-cells = <1>;
+ };
};
};
--
2.43.0
^ permalink raw reply related [flat|nested] 9+ messages in thread
end of thread, other threads:[~2026-10-16 15:56 UTC | newest]
Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-16 15:55 [PATCH v4 0/8] R-Car X5H Ironhide CPG/MDLC SCMI remapping support Geert Uytterhoeven
2026-10-16 15:55 ` [PATCH v4 1/8] clk: renesas: r8a78000: Add clk_hw to clk_map Geert Uytterhoeven
2026-10-16 15:55 ` [PATCH v4 2/8] clk: renesas: r8a78000: Add SCMI remapping support Geert Uytterhoeven
2026-10-16 15:55 ` [PATCH v4 3/8] reset: Extract __reset_control_get_from_provider() Geert Uytterhoeven
2026-10-16 15:55 ` [PATCH v4 4/8] reset: Add reset_control_get_from_provider_exclusive() Geert Uytterhoeven
2026-10-16 15:55 ` [PATCH v4 5/8] pmdomain: Make genpd_get_from_provider() return -EPROBE_DEFER Geert Uytterhoeven
2026-10-16 15:55 ` [PATCH v4 6/8] pmdomain: Make genpd_get_from_provider() public Geert Uytterhoeven
2026-10-16 15:55 ` [PATCH v4 7/8] pmdomain: renesas: r8a78000: Add SCMI remapping support Geert Uytterhoeven
2026-10-16 15:55 ` [PATCH v4 8/8] arm64: dts: renesas: ironhide: Enable SCMI devpd, sys, clock, and reset Geert Uytterhoeven
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox