[PATCH v4 1/7] clk: renesas: r9a09g077: Register SYSC regmap
From: Prabhakar
Date: Mon Aug 17 2026 - 15:29:40 EST
From: Lad Prabhakar <prabhakar.mahadev-lad.rj@xxxxxxxxxxxxxx>
Register a syscon regmap for the System Controller (SYSC) integrated into
the RZ/T2H and RZ/N2H CPG block.
Unlike traditional Renesas CPG/MSSR implementations, the RZ/T2H and RZ/N2H
CPG block also integrates the SYSC, which provides low-power management,
clock monitoring, write protection and peripheral configuration registers
shared by multiple drivers.
Implement the RZ/T2H-specific .post_init() callback to create and register
a syscon regmap covering the SYSC register space using the CPG device node.
For backward compatibility, return without registering the regmap when the
mapped resources correspond to older Device Trees that expose only the
legacy 64 KiB CPG register window.
Signed-off-by: Lad Prabhakar <prabhakar.mahadev-lad.rj@xxxxxxxxxxxxxx>
---
v3->v4:
- Dropped RZT2H_SYSC_BASE macro
- Added array of base pointers to the r9a09g077_sysc_reg struct to
support multiple SYSC regions
- Merged r9a09g077_writeable_readable_sysc0 and
r9a09g077_writeable_readable_sysc1 into a single function
that checks the SYSC block and offset to determine if the register is
readable/writable.
- Added RZT2H_CPG_REG_SIZE macro
- Updated r9a09g077_post_init() to call of_address_to_resource() to check
the size of the mapped CPG register regions
- Dropped changes from cpg_mssr_common_init
v2->v3:
- Dropped dangerous registers from writeable_readable list.
- Renamed sysc_init to post_init and updated signature to take
cpg_mssr_pub struct.
- Created a single regmap covering both SYSC register regions
instead of two separate regmaps.
- Updated commit message
v1->v2:
- Made use of for_each_child_of_node_scoped
- Moved sysc_init() to the end of the probe function
---
drivers/clk/renesas/Kconfig | 2 +
drivers/clk/renesas/r9a09g077-cpg.c | 140 +++++++++++++++++++++++++
drivers/clk/renesas/renesas-cpg-mssr.c | 3 +
drivers/clk/renesas/renesas-cpg-mssr.h | 1 +
4 files changed, 146 insertions(+)
diff --git a/drivers/clk/renesas/Kconfig b/drivers/clk/renesas/Kconfig
index 17b95a2e96fb..46b929e9872b 100644
--- a/drivers/clk/renesas/Kconfig
+++ b/drivers/clk/renesas/Kconfig
@@ -223,11 +223,13 @@ config CLK_R9A09G077
bool "RZ/T2H clock support" if COMPILE_TEST
select CLK_RENESAS_CPG_MSSR
select CLK_RZV2H_CPG_LIB
+ select MFD_SYSCON
config CLK_R9A09G087
bool "RZ/N2H clock support" if COMPILE_TEST
select CLK_RENESAS_CPG_MSSR
select CLK_RZV2H_CPG_LIB
+ select MFD_SYSCON
config CLK_SH73A0
bool "SH-Mobile AG5 clock support" if COMPILE_TEST
diff --git a/drivers/clk/renesas/r9a09g077-cpg.c b/drivers/clk/renesas/r9a09g077-cpg.c
index 5640c2035e5a..0495e3206973 100644
--- a/drivers/clk/renesas/r9a09g077-cpg.c
+++ b/drivers/clk/renesas/r9a09g077-cpg.c
@@ -7,6 +7,7 @@
*/
#include <linux/bitfield.h>
+#include <linux/bits.h>
#include <linux/clk-provider.h>
#include <linux/clk/renesas.h>
#include <linux/device.h>
@@ -15,7 +16,10 @@
#include <linux/iopoll.h>
#include <linux/kernel.h>
#include <linux/math.h>
+#include <linux/mfd/syscon.h>
#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/regmap.h>
#include <linux/types.h>
#include <linux/units.h>
@@ -87,6 +91,20 @@ MODULE_IMPORT_NS("RZV2H_CPG");
#define CPG_PLL_MON(x) ((x) - 0x10)
#define CPG_PLL_MON_LOCK BIT(0)
+/* Size of each mapped CPG/MSSR register region (base0, base1) */
+#define RZT2H_CPG_REG_SIZE 0x20000
+/* This is the size of two SYSC regions combined */
+#define RZT2H_SYSC_SIZE 0x20000
+#define RZT2H_SYSC_OFFSET 0x10000
+#define RZT2H_SYSC_BLOCK_MASK BIT(16)
+#define RZT2H_SYSC_OFFSET_MASK GENMASK(15, 0)
+#define RZT2H_SYSC_BLOCK(x) FIELD_GET(RZT2H_SYSC_BLOCK_MASK, x)
+#define RZT2H_SYSC_REG_OFFSET(x) FIELD_GET(RZT2H_SYSC_OFFSET_MASK, x)
+
+struct r9a09g077_sysc_reg {
+ void __iomem *base[2];
+};
+
enum rzt2h_clk_types {
CLK_TYPE_RZT2H_DIV = CLK_TYPE_CUSTOM, /* Clock with divider */
CLK_TYPE_RZT2H_MUX, /* Clock with clock source selector */
@@ -875,6 +893,126 @@ r9a09g077_cpg_clk_register(struct device *dev, const struct cpg_core_clk *core,
}
}
+static int r9a09g077_regmap_read(void *context, unsigned int reg, unsigned int *val)
+{
+ struct r9a09g077_sysc_reg *sysc = context;
+ void __iomem *base = sysc->base[RZT2H_SYSC_BLOCK(reg)];
+
+ *val = readl(base + RZT2H_SYSC_REG_OFFSET(reg));
+
+ return 0;
+}
+
+static int r9a09g077_regmap_write(void *context, unsigned int reg, unsigned int val)
+{
+ struct r9a09g077_sysc_reg *sysc = context;
+ void __iomem *base = sysc->base[RZT2H_SYSC_BLOCK(reg)];
+
+ writel(val, base + RZT2H_SYSC_REG_OFFSET(reg));
+
+ return 0;
+}
+
+static const struct regmap_bus r9a09g077_sys_regmap_bus = {
+ .reg_write = r9a09g077_regmap_write,
+ .reg_read = r9a09g077_regmap_read,
+};
+
+static bool r9a09g077_writeable_readable_sysc(struct device *dev, unsigned int reg)
+{
+ unsigned int offset = RZT2H_SYSC_REG_OFFSET(reg);
+
+ switch (RZT2H_SYSC_BLOCK(reg)) {
+ case 0:
+ switch (offset) {
+ /* ELOPA/B and GTIOCSEL */
+ case 0x0000 ... 0x0008:
+ /* Encoder config */
+ case 0x1000 ... 0x1164:
+ /* PCIe config */
+ case 0x2000 ... 0x2024:
+ case 0x2030 ... 0x2054:
+ case 0x2060:
+ /* xSPI config */
+ case 0x3000 ... 0x300C:
+ case 0x3100 ... 0x310C:
+ /* MD_MON */
+ case 0x4100:
+ /* PRCRN */
+ case 0x4200:
+ return true;
+
+ default:
+ return false;
+ }
+ case 1:
+ switch (offset) {
+ /* WDTDCRm */
+ case 0x5100 ... 0x5114:
+ /* PRCRS */
+ case 0x6000:
+ return true;
+
+ default:
+ return false;
+ }
+ default:
+ return false;
+ }
+}
+
+static int r9a09g077_post_init(struct device *dev, struct cpg_mssr_pub *pub)
+{
+ struct regmap_config *regmap_cfg __free(kfree) = kzalloc_obj(*regmap_cfg);
+ struct r9a09g077_sysc_reg *sysc_reg;
+ struct regmap *regmap;
+ struct resource res;
+ int ret;
+
+ /*
+ * Return early if the SYSC sizes are not as expected, for backwards
+ * compatibility with older device trees that only expose the 64 KiB
+ * CPG register window.
+ */
+ ret = of_address_to_resource(dev->of_node, 0, &res);
+ if (ret)
+ return ret;
+ if (resource_size(&res) != RZT2H_CPG_REG_SIZE)
+ return 0;
+
+ ret = of_address_to_resource(dev->of_node, 1, &res);
+ if (ret)
+ return ret;
+ if (resource_size(&res) != RZT2H_CPG_REG_SIZE)
+ return 0;
+
+ if (!regmap_cfg)
+ return -ENOMEM;
+
+ sysc_reg = devm_kzalloc(dev, sizeof(*sysc_reg), GFP_KERNEL);
+ if (!sysc_reg)
+ return -ENOMEM;
+
+ /* Only allow access in the SYSC regions */
+ sysc_reg->base[0] = pub->base0 + RZT2H_SYSC_OFFSET;
+ sysc_reg->base[1] = pub->base1 + RZT2H_SYSC_OFFSET;
+
+ regmap_cfg->name = "rzt2h_sysc";
+ regmap_cfg->reg_bits = 32;
+ regmap_cfg->reg_stride = 4;
+ regmap_cfg->val_bits = 32;
+ regmap_cfg->fast_io = true;
+ regmap_cfg->max_register = RZT2H_SYSC_SIZE - 1;
+ regmap_cfg->readable_reg = r9a09g077_writeable_readable_sysc;
+ regmap_cfg->writeable_reg = r9a09g077_writeable_readable_sysc;
+
+ regmap = devm_regmap_init(dev, &r9a09g077_sys_regmap_bus, sysc_reg, regmap_cfg);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ return of_syscon_register_regmap(dev->of_node, regmap);
+}
+
const struct cpg_mssr_info r9a09g077_cpg_mssr_info = {
/* Core Clocks */
.core_clks = r9a09g077_core_clks,
@@ -889,4 +1027,6 @@ const struct cpg_mssr_info r9a09g077_cpg_mssr_info = {
.reg_layout = CLK_REG_LAYOUT_RZ_T2H,
.cpg_clk_register = r9a09g077_cpg_clk_register,
+
+ .post_init = r9a09g077_post_init,
};
diff --git a/drivers/clk/renesas/renesas-cpg-mssr.c b/drivers/clk/renesas/renesas-cpg-mssr.c
index 80f4403ea2ba..f2feac1cc74f 100644
--- a/drivers/clk/renesas/renesas-cpg-mssr.c
+++ b/drivers/clk/renesas/renesas-cpg-mssr.c
@@ -1414,6 +1414,9 @@ static int __init cpg_mssr_probe(struct platform_device *pdev)
error = cpg_mssr_reset_controller_register(priv);
+ if (!error && info->post_init)
+ error = info->post_init(priv->dev, &priv->pub);
+
reserve_exit:
cpg_mssr_reserved_exit(priv);
diff --git a/drivers/clk/renesas/renesas-cpg-mssr.h b/drivers/clk/renesas/renesas-cpg-mssr.h
index ad11ab5f0069..5bef6f34dcb2 100644
--- a/drivers/clk/renesas/renesas-cpg-mssr.h
+++ b/drivers/clk/renesas/renesas-cpg-mssr.h
@@ -178,6 +178,7 @@ struct cpg_mssr_info {
/* Callbacks */
int (*init)(struct device *dev);
+ int (*post_init)(struct device *dev, struct cpg_mssr_pub *pub);
struct clk *(*cpg_clk_register)(struct device *dev,
const struct cpg_core_clk *core,
const struct cpg_mssr_info *info,
--
2.43.0