[PATCH v3 8/8] clk: clocking-wizard: Use separate notifier_block for each clock
Shubhrajyoti Datta <[email protected]> Fri, 31 Jul 2026 12:50:49 +0530
| Newsgroups | org.kernel.vger.linux-clk,org.infradead.lists.linux-arm-kernel,org.kernel.vger.linux-devicetree,org.kernel.vger.linux-kernel |
|---|---|
| Message-ID | <[email protected]> |
A struct notifier_block has a single 'next' pointer, so registering the same instance to two different notification chains corrupts the linked lists. Use a dedicated nb_axi for the s_axi_aclk notifier chain. Signed-off-by: Shubhrajyoti Datta <[email protected]> --- Changes in v3: Split notifier into per-clock callbacks drivers/clk/xilinx/clk-xlnx-clock-wizard.c | 42 +++++++++++++++++----- 1 file changed, 33 insertions(+), 9 deletions(-) diff --git a/drivers/clk/xilinx/clk-xlnx-clock-wizard.c b/drivers/clk/xilinx/clk-xlnx-clock-wizard.c index b16656a11f4b..cfad11593b2b 100644 --- a/drivers/clk/xilinx/clk-xlnx-clock-wizard.c +++ b/drivers/clk/xilinx/clk-xlnx-clock-wizard.c @@ -126,7 +126,8 @@ enum clk_wzrd_int_clks { /** * struct clk_wzrd - Clock wizard private data structure * - * @nb: Notifier block + * @nb: Notifier block for clk_in1 + * @nb_axi: Notifier block for s_axi_aclk * @base: Memory base * @clk_in1: Handle to input clock 'clk_in1' * @axi_clk: Handle to input clock 's_axi_aclk' @@ -137,6 +138,7 @@ enum clk_wzrd_int_clks { */ struct clk_wzrd { struct notifier_block nb; + struct notifier_block nb_axi; void __iomem *base; struct clk *clk_in1; struct clk *axi_clk; @@ -184,6 +186,7 @@ struct versal_clk_data { }; #define to_clk_wzrd(_nb) container_of(_nb, struct clk_wzrd, nb) +#define to_clk_wzrd_axi(_nb) container_of(_nb, struct clk_wzrd, nb_axi) /* maximum frequencies for input/output clocks per speed grade */ static const unsigned long clk_wzrd_max_freq[] = { @@ -1024,6 +1027,30 @@ static struct clk_hw *clk_wzrd_register_divider(struct device *dev, static int clk_wzrd_clk_notifier(struct notifier_block *nb, unsigned long event, void *data) +{ + unsigned long max; + struct clk_notifier_data *ndata = data; + struct clk_wzrd *clk_wzrd = to_clk_wzrd_axi(nb); + + if (clk_wzrd->suspended) + return NOTIFY_OK; + + max = WZRD_ACLK_MAX_FREQ; + + switch (event) { + case PRE_RATE_CHANGE: + if (ndata->new_rate > max) + return NOTIFY_BAD; + return NOTIFY_OK; + case POST_RATE_CHANGE: + case ABORT_RATE_CHANGE: + default: + return NOTIFY_DONE; + } +} + +static int clk_wzrd_clk_notifier_in1(struct notifier_block *nb, unsigned long event, + void *data) { unsigned long max; struct clk_notifier_data *ndata = data; @@ -1032,12 +1059,7 @@ static int clk_wzrd_clk_notifier(struct notifier_block *nb, unsigned long event, if (clk_wzrd->suspended) return NOTIFY_OK; - if (ndata->clk == clk_wzrd->clk_in1) - max = clk_wzrd_max_freq[clk_wzrd->speed_grade - 1]; - else if (ndata->clk == clk_wzrd->axi_clk) - max = WZRD_ACLK_MAX_FREQ; - else - return NOTIFY_DONE; /* should never happen */ + max = clk_wzrd_max_freq[clk_wzrd->speed_grade - 1]; switch (event) { case PRE_RATE_CHANGE: @@ -1310,7 +1332,7 @@ static int clk_wzrd_probe(struct platform_device *pdev) } if (clk_wzrd->speed_grade) { - clk_wzrd->nb.notifier_call = clk_wzrd_clk_notifier; + clk_wzrd->nb.notifier_call = clk_wzrd_clk_notifier_in1; ret = devm_clk_notifier_register(&pdev->dev, clk_wzrd->clk_in1, &clk_wzrd->nb); @@ -1318,8 +1340,10 @@ static int clk_wzrd_probe(struct platform_device *pdev) dev_warn(&pdev->dev, "unable to register clock notifier\n"); + clk_wzrd->nb_axi.notifier_call = clk_wzrd_clk_notifier; + ret = devm_clk_notifier_register(&pdev->dev, clk_wzrd->axi_clk, - &clk_wzrd->nb); + &clk_wzrd->nb_axi); if (ret) dev_warn(&pdev->dev, "unable to register clock notifier\n"); -- 2.34.1