[PATCH v2] clk: zynq: return -ETIMEDOUT if the PLL never locks
From: Linkai Gong
Date: Tue Sep 08 2026 - 05:02:09 EST
zynq_pll_enable() waits for lock under a spinlock with no timeout.
A stuck PLL would wedge the enable path with IRQs off.
Poll with readl_poll_timeout_atomic() and return -ETIMEDOUT on
failure. The 1 ms upper bound is a software limit for a stuck PLL,
not a TRM-derived value; Zynq PLL lock is still expected well
within that window.
Changes in v2:
- Use BIT(clk->lockbit)
- Name the poll delay/timeout constants
- Clarify the 1 ms bound in the commit message
Fixes: 3682af46d55f ("clk: zynq: Factor out PLL driver")
Signed-off-by: Linkai Gong <gonglinkai@xxxxxxxxxx>
---
drivers/clk/zynq/pll.c | 15 ++++++++++++---
1 file changed, 12 insertions(+), 3 deletions(-)
diff --git a/drivers/clk/zynq/pll.c b/drivers/clk/zynq/pll.c
index fe90b50e1545..6a98bc60fb91 100644
--- a/drivers/clk/zynq/pll.c
+++ b/drivers/clk/zynq/pll.c
@@ -9,7 +9,9 @@
#include <linux/clk/zynq.h>
#include <linux/clk-provider.h>
#include <linux/slab.h>
+#include <linux/bits.h>
#include <linux/io.h>
+#include <linux/iopoll.h>
/**
* struct zynq_pll - pll clock
@@ -41,6 +43,10 @@ struct zynq_pll {
#define PLL_FBDIV_MIN 13
#define PLL_FBDIV_MAX 66
+/* Software bound for a stuck PLL under spinlock; not from the TRM. */
+#define PLL_LOCK_POLL_DELAY_US 10
+#define PLL_LOCK_TIMEOUT_US 1000
+
/**
* zynq_pll_determine_rate() - Round a clock frequency
* @hw: Handle between common and hardware-specific interfaces
@@ -119,6 +125,7 @@ static int zynq_pll_enable(struct clk_hw *hw)
unsigned long flags = 0;
u32 reg;
struct zynq_pll *clk = to_zynq_pll(hw);
+ int ret;
if (zynq_pll_is_enabled(hw))
return 0;
@@ -131,12 +138,14 @@ static int zynq_pll_enable(struct clk_hw *hw)
reg = readl(clk->pll_ctrl);
reg &= ~(PLLCTRL_RESET_MASK | PLLCTRL_PWRDWN_MASK);
writel(reg, clk->pll_ctrl);
- while (!(readl(clk->pll_status) & (1 << clk->lockbit)))
- ;
+ ret = readl_poll_timeout_atomic(clk->pll_status, reg,
+ reg & BIT(clk->lockbit),
+ PLL_LOCK_POLL_DELAY_US,
+ PLL_LOCK_TIMEOUT_US);
spin_unlock_irqrestore(clk->lock, flags);
- return 0;
+ return ret;
}
/**
--
2.25.1