Hi Thierry, Thanks for your patch! On Thu, 3 Apr 2025 at 23:30, Thierry Bultel <thierry.bultel.yh@xxxxxxxxxxxxxx> wrote: > RZ/T2H has 2 registers blocks at different addresses. register > The clock tree has configurable dividers and mux selectors. > Add these new clock types, new register layout type, and > registration code for mux and div in registration callback. > > Signed-off-by: Thierry Bultel <thierry.bultel.yh@xxxxxxxxxxxxxx> > --- a/drivers/clk/renesas/Makefile > +++ b/drivers/clk/renesas/Makefile > @@ -31,6 +31,7 @@ obj-$(CONFIG_CLK_R8A779A0) += r8a779a0-cpg-mssr.o > obj-$(CONFIG_CLK_R8A779F0) += r8a779f0-cpg-mssr.o > obj-$(CONFIG_CLK_R8A779G0) += r8a779g0-cpg-mssr.o > obj-$(CONFIG_CLK_R8A779H0) += r8a779h0-cpg-mssr.o > +obj-$(CONFIG_CLK_R9A09G077) += r9a09g077-cpg-mssr.o Please keep the list sorted. > obj-$(CONFIG_CLK_R9A06G032) += r9a06g032-clocks.o > obj-$(CONFIG_CLK_R9A07G043) += r9a07g043-cpg.o > obj-$(CONFIG_CLK_R9A07G044) += r9a07g044-cpg.o > diff --git a/drivers/clk/renesas/r9a09g077-cpg-mssr.c b/drivers/clk/renesas/r9a09g077-cpg-mssr.c > new file mode 100644 > index 000000000000..b67dbf5d59d8 > --- /dev/null > +++ b/drivers/clk/renesas/r9a09g077-cpg-mssr.c > @@ -0,0 +1,238 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * r9a09g077 Clock Pulse Generator / Module Standby and Software Reset > + * > + * Copyright (C) 2025 Renesas Electronics Corp. > + * > + */ > + > +#include <linux/bitfield.h> > +#include <linux/clk-provider.h> > +#include <linux/device.h> > +#include <linux/init.h> > +#include <linux/kernel.h> > + > +#include <dt-bindings/clock/renesas,r9a09g077-cpg-mssr.h> > +#include "renesas-cpg-mssr.h" > + > +#define SCKCR 0x00 > +#define SCKCR2 0x04 Please add comments to indicate whether an offset is against base0 (e.g. SCKCR) or base1 (e.g. SCKCR2). Perhaps encode it in a high-bit, like for the MSTPCRx registers, instead of storing it separately in .sel_base, which is more error-prone? > +#define SCKCR3 0x08 > +#define SCKCR4 0x0C > +#define PMSEL 0x10 > +#define PMSEL_PLL0 BIT(0) > +#define PMSEL_PLL2 BIT(2) > +#define PMSEL_PLL3 BIT(3) > +#define PLL0EN BIT(0) > +#define PLL2EN BIT(0) > +#define PLL3EN BIT(0) These 6 bit definitions don't match the documentation (and are unused?) > +#define PLL0MON 0x20 > +#define PLL0EN_REG 0x30 Why the "_REG"-suffix? Unused, anyway. > +#define PLL0_SSC_CTR 0x34 > +#define PLL1MON 0x40 > +#define LOCOCR 0x70 > +#define HIZCTRLEN 0x80 > +#define PLL2MON 0x90 > +#define PLL2EN_REG 0xA0 > +#define PLL2_SSC_CTR 0xAC > +#define PLL3MON 0xB0 > +#define PLL3EN_REG 0xC0 > +#define PLL3_VCO_CTR0 0xC4 > +#define PLL3_VCO_CTR1 0xC8 > +#define PLL4MON 0xD0 > +#define PHYSEL BIT(21) SCKCR_PHYSEL? So it would belong under SCKCR. But is unused, as you have the DIVETHPHY macro. > + > +#define MRCTLA 0x240 > +#define MRCTLE 0x250 > +#define MRCTLI 0x260 > +#define MRCTLM 0x270 Unused, these should end up in srcr_for_rzt2h[] in renesas-cpg-mssr.c when adding reset controller support for RZ/T2H. Until then, the call to cpg_mssr_reset_controller_register() in cpg_mssr_probe() should be skipped when priv->reg_layout == CLK_REG_LAYOUT_RZ_T2H. > + > +#define DDIV_PACK(offset, bitpos, size) \ > + (((offset) << 20) | ((bitpos) << 12) | ((size) << 8)) Indented by 2 TABs... > + > +#define DIVCA55 DDIV_PACK(SCKCR2, 8, 4) This is not correct: these 4 bits are not a single bit mask, but 4 individual bits, one for each CPU core. Oh, due to dtable_1_2[] just having values "0" and "15", all 4 CPU cores are always clocked at the same rate. Hmm..... > +#define DIVCA55S DDIV_PACK(SCKCR2, 12, 1) > +#define DIVCR520 DDIV_PACK(SCKCR2, 2, 2) > +#define DIVCR521 DDIV_PACK(SCKCR2, 0, 2) Surprisingly, you do handle the 2 CR52 cores individually ;-) Unused, anyway... > +#define DIVLCDC DDIV_PACK(SCKCR3, 20, 3) Should be "4" instead of "3". > +#define DIVCKIO DDIV_PACK(SCKCR, 16, 3) > +#define DIVETHPHY DDIV_PACK(SCKCR, 21, 1) > +#define DIVCANFD DDIV_PACK(SCKCR, 20, 1) > +#define DIVSPI0 DDIV_PACK(SCKCR3, 0, 2) > +#define DIVSPI1 DDIV_PACK(SCKCR3, 2, 2) > +#define DIVSPI2 DDIV_PACK(SCKCR3, 4, 2) > +#define DIVSPI3 DDIV_PACK(SCKCR2, 16, 2) Perhaps sort these definitions by SCKCR* register? > + > +#define SEL_PLL_PACK(offset, bitpos, size) \ > + (((offset) << 20) | ((bitpos) << 12) | ((size) << 8)) ... indented by 1 TAB. Please match them. > + > +#define SEL_PLL SEL_PLL_PACK(SCKCR, 22, 1) > + > +#define GET_SHIFT(val) FIELD_GET(GENMASK(19, 12), val) > +#define GET_WIDTH(val) FIELD_GET(GENMASK(11, 8), val) > +#define GET_REG_OFFSET(val) FIELD_GET(GENMASK(31, 20), val) Please use consistent naming: SHIFT ~ bitpos WIDTH ~ size REG_OFFSET ~ offset If you would use FIELD_*() helpers for both, you could write the above as e.g. #define OFFSET_MASK GENMASK(31, 20) #define SHIFT_MASK GENMASK(19, 12) #define WIDTH_MASK GENMASK(11, 8) #define SEL_PLL_PACK(offset, shift, width) \ (FIELD_PREP_CONST(OFFSET_MASK, (offset)) | \ FIELD_PREP_CONST(SHIFT_MASK, (shift)) | \ FIELD_PREP_CONST(WIDTH_MASK, (width))) \ #define GET_SHIFT(val) FIELD_GET(SHIFT_MASK, val) #define GET_WIDTH(val) FIELD_GET(WIDTH_MASK val) #define GET_REG_OFFSET(val) FIELD_GET(OFFSET_MASK, val) > + > +enum clk_ids { > + /* Core Clock Outputs exported to DT */ > + LAST_DT_CORE_CLK = R9A09G077_LCDC_CLKD, > + > + /* External Input Clocks */ > + CLK_EXTAL, > + CLK_LOCO, This is an internally-generated clock. > + > + /* Internal Core Clocks */ > + CLK_PLL0, > + CLK_PLL1, > + CLK_PLL2, > + CLK_PLL3, > + CLK_PLL4, > + CLK_SEL_PLL0, > + CLK_SEL_CLK_PLL0, > + CLK_SEL_PLL1, > + CLK_SEL_CLK_PLL1, > + CLK_SEL_PLL2, > + CLK_SEL_CLK_PLL2, > + CLK_SEL_PLL4, > + CLK_SEL_CLK_PLL4, > + CLK_SEL_CLK_SRC, > + CLK_SEL_EXTAL, > + CLK_SEL_LOCO, > + CLK_PLL3_INPUT, > + > + /* Module Clocks */ > + MOD_CLK_BASE, > +}; > + > +static const struct clk_div_table dtable_1_2[] = { > + {0, 2}, > + {15, 1}, > + {0, 0}, > +}; > + > +/* Mux clock tables */ > +static const char * const sel_clk_pll0[] = { ".sel_loco", ".sel_pll0" }; > +static const char * const sel_clk_pll1[] = { ".sel_loco", ".sel_pll1" }; > +static const char * const sel_clk_pll4[] = { ".sel_loco", ".sel_pll4" }; > + > +static const struct cpg_core_clk r9a09g077_core_clks[] __initconst = { > + /* External Clock Inputs */ > + DEF_INPUT("extal", CLK_EXTAL), > + DEF_INPUT("loco", CLK_LOCO), DEF_RATE(), as this is an internally-generated clock. > + > + /* Internal Core Clocks */ > + DEF_FIXED(".pll0", CLK_PLL0, CLK_EXTAL, 1, 48), > + DEF_FIXED(".pll1", CLK_PLL1, CLK_EXTAL, 1, 40), > + DEF_FIXED(".pll4", CLK_PLL4, CLK_EXTAL, 1, 96), > + DEF_FIXED(".sel_pll0", CLK_SEL_PLL0, CLK_PLL0, 1, 1), This is the unimplemented selector to bypass the PLL, right? > + DEF_MUX(".sel_clk_pll0", CLK_SEL_CLK_PLL0, SEL_PLL, > + sel_clk_pll0, ARRAY_SIZE(sel_clk_pll0), 0, CLK_MUX_READ_ONLY), > + DEF_FIXED(".sel_pll1", CLK_SEL_PLL1, CLK_PLL1, 1, 1), > + DEF_MUX(".sel_clk_pll1", CLK_SEL_CLK_PLL1, SEL_PLL, > + sel_clk_pll1, ARRAY_SIZE(sel_clk_pll1), 0, CLK_MUX_READ_ONLY), > + DEF_FIXED(".sel_pll4", CLK_SEL_PLL4, CLK_PLL4, 1, 1), > + DEF_MUX(".sel_clk_pll4", CLK_SEL_CLK_PLL4, SEL_PLL, > + sel_clk_pll4, ARRAY_SIZE(sel_clk_pll4), 0, CLK_MUX_READ_ONLY), > + > + /* Core output clk */ > + DEF_DIV("CA55", R9A09G077_CA55, CLK_SEL_CLK_PLL0, DIVCA55, > + dtable_1_2, CLK_DIVIDER_HIWORD_MASK, 1), > + DEF_FIXED("PCLKM", R9A09G077_PCLKM, CLK_SEL_CLK_PLL1, 8, 1), > + DEF_FIXED("PCLKGPTL", R9A09G077_PCLKGPTL, CLK_SEL_CLK_PLL1, 2, 1), Please sort these two alphabetically. > +}; > + > +static const struct mssr_mod_clk r9a09g077_mod_clks[] __initconst = { > + DEF_MOD("sci0", 108, R9A09G077_PCLKM), Shouldn't that be 8 instead of 108? Using R9A09G077_PCLKM as the parent is a temporary simplification, right? > +}; > + > +static struct clk * __init > +r9a09g077_cpg_div_clk_register(struct device *dev, > + const struct cpg_core_clk *core, > + void __iomem *base, > + struct cpg_mssr_pub *pub) Fits on one line less when wrapped. > +{ > + const struct clk *parent; > + const char *parent_name; > + struct clk_hw *clk_hw; > + > + parent = pub->clks[core->parent]; > + > + if (IS_ERR(parent)) > + return ERR_CAST(parent); > + > + parent_name = __clk_get_name(parent); > + > + if (core->dtable) > + clk_hw = clk_hw_register_divider_table(dev, core->name, > + parent_name, 0, > + base + GET_REG_OFFSET(core->conf), > + GET_SHIFT(core->conf), > + GET_WIDTH(core->conf), > + core->flag, > + core->dtable, > + &pub->rmw_lock); > + else > + clk_hw = clk_hw_register_divider(dev, core->name, > + parent_name, 0, > + base + GET_REG_OFFSET(core->conf), > + GET_SHIFT(core->conf), > + GET_WIDTH(core->conf), > + core->flag, &pub->rmw_lock); > + > + if (IS_ERR(clk_hw)) > + return ERR_CAST(clk_hw); > + > + return clk_hw->clk; > + > +} > + > +static struct clk * __init > +r9a09g077_cpg_mux_clk_register(struct device *dev, > + const struct cpg_core_clk *core, > + void __iomem *base, > + struct cpg_mssr_pub *pub) Fits on one line less when wrapped. > +{ > + struct clk_hw *clk_hw; > + > + clk_hw = devm_clk_hw_register_mux(dev, core->name, > + core->parent_names, core->num_parents, > + core->flag, > + base + GET_REG_OFFSET(core->conf), > + GET_SHIFT(core->conf), > + GET_WIDTH(core->conf), > + core->mux_flags, &pub->rmw_lock); Missing error check for clk_hw. > + return clk_hw->clk; > +} > + > +static struct clk * __init > +r9a09g077_cpg_clk_register(struct device *dev, > + const struct cpg_core_clk *core, > + const struct cpg_mssr_info *info, > + struct cpg_mssr_pub *pub) Fits on one line less when wrapped. > +{ > + void __iomem *base = core->sel_base ? pub->base1 : pub->base0; > + > + switch (core->type) { > + case CLK_TYPE_DIV: > + return r9a09g077_cpg_div_clk_register(dev, core, base, pub); > + case CLK_TYPE_MUX: > + return r9a09g077_cpg_mux_clk_register(dev, core, base, pub); > + default: > + return ERR_PTR(-EINVAL); > + } > +} > + > +const struct cpg_mssr_info r9a09g077_cpg_mssr_info = { > + /* Core Clocks */ > + .core_clks = r9a09g077_core_clks, > + .num_core_clks = ARRAY_SIZE(r9a09g077_core_clks), > + .last_dt_core_clk = LAST_DT_CORE_CLK, > + .num_total_core_clks = MOD_CLK_BASE, > + > + /* Module Clocks */ > + .mod_clks = r9a09g077_mod_clks, > + .num_mod_clks = ARRAY_SIZE(r9a09g077_mod_clks), > + .num_hw_mod_clks = 12 * 32, "14 * 32", to account for the two gaps in the MSTPCRx registers? > + > + .reg_layout = CLK_REG_LAYOUT_RZ_T2H, > + .cpg_clk_register = r9a09g077_cpg_clk_register, > +}; > diff --git a/drivers/clk/renesas/renesas-cpg-mssr.c b/drivers/clk/renesas/renesas-cpg-mssr.c > index 4bdfa4f65ab4..123bc1558e53 100644 > --- a/drivers/clk/renesas/renesas-cpg-mssr.c > +++ b/drivers/clk/renesas/renesas-cpg-mssr.c > @@ -79,6 +79,27 @@ static const u16 mstpcr_for_gen4[] = { > 0x2D60, 0x2D64, 0x2D68, 0x2D6C, 0x2D70, 0x2D74, > }; > > +/* > + * Module Stop Control Register (RZ/T2H) > + * RZ/T2H has 2 registers blocks, > + * Bit 12 is used to differentiate them > + */ > + > +#define RZT2H_MSTPCR_BLOCK_SHIFT 12 > +#define RZT2H_MSTPCR_OFFSET_MASK GENMASK(11, 0) > +#define RZT2H_MSTPCR(block, offset) (((block) << RZT2H_MSTPCR_BLOCK_SHIFT) & \ "|" instead of "&" > + ((offset) & RZT2H_MSTPCR_OFFSET_MASK)) > + > +#define RZT2H_MSTPCR_BLOCK(x) ((x) >> RZT2H_MSTPCR_BLOCK_SHIFT) > +#define RZT2H_MSTPCR_OFFSET(x) ((x) & RZT2H_MSTPCR_OFFSET_MASK) > + > +static const u16 mstpcr_for_rzt2h[] = { > + RZT2H_MSTPCR(0, 0x300), RZT2H_MSTPCR(0, 0x304), RZT2H_MSTPCR(0, 0x308), > + RZT2H_MSTPCR(0, 0x30c), RZT2H_MSTPCR(0, 0x310), RZT2H_MSTPCR(1, 0x318), > + RZT2H_MSTPCR(1, 0x320), RZT2H_MSTPCR(0, 0x324), RZT2H_MSTPCR(0, 0x328), > + RZT2H_MSTPCR(0, 0x32c), RZT2H_MSTPCR(0, 0x330), RZT2H_MSTPCR(1, 0x334), Missing holes for non-existent MSTPCRF (0x314) and MSTPCRH (0x31c). > +}; > + > /* > * Standby Control Register offsets (RZ/A) > * Base address is FRQCR register > @@ -227,7 +257,8 @@ static int cpg_mstp_clock_endisable(struct clk_hw *hw, bool enable) > > spin_unlock_irqrestore(&priv->pub.rmw_lock, flags); > > - if (!enable || priv->reg_layout == CLK_REG_LAYOUT_RZ_A) > + if (!enable || priv->reg_layout == CLK_REG_LAYOUT_RZ_A || > + priv->reg_layout == CLK_REG_LAYOUT_RZ_T2H) Please align the continuation of the if-condition with the start of the condition above. > return 0; > > error = readl_poll_timeout_atomic(priv->pub.base0 + priv->status_regs[reg], > @@ -258,6 +289,12 @@ static int cpg_mstp_clock_is_enabled(struct clk_hw *hw) > > if (priv->reg_layout == CLK_REG_LAYOUT_RZ_A) > value = readb(priv->pub.base0 + priv->control_regs[reg]); > + else if (priv->reg_layout == CLK_REG_LAYOUT_RZ_T2H) { Please use curly braces in all branches if you need them in at least one branch... > + void __iomem *addr = > + cpg_rzt2h_addr_from_offset(hw, > + priv->control_regs[reg]); > + value = readw(addr); Aren't the MSTPCR* registers 32-bit wide? ... however, you can avoid the curly braces by moving the register read into the helper function. > + } > else > value = readl(priv->pub.base0 + priv->status_regs[reg]); > > --- a/drivers/clk/renesas/renesas-cpg-mssr.h > +++ b/drivers/clk/renesas/renesas-cpg-mssr.h > @@ -22,6 +22,8 @@ > struct cpg_core_clk { > /* Common */ > const char *name; > + const char * const *parent_names; > + const struct clk_div_table *dtable; Please move them below, as they are type-specific. I think they are never used together, so you can reduce kernel size by combining them into an anonymous union. > unsigned int id; > unsigned int type; > /* Depending on type */ > @@ -29,18 +31,26 @@ struct cpg_core_clk { > unsigned int div; > unsigned int mult; > unsigned int offset; > + unsigned int conf; u32 > + int flag; u8? (or u16, cfr. clk_divider.flags? see below) > + int mux_flags; u8 (cfr. clk_mux.flags) > + int num_parents; u8? > + int sel_base; bool? > +}; FTR, your additions as-is would have increased the size of each core clock on arm64 by 32 bytes. Note to self: use unions for every core clock type. > /** > * struct cpg_mssr_pub - Private data shared with > * device-specific clk registration code > * > * @base0: CPG/MSSR register block base0 address > + * @base1: CPG/MSSR register block base1 address > * @rmw_lock: protects RMW register accesses > * @notifiers: Notifier chain to save/restore clock state for system resume > * @clks: pointer to clocks > */ > struct cpg_mssr_pub { > void __iomem *base0; > + void __iomem *base1; > struct raw_notifier_head notifiers; > spinlock_t rmw_lock; > struct clk **clks; > @@ -53,6 +63,8 @@ enum clk_types { > CLK_TYPE_DIV6P1, /* DIV6 Clock with 1 parent clock */ > CLK_TYPE_DIV6_RO, /* DIV6 Clock read only with extra divisor */ > CLK_TYPE_FR, /* Fixed Rate Clock */ > + CLK_TYPE_DIV, /* Clock with divider */ > + CLK_TYPE_MUX, /* Clock with clock source selector */ Please move these into a new enum in r9a09g077-cpg-mssr.c, starting from CLK_TYPE_CUSTOM, as they are specific to RZ/T2H. > > /* Custom definitions start here */ > CLK_TYPE_CUSTOM, > @@ -73,6 +85,15 @@ enum clk_types { > DEF_BASE(_name, _id, CLK_TYPE_DIV6_RO, _parent, .offset = _offset, .div = _div, .mult = 1) > #define DEF_RATE(_name, _id, _rate) \ > DEF_TYPE(_name, _id, CLK_TYPE_FR, .mult = _rate) > +#define DEF_DIV(_name, _id, _parent, _conf, _dtable, _flag, _sel_base) \ > + DEF_TYPE(_name, _id, CLK_TYPE_DIV, .conf = _conf, \ > + .parent = _parent, .dtable = _dtable, .flag = _flag, .sel_base = _sel_base) > +#define DEF_MUX(_name, _id, _conf, _parent_names, _num_parents, _flag, \ > + _mux_flags) \ > + DEF_TYPE(_name, _id, CLK_TYPE_MUX, .conf = _conf, \ > + .parent_names = _parent_names, .num_parents = _num_parents, \ > + .flag = _flag, .mux_flags = _mux_flags) The passed _flag parameter is always 0? So when non-zero, cpg_core_clk.flag is always clk_divider.flags. Please move both definitions to r9a09g077-cpg-mssr.c, as they are specific to RZ/T2H. > + > > /* > * Definitions of Module Clocks Gr{oetje,eeting}s, Geert -- Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@xxxxxxxxxxxxxx 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