[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <CAMuHMdVpiZ+F0TMbLm000M_Scwozj2-SHPrUwTHqFKckVcmufQ@mail.gmail.com>
Date: Thu, 17 Apr 2025 20:45:33 +0200
From: Geert Uytterhoeven <geert@...ux-m68k.org>
To: Thierry Bultel <thierry.bultel.yh@...renesas.com>
Cc: thierry.bultel@...atsea.fr, linux-renesas-soc@...r.kernel.org,
paul.barker.ct@...renesas.com, linux-kernel@...r.kernel.org,
linux-clk@...r.kernel.org
Subject: Re: [PATCH v7 06/13] clk: renesas: Add support for R9A09G077 SoC
Hi Thierry,
Thanks for your patch!
On Thu, 3 Apr 2025 at 23:30, Thierry Bultel
<thierry.bultel.yh@...renesas.com> 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@...renesas.com>
> --- 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@...ux-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
Powered by blists - more mailing lists