From: Ryan Wanner Ryan.Wanner@microchip.com
[ Upstream commit e0237f5635727d64635ec6665e1de9f4cacce35c ]
A potential divider for the master clock is div/3. The register configuration for div/3 is MASTER_PRES_MAX. The current bit shifting method does not work for this case. Checking for MASTER_PRES_MAX will ensure the correct decimal value is stored in the system.
Signed-off-by: Ryan Wanner Ryan.Wanner@microchip.com Signed-off-by: Nicolas Ferre nicolas.ferre@microchip.com Signed-off-by: Sasha Levin sashal@kernel.org ---
LLM Generated explanations, may be completely bogus:
YES – the change fixes a real bug in the sama7g5 master clock code with minimal risk.
- `clk_sama7g5_master_recalc_rate()` now treats the special register value used for divide-by-3, returning `parent_rate / 3` instead of wrongly shifting by `1 << 7` and reporting a 1/128 rate (drivers/clk/at91/clk-master.c:583). This corrects `clk_get_rate()` for every consumer of the master clock when that divider is active. - The rest of the sama7g5 clock logic already maps the same register value to divide-by-3 (e.g. `clk_sama7g5_master_set_rate()` stores `MASTER_PRES_MAX` for a /3 request), so the fix restores consistency in the clock framework and prevents child clocks from inheriting a bogus rate (drivers/clk/at91/clk-master.c:732). - Other SoCs using the generic master clock ops are unaffected; the new branch lives only in the sama7g5-specific implementation and matches existing handling of this divider elsewhere in the driver (drivers/clk/at91/clk-master.c:392).
Because the bug misreports hardware frequencies and can break downstream rate selection, and the fix is self-contained and low risk, this commit is a good candidate for stable backporting.
drivers/clk/at91/clk-master.c | 3 +++ 1 file changed, 3 insertions(+)
diff --git a/drivers/clk/at91/clk-master.c b/drivers/clk/at91/clk-master.c index 7a544e429d34e..d5ea2069ec83a 100644 --- a/drivers/clk/at91/clk-master.c +++ b/drivers/clk/at91/clk-master.c @@ -580,6 +580,9 @@ clk_sama7g5_master_recalc_rate(struct clk_hw *hw, { struct clk_master *master = to_clk_master(hw);
+ if (master->div == MASTER_PRES_MAX) + return DIV_ROUND_CLOSEST_ULL(parent_rate, 3); + return DIV_ROUND_CLOSEST_ULL(parent_rate, (1 << master->div)); }