| From 047a2507bf842b35c744f00f7831dd0a22038e8a Mon Sep 17 00:00:00 2001 |
| From: Sasha Levin <sashal@kernel.org> |
| Date: Thu, 25 Mar 2021 13:26:37 -0600 |
| Subject: clk: si5341: Avoid divide errors due to bogus register contents |
| |
| From: Robert Hancock <robert.hancock@calian.com> |
| |
| [ Upstream commit 78f6f406026d688868223d5dbeb197a4f7e9a9fd ] |
| |
| If the Si5341 is being initially programmed and has no stored NVM |
| configuration, some of the register contents may contain unexpected |
| values, such as zeros, which could cause divide by zero errors during |
| driver initialization. Trap errors caused by zero registers or zero clock |
| rates which could result in divide errors later in the code. |
| |
| Fixes: 3044a860fd ("clk: Add Si5341/Si5340 driver") |
| Signed-off-by: Robert Hancock <robert.hancock@calian.com> |
| Link: https://lore.kernel.org/r/20210325192643.2190069-4-robert.hancock@calian.com |
| Signed-off-by: Stephen Boyd <sboyd@kernel.org> |
| Signed-off-by: Sasha Levin <sashal@kernel.org> |
| --- |
| drivers/clk/clk-si5341.c | 15 +++++++++++++-- |
| 1 file changed, 13 insertions(+), 2 deletions(-) |
| |
| diff --git a/drivers/clk/clk-si5341.c b/drivers/clk/clk-si5341.c |
| index b8a960e927bc..ac1ccec2b681 100644 |
| --- a/drivers/clk/clk-si5341.c |
| +++ b/drivers/clk/clk-si5341.c |
| @@ -624,6 +624,9 @@ static unsigned long si5341_synth_clk_recalc_rate(struct clk_hw *hw, |
| SI5341_SYNTH_N_NUM(synth->index), &n_num, &n_den); |
| if (err < 0) |
| return err; |
| + /* Check for bogus/uninitialized settings */ |
| + if (!n_num || !n_den) |
| + return 0; |
| |
| /* |
| * n_num and n_den are shifted left as much as possible, so to prevent |
| @@ -807,6 +810,9 @@ static long si5341_output_clk_round_rate(struct clk_hw *hw, unsigned long rate, |
| { |
| unsigned long r; |
| |
| + if (!rate) |
| + return 0; |
| + |
| r = *parent_rate >> 1; |
| |
| /* If rate is an even divisor, no changes to parent required */ |
| @@ -835,11 +841,16 @@ static int si5341_output_clk_set_rate(struct clk_hw *hw, unsigned long rate, |
| unsigned long parent_rate) |
| { |
| struct clk_si5341_output *output = to_clk_si5341_output(hw); |
| - /* Frequency divider is (r_div + 1) * 2 */ |
| - u32 r_div = (parent_rate / rate) >> 1; |
| + u32 r_div; |
| int err; |
| u8 r[3]; |
| |
| + if (!rate) |
| + return -EINVAL; |
| + |
| + /* Frequency divider is (r_div + 1) * 2 */ |
| + r_div = (parent_rate / rate) >> 1; |
| + |
| if (r_div <= 1) |
| r_div = 0; |
| else if (r_div >= BIT(24)) |
| -- |
| 2.30.2 |
| |