diff mbox series

[v4,4/4] drivers: clk: renesas: ignore all clocks which are assinged to non-Linux system

Message ID 87r0jto2yq.wl-kuninori.morimoto.gx@renesas.com (mailing list archive)
State Changes Requested, archived
Headers show
Series drivers: clk: renesas: ignore all clocks which are assinged to non-Linux system | expand

Commit Message

Kuninori Morimoto Dec. 11, 2023, 3:03 a.m. UTC
Some boards might use Linux and another OS at the same time. In such
case, currently, during booting, Linux will stop necessary module clocks
which are not used on the Linux side, but are used by another OS.

To avoid such situation, renesas-cpg-mssr tries to find
status = "reserved" devices (A), and adds CLK_IGNORE_UNUSED flag to its
<&cgp CPG_MOD xxx> clock (B).

Table 2.4: Values for status property
https://github.com/devicetree-org/devicetree-specification/releases/download/v0.4/devicetree-specification-v0.4.pdf

"reserved"
	Indicates that the device is operational, but should not be
	used. Typically this is used for devices that are controlled
	by another software component, such as platform firmware.

ex)
	scif5: serial@e6f30000 {
		...
(B)		clocks = <&cpg CPG_MOD 202>,
			 <&cpg CPG_CORE R8A7795_CLK_S3D1>,
			 <&scif_clk>;
		...
(A)		status = "reserved";
	};

Cc: Aymeric Aillet <aymeric.aillet@iot.bzh>
Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
Tested-by: Yusuke Goda <yusuke.goda.sx@renesas.com>
---
 drivers/clk/renesas/renesas-cpg-mssr.c | 101 +++++++++++++++++++++++--
 1 file changed, 95 insertions(+), 6 deletions(-)

Comments

Geert Uytterhoeven Dec. 14, 2023, 9:06 a.m. UTC | #1
Hi Morimoto-san,

Thanks for the update!

s/assinged/assigned/

On Mon, Dec 11, 2023 at 4:03 AM Kuninori Morimoto
<kuninori.morimoto.gx@renesas.com> wrote:
> Some boards might use Linux and another OS at the same time. In such
> case, currently, during booting, Linux will stop necessary module clocks
> which are not used on the Linux side, but are used by another OS.
>
> To avoid such situation, renesas-cpg-mssr tries to find
> status = "reserved" devices (A), and adds CLK_IGNORE_UNUSED flag to its
> <&cgp CPG_MOD xxx> clock (B).
>
> Table 2.4: Values for status property
> https://github.com/devicetree-org/devicetree-specification/releases/download/v0.4/devicetree-specification-v0.4.pdf
>
> "reserved"
>         Indicates that the device is operational, but should not be
>         used. Typically this is used for devices that are controlled
>         by another software component, such as platform firmware.
>
> ex)
>         scif5: serial@e6f30000 {
>                 ...
> (B)             clocks = <&cpg CPG_MOD 202>,
>                          <&cpg CPG_CORE R8A7795_CLK_S3D1>,
>                          <&scif_clk>;
>                 ...
> (A)             status = "reserved";
>         };
>
> Cc: Aymeric Aillet <aymeric.aillet@iot.bzh>
> Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
> Tested-by: Yusuke Goda <yusuke.goda.sx@renesas.com>

