linux-gpio.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Geert Uytterhoeven <geert@linux-m68k.org>
To: Claudiu <claudiu.beznea@tuxon.dev>
Cc: mturquette@baylibre.com, sboyd@kernel.org, robh+dt@kernel.org,
	krzysztof.kozlowski+dt@linaro.org, conor+dt@kernel.org,
	linus.walleij@linaro.org, gregkh@linuxfoundation.org,
	jirislaby@kernel.org, magnus.damm@gmail.com,
	catalin.marinas@arm.com, will@kernel.org,
	quic_bjorande@quicinc.com, konrad.dybcio@linaro.org,
	arnd@arndb.de, neil.armstrong@linaro.org,
	prabhakar.mahadev-lad.rj@bp.renesas.com,
	biju.das.jz@bp.renesas.com, linux-renesas-soc@vger.kernel.org,
	linux-clk@vger.kernel.org, devicetree@vger.kernel.org,
	linux-kernel@vger.kernel.org, linux-gpio@vger.kernel.org,
	linux-serial@vger.kernel.org,
	linux-arm-kernel@lists.infradead.org
Subject: Re: [PATCH v2 11/28] clk: renesas: rzg2l: add a divider clock for RZ/G3S
Date: Wed, 4 Oct 2023 14:30:30 +0200	[thread overview]
Message-ID: <CAMuHMdX8wPNr4LsPJR7zk3Ktb8NnqcSMUEjsAURc2NJ_dpiDHw@mail.gmail.com> (raw)
In-Reply-To: <20230929053915.1530607-12-claudiu.beznea@bp.renesas.com>

Hi Claudiu,

On Fri, Sep 29, 2023 at 7:39 AM Claudiu <claudiu.beznea@tuxon.dev> wrote:
> From: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
>
> Add a divider clock driver for RZ/G3S. This will be used in RZ/G3S
> by SDHI, SPI, OCTA, I, I2, I3, P0, P1, P2, P3 core clocks.
> The divider has some limitation for SDHI and OCTA clocks:
> - SD div cannot be 1 if parent rate is 800MHz
> - OCTA div cannot be 1 if parent rate is 400MHz
> For these clocks a notifier could be registered from platform specific
> clock driver and proper actions are taken before clock rate is changed,
> if needed.
>
> Signed-off-by: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
> ---
>
> Changes in v2:
> - removed DIV_NOTIF macro

Thanks for the update!

