[PATCH v3 8/8] clk: clocking-wizard: Use separate notifier_block for each clock

From: Shubhrajyoti Datta

Date: Fri Jul 31 2026 - 03:30:02 EST


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 <shubhrajyoti.datta@xxxxxxx>
---

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