> @@ -949,6 +967,72 @@ static const struct dev_pm_ops cpg_mssr_pm = {
>  #define DEV_PM_OPS     NULL
>  #endif /* CONFIG_PM_SLEEP && CONFIG_ARM_PSCI_FW */
>
> +static void __init cpg_mssr_reserved_exit(struct cpg_mssr_priv *priv)
> +{
> +       kfree(priv->reserved_ids);
> +}

This function is called only once, so you might want to inline it manually.

> +
> +static int __init cpg_mssr_reserved_init(struct cpg_mssr_priv *priv,
> +                                        const struct cpg_mssr_info *info)
> +{
> +       struct device_node *soc = of_find_node_by_path("/soc");
> +       struct device_node *node;
> +       uint32_t args[MAX_PHANDLE_ARGS];
> +       unsigned int *ids = NULL;
> +       unsigned int num = 0;
> +
> +       /*
> +        * Because clk_disable_unused() will disable all unused clocks, the device which is assigned
> +        * to a non-Linux system will be disabled when Linux is booted.
> +        *
> +        * To avoid such situation, renesas-cpg-mssr assumes the device which has
> +        * status = "reserved" is assigned to a non-Linux system, and adds CLK_IGNORE_UNUSED flag
> +        * to its CPG_MOD clocks.
> +        * see also
> +        *      cpg_mssr_register_mod_clk()
> +        *
> +        *      scif5: serial@e6f30000 {
> +        *              ...
> +        * =>           clocks = <&cpg CPG_MOD 202>,
> +        *                       <&cpg CPG_CORE R8A7795_CLK_S3D1>,
> +        *                       <&scif_clk>;
> +        *                       ...
> +        *               status = "reserved";
> +        *      };
> +        */
> +       for_each_reserved_child_of_node(soc, node) {
> +               struct of_phandle_iterator it;
> +               int rc;
> +
> +               of_for_each_phandle(&it, rc, node, "clocks", "#clock-cells", -1) {
> +                       int idx;
> +
> +                       of_phandle_iterator_args(&it, args, MAX_PHANDLE_ARGS);
> +
> +                       if (!(it.node == priv->np && args[0] == CPG_MOD))

I think "(it.node != priv->np || args[0] != CPG_MOD)" is easier to read ;-)

However, I think it would make sense to split this in two separate
checks, to avoid calling of_phandle_iterator_args() when it.node !=
priv->np, and to validate the number of arguments:

    if (it.node != priv->np)
            continue;

    if (of_phandle_iterator_args(&it, args, MAX_PHANDLE_ARGS) != 2)
            continue;

    if (args[0] != CPG_MOD)
            continue;

> +                               continue;
> +
> +                       ids = krealloc_array(ids, (num + 1), sizeof(*ids), GFP_KERNEL);
> +                       if (!ids)
> +                               return -ENOMEM;

Missing of_node_put(it.node) in the error path.

> +
> +                       if (priv->reg_layout == CLK_REG_LAYOUT_RZ_A)
> +                               idx = MOD_CLK_PACK_10(args[1]); /* for DEF_MOD_STB() */
> +                       else
> +                               idx = MOD_CLK_PACK(args[1]);    /* for DEF_MOD() */
> +
> +                       ids[num] = info->num_total_core_clks + idx;
> +
> +                       num++;
> +               }
> +       }
> +
> +       priv->num_reserved_ids  = num;
> +       priv->reserved_ids      = ids;
> +
> +       return 0;
> +}
> +
>  static int __init cpg_mssr_common_init(struct device *dev,
>                                        struct device_node *np,
>                                        const struct cpg_mssr_info *info)
> @@ -1007,6 +1091,10 @@ static int __init cpg_mssr_common_init(struct device *dev,
>         if (error)
>                 goto out_err;
>
> +       error = cpg_mssr_reserved_init(priv, info);
> +       if (error)
> +               goto out_err;

Missing of_clk_del_provider() in the error path.

You may want to move the call to cpg_mssr_reserved_init() up, as
reverting that just needs an unconditional call to kfree() (kfree
works fine on NULL), while calling of_clk_del_provider() requires a
new label to jump to.

> +
>         cpg_mssr_priv = priv;
>
>         return 0;
> @@ -1070,22 +1158,23 @@ static int __init cpg_mssr_probe(struct platform_device *pdev)
>                                          cpg_mssr_del_clk_provider,
>                                          np);
>         if (error)
> -               return error;
> +               goto reserve_err;
>
>         error = cpg_mssr_add_clk_domain(dev, info->core_pm_clks,
>                                         info->num_core_pm_clks);
>         if (error)
> -               return error;
> +               goto reserve_err;
>
>         /* Reset Controller not supported for Standby Control SoCs */
>         if (priv->reg_layout == CLK_REG_LAYOUT_RZ_A)
> -               return 0;
> +               goto reserve_err;
>
>         error = cpg_mssr_reset_controller_register(priv);
> -       if (error)
> -               return error;
>
> -       return 0;
> +reserve_err:

Perhaps rename the label to "reserve_exit", as this is called on
success, too?