> --- a/drivers/clk/renesas/rzg2l-cpg.c
> +++ b/drivers/clk/renesas/rzg2l-cpg.c
> @@ -91,6 +91,22 @@ struct sd_mux_hw_data {
>
>  #define to_sd_mux_hw_data(_hw) container_of(_hw, struct sd_mux_hw_data, hw_data)
>
> +/**
> + * struct div_hw_data - divider clock hardware data
> + * @hw_data: clock hw data
> + * @dtable: pointer to divider table
> + * @invalid_rate: invalid rate for divider
> + * @width: divider width
> + */
> +struct div_hw_data {
> +       struct clk_hw_data hw_data;
> +       const struct clk_div_table *dtable;
> +       unsigned long invalid_rate;
> +       u32 width;
> +};
> +
> +#define to_div_hw_data(_hw)    container_of(_hw, struct div_hw_data, hw_data)
> +
>  struct rzg2l_pll5_param {
>         u32 pl5_fracin;
>         u8 pl5_refdiv;
> @@ -200,6 +216,54 @@ int rzg2l_cpg_sd_mux_clk_notifier(struct notifier_block *nb, unsigned long event
>         return ret;
>  }
>
> +int rzg3s_cpg_div_clk_notifier(struct notifier_block *nb, unsigned long event,
> +                              void *data)
> +{
> +       struct clk_notifier_data *cnd = data;
> +       struct clk_hw *hw = __clk_get_hw(cnd->clk);
> +       struct clk_hw_data *clk_hw_data = to_clk_hw_data(hw);
> +       struct div_hw_data *div_hw_data = to_div_hw_data(clk_hw_data);
> +       struct rzg2l_cpg_priv *priv = clk_hw_data->priv;
> +       u32 off = GET_REG_OFFSET(clk_hw_data->conf);
> +       u32 shift = GET_SHIFT(clk_hw_data->conf);
> +       u32 bitmask = GENMASK(GET_WIDTH(clk_hw_data->conf) - 1, 0);
> +       unsigned long flags;
> +       int ret = 0;
> +       u32 val;
> +
> +       if (event != PRE_RATE_CHANGE || !div_hw_data->invalid_rate ||
> +           div_hw_data->invalid_rate % cnd->new_rate)
> +               return 0;

NOTIFY_DONE for event != PRE_RATE_CHANGE
NOTIFY_OK for the other cases

> +
> +       spin_lock_irqsave(&priv->rmw_lock, flags);
> +
> +       val = readl(priv->base + off);
> +       val >>= shift;
> +       val &= bitmask;
> +
> +       /*
> +        * There are different constraints for the user of this notifiers as follows:
> +        * 1/ SD div cannot be 1 (val == 0) if parent rate is 800MHz
> +        * 2/ OCTA div cannot be 1 (val == 0) if parent rate is 400MHz
> +        * As SD can have only one parent having 800MHz and OCTA div can have
> +        * only one parent having 400MHz we took into account the parent rate
> +        * at the beginning of function (by checking invalid_rate % new_rate).
> +        * Now it is time to check the hardware divider and update it accordingly.
> +        */
> +       if (!val) {
> +               writel(((bitmask << shift) << 16) | BIT(shift), priv->base + off);

Haven't you exchanged the (single) write-enable bit and the (multi-bit)
division ratio setting?  According to the docs, the write-enable bit
is at 16 + shift, while the division ratio is at shift.

Also, using bitmask as the division ratio means the maximum value
that fits in the bitfield, which would be a prohibited setting in case
of DIV_OCTA.

Now, looking at rzg3s_div_clk_set_rate() below, perhaps you just wanted
to set the ratio to value to 1, but used the wrong size for bitmask?

> +               /* Wait for the update done. */
> +               ret = rzg2l_cpg_wait_clk_update_done(priv->base, clk_hw_data->sconf);
> +       }
> +
> +       spin_unlock_irqrestore(&priv->rmw_lock, flags);
> +
> +       if (ret)
> +               dev_err(priv->dev, "Failed to downgrade the div\n");

and return NOTIFY_BAD

> +
> +       return ret;

NOTIFY_OK

> +}
> +
>  static int rzg2l_register_notifier(struct clk_hw *hw, const struct cpg_core_clk *core,
>                                    struct rzg2l_cpg_priv *priv)
>  {
> @@ -217,6 +281,146 @@ static int rzg2l_register_notifier(struct clk_hw *hw, const struct cpg_core_clk
>         return clk_notifier_register(hw->clk, nb);
>  }
>
> +static unsigned long rzg3s_div_clk_recalc_rate(struct clk_hw *hw,
> +                                              unsigned long parent_rate)
> +{
> +       struct clk_hw_data *clk_hw_data = to_clk_hw_data(hw);
> +       struct div_hw_data *div_hw_data = to_div_hw_data(clk_hw_data);
> +       struct rzg2l_cpg_priv *priv = clk_hw_data->priv;
> +       u32 val;
> +
> +       val = readl(priv->base + GET_REG_OFFSET(clk_hw_data->conf));
> +       val >>= GET_SHIFT(clk_hw_data->conf);
> +       val &= GENMASK(GET_WIDTH(clk_hw_data->conf) - 1, 0);
> +
> +       return divider_recalc_rate(hw, parent_rate, val, div_hw_data->dtable,
> +                                  CLK_DIVIDER_ROUND_CLOSEST, div_hw_data->width);
> +}
> +
> +static bool rzg3s_div_clk_is_rate_valid(const unsigned long invalid_rate, unsigned long rate)
> +{
> +       if (invalid_rate && rate >= invalid_rate)
> +               return false;
> +
> +       return true;
> +}
> +
> +static long rzg3s_div_clk_round_rate(struct clk_hw *hw, unsigned long rate,
> +                                    unsigned long *parent_rate)
> +{
> +       struct clk_hw_data *clk_hw_data = to_clk_hw_data(hw);
> +       struct div_hw_data *div_hw_data = to_div_hw_data(clk_hw_data);
> +       long round_rate;
> +
> +       round_rate = divider_round_rate(hw, rate, parent_rate, div_hw_data->dtable,
> +                                       div_hw_data->width, CLK_DIVIDER_ROUND_CLOSEST);
> +
> +       if (!rzg3s_div_clk_is_rate_valid(div_hw_data->invalid_rate, round_rate))
> +               return -EINVAL;

Shouldn't this return the closest rate that is actually supported instead?

> +
> +       return round_rate;
> +}

But please implement .determine_rate() instead of .round_rate() in
new drivers.

> +
> +static int rzg3s_div_clk_set_rate(struct clk_hw *hw, unsigned long rate,
> +                                 unsigned long parent_rate)
> +{
> +       struct clk_hw_data *clk_hw_data = to_clk_hw_data(hw);
> +       struct div_hw_data *div_hw_data = to_div_hw_data(clk_hw_data);
> +       struct rzg2l_cpg_priv *priv = clk_hw_data->priv;
> +       u32 off = GET_REG_OFFSET(clk_hw_data->conf);
> +       u32 shift = GET_SHIFT(clk_hw_data->conf);
> +       unsigned long flags;
> +       u32 bitmask, val;
> +       int ret;
> +
> +       /*
> +        * Some dividers cannot support some rates:
> +        * - SD div cannot support 800 MHz when parent is @800MHz and div = 1
> +        * - OCTA div cannot support 400 MHz when parent is @400MHz and div = 1
> +        * Check these scenarios.
> +        */
> +       if (!rzg3s_div_clk_is_rate_valid(div_hw_data->invalid_rate, rate))
> +               return -EINVAL;

Can this actually happen? Wouldn't the notifier have prevented us from
getting here?

> +
> +       val = divider_get_val(rate, parent_rate, div_hw_data->dtable, div_hw_data->width,
> +                             CLK_DIVIDER_ROUND_CLOSEST);
> +
> +       bitmask = (GENMASK(GET_WIDTH(clk_hw_data->conf) - 1, 0) << shift) << 16;

Is bitmask the (single) write-enable bit?

If yes, that should be BIT(16 + shift), and the variable should be
renamed to reflect that.

I guess there should be a general "#define CPG_WEN BIT(16)", then you
can simply use

    writel((CPG_WEN | val) << shift, ...);

> +
> +       spin_lock_irqsave(&priv->rmw_lock, flags);
> +       writel(bitmask | (val << shift), priv->base + off);
> +       /* Wait for the update done. */
> +       ret = rzg2l_cpg_wait_clk_update_done(priv->base, clk_hw_data->sconf);
> +       spin_unlock_irqrestore(&priv->rmw_lock, flags);
> +
> +       return ret;
> +}
> +
> +static const struct clk_ops rzg3s_div_clk_ops = {
> +       .recalc_rate = rzg3s_div_clk_recalc_rate,
> +       .round_rate = rzg3s_div_clk_round_rate,
> +       .set_rate = rzg3s_div_clk_set_rate,
> +};
> +
> +static struct clk * __init
> +rzg3s_cpg_div_clk_register(const struct cpg_core_clk *core, struct clk **clks,
> +                          void __iomem *base, struct rzg2l_cpg_priv *priv)
> +{
> +       struct div_hw_data *div_hw_data;
> +       struct clk_init_data init = {};
> +       const struct clk_div_table *clkt;
> +       struct clk_hw *clk_hw;
> +       const struct clk *parent;
> +       const char *parent_name;
> +       u32 max;
> +       int ret;
> +
> +       parent = clks[core->parent & 0xffff];
> +       if (IS_ERR(parent))
> +               return ERR_CAST(parent);
> +
> +       parent_name = __clk_get_name(parent);
> +
> +       div_hw_data = devm_kzalloc(priv->dev, sizeof(*div_hw_data), GFP_KERNEL);
> +       if (!div_hw_data)
> +               return ERR_PTR(-ENOMEM);
> +
> +       init.name = core->name;
> +       init.flags = core->flag;
> +       init.ops = &rzg3s_div_clk_ops;
> +       init.parent_names = &parent_name;
> +       init.num_parents = 1;
> +
> +       /* Get the maximum divider to retrieve div width. */
> +       for (clkt = core->dtable; clkt->div; clkt++) {
> +               if (max < clkt->div)

"max" is used uninitialized

> +                       max = clkt->div;
> +       }
> +
> +       div_hw_data->hw_data.priv = priv;
> +       div_hw_data->hw_data.conf = core->conf;
> +       div_hw_data->hw_data.sconf = core->sconf;
> +       div_hw_data->dtable = core->dtable;
> +       div_hw_data->invalid_rate = core->invalid_rate;
> +       div_hw_data->width = fls(max) - 1;

Isn't that
> +
> +       clk_hw = &div_hw_data->hw_data.hw;
> +       clk_hw->init = &init;
> +
> +       ret = devm_clk_hw_register(priv->dev, clk_hw);
> +       if (ret)
> +               return ERR_PTR(ret);
> +
> +       ret = rzg2l_register_notifier(clk_hw, core, priv);
> +       if (ret) {
> +               dev_err(priv->dev, "Failed to register notifier for %s\n",
> +                       core->name);
> +               return ERR_PTR(ret);
> +       }
> +
> +       return clk_hw->clk;
> +}

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

  reply	other threads:[~2023-10-04 12:30 UTC|newest]

Thread overview: 67+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2023-09-29  5:38 [PATCH v2 00/28] Add new Renesas RZ/G3S SoC and RZ/G3S SMARC EVK Claudiu
2023-09-29  5:38 ` [PATCH v2 01/28] dt-bindings: serial: renesas,scif: document r9a08g045 support Claudiu
2023-09-29  5:38 ` [PATCH v2 02/28] clk: renesas: rzg2l: wait for status bit of SD mux before continuing Claudiu
2023-10-03 15:14   ` Geert Uytterhoeven
2023-09-29  5:38 ` [PATCH v2 03/28] clk: renesas: rzg2l: lock around writes to mux register Claudiu
2023-10-03 15:18   ` Geert Uytterhoeven
2023-09-29  5:38 ` [PATCH v2 04/28] clk: renesas: rzg2l: trust value returned by hardware Claudiu
2023-10-03 15:19   ` Geert Uytterhoeven
2023-09-29  5:38 ` [PATCH v2 05/28] clk: renesas: rzg2l: fix computation formula Claudiu
2023-10-04  8:08   ` Geert Uytterhoeven
2023-09-29  5:38 ` [PATCH v2 06/28] clk: renesas: rzg2l: remove critical area Claudiu
2023-10-04  8:11   ` Geert Uytterhoeven
2023-09-29  5:38 ` [PATCH v2 07/28] clk: renesas: rzg2l: add support for RZ/G3S PLL Claudiu
2023-10-04  8:45   ` Geert Uytterhoeven
2023-09-29  5:38 ` [PATCH v2 08/28] clk: renesas: rzg2l: add struct clk_hw_data Claudiu
2023-10-04  8:47   ` Geert Uytterhoeven
2023-09-29  5:38 ` [PATCH v2 09/28] clk: renesas: rzg2l: remove CPG_SDHI_DSEL from generic header Claudiu
2023-10-04  8:50   ` Geert Uytterhoeven
2023-09-29  5:38 ` [PATCH v2 10/28] clk: renesas: rzg2l: refactor sd mux driver Claudiu
2023-10-04 11:30   ` Geert Uytterhoeven
2023-10-05  4:24     ` claudiu beznea
2023-09-29  5:38 ` [PATCH v2 11/28] clk: renesas: rzg2l: add a divider clock for RZ/G3S Claudiu
2023-10-04 12:30   ` Geert Uytterhoeven [this message]
2023-10-05  5:04     ` claudiu beznea
2023-10-09 11:57     ` Geert Uytterhoeven
2023-09-29  5:38 ` [PATCH v2 12/28] dt-bindings: clock: renesas,rzg2l-cpg: document RZ/G3S SoC Claudiu
2023-10-04 12:37   ` Geert Uytterhoeven
2023-09-29  5:39 ` [PATCH v2 13/28] clk: renesas: add minimal boot support for " Claudiu
2023-10-04 12:41   ` Geert Uytterhoeven
2023-09-29  5:39 ` [PATCH v2 14/28] pinctrl: renesas: rzg2l: index all registers based on port offset Claudiu
2023-10-04 12:52   ` Geert Uytterhoeven
2023-09-29  5:39 ` [PATCH v2 15/28] pinctrl: renesas: rzg2l: adapt for different SD/PWPR register offsets Claudiu
2023-10-04 12:57   ` Geert Uytterhoeven
2023-09-29  5:39 ` [PATCH v2 16/28] pinctrl: renesas: rzg2l: adapt function number for RZ/G3S Claudiu
2023-10-04 12:58   ` Geert Uytterhoeven
2023-09-29  5:39 ` [PATCH v2 17/28] pinctrl: renesas: rzg2l: move ds and oi to SoC specific configuration Claudiu
2023-10-04 13:18   ` Geert Uytterhoeven
2023-09-29  5:39 ` [PATCH v2 18/28] pinctrl: renesas: rzg2l: add support for different ds values on different groups Claudiu
2023-09-29  9:24   ` Paul Barker
2023-10-04 13:17   ` Geert Uytterhoeven
2023-10-05  5:05     ` claudiu beznea
2023-10-05 10:04     ` Geert Uytterhoeven
2023-09-29  5:39 ` [PATCH v2 19/28] dt-bindings: pinctrl: renesas: set additionalProperties: false Claudiu
2023-09-29 14:09   ` Conor Dooley
2023-10-02 14:50   ` Rob Herring
2023-10-03  3:57     ` claudiu beznea
2023-09-29  5:39 ` [PATCH v2 20/28] dt-bindings: pinctrl: renesas: document RZ/G3S SoC Claudiu
2023-09-29 14:07   ` Conor Dooley
2023-10-04 13:21   ` Geert Uytterhoeven
2023-09-29  5:39 ` [PATCH v2 21/28] pinctrl: renesas: rzg2l: add support for " Claudiu
2023-10-04 13:24   ` Geert Uytterhoeven
2023-09-29  5:39 ` [PATCH v2 22/28] arm64: dts: renesas: add initial DTSI " Claudiu
2023-10-04 13:29   ` Geert Uytterhoeven
2023-09-29  5:39 ` [PATCH v2 23/28] dt-bindings: arm: renesas: document RZ/G3S SMARC SoM Claudiu
2023-09-29 14:05   ` Conor Dooley
2023-10-04 13:28   ` Geert Uytterhoeven
2023-09-29  5:39 ` [PATCH v2 24/28] arm64: dts: renesas: rzg3l-smarc-som: add initial support for " Claudiu
2023-10-04 13:30   ` Geert Uytterhoeven
2023-09-29  5:39 ` [PATCH v2 25/28] arm64: dts: renesas: rzg3s-smarc: add initial device tree for RZ SMARC Carrier-II Board Claudiu
2023-10-04 13:31   ` Geert Uytterhoeven
2023-09-29  5:39 ` [PATCH v2 26/28] dt-bindings: arm: renesas: document SMARC Carrier-II EVK Claudiu
2023-09-29 14:05   ` Conor Dooley
2023-10-04 13:32   ` Geert Uytterhoeven
2023-09-29  5:39 ` [PATCH v2 27/28] arm64: dts: renesas: r9a08g045s33-smarc: add initial device tree for RZ/G3S SMARC EVK board Claudiu
2023-10-04 13:33   ` Geert Uytterhoeven
2023-09-29  5:39 ` [PATCH v2 28/28] arm64: defconfig: enable RZ/G3S (R9A08G045) SoC Claudiu
2023-10-04 13:34   ` Geert Uytterhoeven

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=CAMuHMdX8wPNr4LsPJR7zk3Ktb8NnqcSMUEjsAURc2NJ_dpiDHw@mail.gmail.com \
    --to=geert@linux-m68k.org \
    --cc=arnd@arndb.de \
    --cc=biju.das.jz@bp.renesas.com \
    --cc=catalin.marinas@arm.com \
    --cc=claudiu.beznea@tuxon.dev \
    --cc=conor+dt@kernel.org \
    --cc=devicetree@vger.kernel.org \
    --cc=gregkh@linuxfoundation.org \
    --cc=jirislaby@kernel.org \
    --cc=konrad.dybcio@linaro.org \
    --cc=krzysztof.kozlowski+dt@linaro.org \
    --cc=linus.walleij@linaro.org \
    --cc=linux-arm-kernel@lists.infradead.org \
    --cc=linux-clk@vger.kernel.org \
    --cc=linux-gpio@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-renesas-soc@vger.kernel.org \
    --cc=linux-serial@vger.kernel.org \
    --cc=magnus.damm@gmail.com \
    --cc=mturquette@baylibre.com \
    --cc=neil.armstrong@linaro.org \
    --cc=prabhakar.mahadev-lad.rj@bp.renesas.com \
    --cc=quic_bjorande@quicinc.com \
    --cc=robh+dt@kernel.org \
    --cc=sboyd@kernel.org \
    --cc=will@kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).