Message ID | 87a5r7c13d.wl-kuninori.morimoto.gx@renesas.com (mailing list archive) |
---|---|
State | Not Applicable, archived |
Headers | show |
Series | drivers: clk: renesas: ignore all clocks which is assinged to non Linux system | expand |
On Tue, Nov 21, 2023 at 02:05:42AM +0000, Kuninori Morimoto wrote: > Some board might use Linux and another OS in the same time. In such > case, current Linux will stop necessary module clock when booting > which is not used on Linux side, but is used on another OS side. > > To avoid such situation, renesas-cpg-mssr try to find > status = "reserved" devices (A), and add 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 | 118 +++++++++++++++++++++++-- > 1 file changed, 109 insertions(+), 9 deletions(-) > > diff --git a/drivers/clk/renesas/renesas-cpg-mssr.c b/drivers/clk/renesas/renesas-cpg-mssr.c > index cb80d1bf6c7c..26098b7f4323 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,75 @@ 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 *root = of_find_node_by_path("/soc"); 'root' is '/', so I find this slightly confusing. > + struct device_node *node = NULL; > + struct of_phandle_args clkspec; > + unsigned int *ids = NULL; > + unsigned int num = 0; > + > + /* > + * Because cpg_mssr_info has .num_hw_mod_clks which indicates number of all Module Clocks, > + * and clk_disable_unused() will disable all unused clocks, the device which is assigned to > + * non-Linux system will be disabled when Linux was booted. > + * > + * To avoid such situation, renesas-cpg-mssr assumes the device which has > + * status = "reserved" is assigned to non-Linux system, and add CLK_IGNORE_UNUSED flag > + * to its clocks if it was CPG_MOD. > + * 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(root, node) { Don't you really want to find all reserved nodes in the DT rather than just child nodes of a single node? > + unsigned int i = 0; > + > + while (!of_parse_phandle_with_args(node, "clocks", "#clock-cells", i++, &clkspec)) { of_for_each_phandle() > + > + of_node_put(clkspec.np); > + > + if (clkspec.np == priv->dev->of_node && > + clkspec.args[0] == CPG_MOD) { > + > + ids = krealloc_array(ids, (num + 1), sizeof(*ids), GFP_KERNEL); > + if (!ids) > + return -ENOMEM; > + > + ids[num] = info->num_total_core_clks + > + MOD_CLK_PACK(clkspec.args[1]); > + > + num++; > + } > + } > + }
Hi Rob Thank you for reviewing the patch. > > +static int __init cpg_mssr_reserved_init(struct cpg_mssr_priv *priv, > > + const struct cpg_mssr_info *info) > > +{ > > + struct device_node *root = of_find_node_by_path("/soc"); > > 'root' is '/', so I find this slightly confusing. (snip) > > + * scif5: serial@e6f30000 { > > + * ... > > + * => clocks = <&cpg CPG_MOD 202>, > > + * <&cpg CPG_CORE R8A7795_CLK_S3D1>, > > + * <&scif_clk>; > > + * ... > > + * status = "reserved"; > > + * }; > > + */ > > + for_each_reserved_child_of_node(root, node) { > > Don't you really want to find all reserved nodes in the DT rather than > just child nodes of a single node? The all devices which we need to check (and ignore the clock) in this driver is defined under /soc for now. Thank you for your help !! Best regards --- Kuninori Morimoto
Hi Morimoto-san, On Wed, Nov 29, 2023 at 12:13 AM Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> wrote: > > > +static int __init cpg_mssr_reserved_init(struct cpg_mssr_priv *priv, > > > + const struct cpg_mssr_info *info) > > > +{ > > > + struct device_node *root = of_find_node_by_path("/soc"); > > > > 'root' is '/', so I find this slightly confusing. > (snip) > > > + * scif5: serial@e6f30000 { > > > + * ... > > > + * => clocks = <&cpg CPG_MOD 202>, > > > + * <&cpg CPG_CORE R8A7795_CLK_S3D1>, > > > + * <&scif_clk>; > > > + * ... > > > + * status = "reserved"; > > > + * }; > > > + */ > > > + for_each_reserved_child_of_node(root, node) { > > > > Don't you really want to find all reserved nodes in the DT rather than > > just child nodes of a single node? > > The all devices which we need to check (and ignore the clock) in this > driver is defined under /soc for now. "For now" is the right clause: all module clocks are inputs to on-SoC devices under the "soc" node. However, once the clock drivers become aware[1]* of programmable (core) clocks driving the real-time CPU cores not running Linux, you may need special handling for these, too. [1] On R-Car V3U, the clock driver already supports R8A779A0_CLK_ZR, but it is a fixed clock that cannot be disabled nor changed. Gr{oetje,eeting}s, Geert
diff --git a/drivers/clk/renesas/renesas-cpg-mssr.c b/drivers/clk/renesas/renesas-cpg-mssr.c index cb80d1bf6c7c..26098b7f4323 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,75 @@ 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 *root = of_find_node_by_path("/soc"); + struct device_node *node = NULL; + struct of_phandle_args clkspec; + unsigned int *ids = NULL; + unsigned int num = 0; + + /* + * Because cpg_mssr_info has .num_hw_mod_clks which indicates number of all Module Clocks, + * and clk_disable_unused() will disable all unused clocks, the device which is assigned to + * non-Linux system will be disabled when Linux was booted. + * + * To avoid such situation, renesas-cpg-mssr assumes the device which has + * status = "reserved" is assigned to non-Linux system, and add CLK_IGNORE_UNUSED flag + * to its clocks if it was CPG_MOD. + * 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(root, node) { + unsigned int i = 0; + + while (!of_parse_phandle_with_args(node, "clocks", "#clock-cells", i++, &clkspec)) { + + of_node_put(clkspec.np); + + if (clkspec.np == priv->dev->of_node && + clkspec.args[0] == CPG_MOD) { + + ids = krealloc_array(ids, (num + 1), sizeof(*ids), GFP_KERNEL); + if (!ids) + return -ENOMEM; + + ids[num] = info->num_total_core_clks + + MOD_CLK_PACK(clkspec.args[1]); + + num++; + } + } + } + + priv->num_reserved_ids = num; + priv->reserved_ids = ids; + + return 0; +} + +static void __init cpg_mssr_common_exit(struct cpg_mssr_priv *priv) +{ + if (priv->base) + iounmap(priv->base); + kfree(priv); +} + static int __init cpg_mssr_common_init(struct device *dev, struct device_node *np, const struct cpg_mssr_info *info) @@ -1012,9 +1099,7 @@ static int __init cpg_mssr_common_init(struct device *dev, return 0; out_err: - if (priv->base) - iounmap(priv->base); - kfree(priv); + cpg_mssr_common_exit(priv); return error; } @@ -1029,6 +1114,10 @@ void __init cpg_mssr_early_init(struct device_node *np, if (error) return; + error = cpg_mssr_reserved_init(cpg_mssr_priv, info); + if (error) + goto err; + for (i = 0; i < info->num_early_core_clks; i++) cpg_mssr_register_core_clk(&info->early_core_clks[i], info, cpg_mssr_priv); @@ -1037,6 +1126,12 @@ void __init cpg_mssr_early_init(struct device_node *np, cpg_mssr_register_mod_clk(&info->early_mod_clks[i], info, cpg_mssr_priv); + cpg_mssr_reserved_exit(cpg_mssr_priv); + + return; + +err: + cpg_mssr_common_exit(cpg_mssr_priv); } static int __init cpg_mssr_probe(struct platform_device *pdev) @@ -1060,6 +1155,10 @@ static int __init cpg_mssr_probe(struct platform_device *pdev) priv->dev = dev; dev_set_drvdata(dev, priv); + error = cpg_mssr_reserved_init(priv, info); + if (error) + return error; + for (i = 0; i < info->num_core_clks; i++) cpg_mssr_register_core_clk(&info->core_clks[i], info, priv); @@ -1070,22 +1169,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 = {