> +       cpg_mssr_reserved_exit(priv);
> +
> +       return error;
>  }
>
>  static struct platform_driver cpg_mssr_driver = {

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
Kuninori Morimoto Dec. 14, 2023, 11:07 p.m. UTC | #2
Hi Geert

Thank you for your reveiw

> > +static void __init cpg_mssr_reserved_exit(struct cpg_mssr_priv *priv)
> > +{
> > +       kfree(priv->reserved_ids);
> > +}
> 
> This function is called only once, so you might want to inline it manually.

I want to keep init()/exit() pair because it is easy to understand.

I will consider other comment and post v5 patch next week


Thank you for your help !!

Best regards
---
Renesas Electronics
Ph.D. Kuninori Morimoto
diff mbox series

Patch

diff --git a/drivers/clk/renesas/renesas-cpg-mssr.c b/drivers/clk/renesas/renesas-cpg-mssr.c
index cb80d1bf6c7c..0db2aec00d10 100644
--- a/drivers/clk/renesas/renesas-cpg-mssr.c
+++ b/drivers/clk/renesas/renesas-cpg-mssr.c
@@ -142,6 +142,8 @@  static const u16 srstclr_for_gen4[] = {
  * @reset_clear_regs:  Pointer to reset clearing registers array
  * @smstpcr_saved: [].mask: Mask of SMSTPCR[] bits under our control
  *                 [].val: Saved values of SMSTPCR[]
+ * @reserved_ids: Temporary used, reserved id list
+ * @num_reserved_ids: Temporary used, number of reserved id list
  * @clks: Array containing all Core and Module Clocks
  */
 struct cpg_mssr_priv {
@@ -168,6 +170,9 @@  struct cpg_mssr_priv {
 		u32 val;
 	} smstpcr_saved[ARRAY_SIZE(mstpsr_for_gen4)];
 
+	unsigned int *reserved_ids;
+	unsigned int num_reserved_ids;
+
 	struct clk *clks[];
 };
 
@@ -453,6 +458,19 @@  static void __init cpg_mssr_register_mod_clk(const struct mssr_mod_clk *mod,
 			break;
 		}
 
+	/*
+	 * Ignore reserved device.
+	 * see
+	 *	cpg_mssr_reserved_init()
+	 */
+	for (i = 0; i < priv->num_reserved_ids; i++) {
+		if (id == priv->reserved_ids[i]) {
+			dev_info(dev, "Ignore Linux non-assigned mod (%s)\n", mod->name);
+			init.flags |= CLK_IGNORE_UNUSED;
+			break;
+		}
+	}
+
 	clk = clk_register(NULL, &clock->hw);
 	if (IS_ERR(clk))
 		goto fail;
@@ -949,6 +967,72 @@  static const struct dev_pm_ops cpg_mssr_pm = {
 #define DEV_PM_OPS	NULL
 #endif /* CONFIG_PM_SLEEP && CONFIG_ARM_PSCI_FW */
 
+static void __init cpg_mssr_reserved_exit(struct cpg_mssr_priv *priv)
+{
+	kfree(priv->reserved_ids);
+}
+
+static int __init cpg_mssr_reserved_init(struct cpg_mssr_priv *priv,
+					 const struct cpg_mssr_info *info)
+{
+	struct device_node *soc = of_find_node_by_path("/soc");
+	struct device_node *node;
+	uint32_t args[MAX_PHANDLE_ARGS];
+	unsigned int *ids = NULL;
+	unsigned int num = 0;
+
+	/*
+	 * Because clk_disable_unused() will disable all unused clocks, the device which is assigned
+	 * to a non-Linux system will be disabled when Linux is booted.
+	 *
+	 * To avoid such situation, renesas-cpg-mssr assumes the device which has
+	 * status = "reserved" is assigned to a non-Linux system, and adds CLK_IGNORE_UNUSED flag
+	 * to its CPG_MOD clocks.
+	 * see also
+	 *	cpg_mssr_register_mod_clk()
+	 *
+	 *	scif5: serial@e6f30000 {
+	 *		...
+	 * =>		clocks = <&cpg CPG_MOD 202>,
+	 *			 <&cpg CPG_CORE R8A7795_CLK_S3D1>,
+	 *			 <&scif_clk>;
+	 *			 ...
+	 *		 status = "reserved";
+	 *	};
+	 */
+	for_each_reserved_child_of_node(soc, node) {
+		struct of_phandle_iterator it;
+		int rc;
+
+		of_for_each_phandle(&it, rc, node, "clocks", "#clock-cells", -1) {
+			int idx;
+
+			of_phandle_iterator_args(&it, args, MAX_PHANDLE_ARGS);
+
+			if (!(it.node == priv->np && args[0] == CPG_MOD))
+				continue;
+
+			ids = krealloc_array(ids, (num + 1), sizeof(*ids), GFP_KERNEL);
+			if (!ids)
+				return -ENOMEM;
+
+			if (priv->reg_layout == CLK_REG_LAYOUT_RZ_A)
+				idx = MOD_CLK_PACK_10(args[1]);	/* for DEF_MOD_STB() */
+			else
+				idx = MOD_CLK_PACK(args[1]);	/* for DEF_MOD() */
+
+			ids[num] = info->num_total_core_clks + idx;
+
+			num++;
+		}
+	}
+
+	priv->num_reserved_ids	= num;
+	priv->reserved_ids	= ids;
+
+	return 0;
+}
+
 static int __init cpg_mssr_common_init(struct device *dev,
 				       struct device_node *np,
 				       const struct cpg_mssr_info *info)
@@ -1007,6 +1091,10 @@  static int __init cpg_mssr_common_init(struct device *dev,
 	if (error)
 		goto out_err;
 
+	error = cpg_mssr_reserved_init(priv, info);
+	if (error)
+		goto out_err;
+
 	cpg_mssr_priv = priv;
 
 	return 0;
@@ -1070,22 +1158,23 @@  static int __init cpg_mssr_probe(struct platform_device *pdev)
 					 cpg_mssr_del_clk_provider,
 					 np);
 	if (error)
-		return error;
+		goto reserve_err;
 
 	error = cpg_mssr_add_clk_domain(dev, info->core_pm_clks,
 					info->num_core_pm_clks);
 	if (error)
-		return error;
+		goto reserve_err;
 
 	/* Reset Controller not supported for Standby Control SoCs */
 	if (priv->reg_layout == CLK_REG_LAYOUT_RZ_A)
-		return 0;
+		goto reserve_err;
 
 	error = cpg_mssr_reset_controller_register(priv);
-	if (error)
-		return error;
 
-	return 0;
+reserve_err:
+	cpg_mssr_reserved_exit(priv);
+
+	return error;
 }
 
 static struct platform_driver cpg_mssr_driver = {