Re: [PATCH 06/12] clk: qcom: ipq-cmn-pll: Add PON reference clock support

Jie Luo <[email protected]> Thu, 30 Jul 2026 21:21:40 +0800
Newsgroups dev.linux.lists.sashiko-reviews,org.kernel.vger.linux-devicetree
Message-ID <[email protected]>

On 7/24/2026 11:11 PM, [email protected] wrote:
> Thank you for your contribution! Sashiko AI review found 6 potential issue(s) to consider:
> - [High] 64-bit divisor passed to DIV_ROUND_CLOSEST_ULL causes 32-bit truncation and potential divide-by-zero panics.
> - [Medium] Non-atomic register updates when switching to PON mode can cause transient invalid clock states.
> - [Medium] `clk_pon_refclk_determine_rate()` returns 0 on invalid rate requests instead of an error code.
> - [Medium] Missing PLL analog soft reset when entering UNIPHY fixed mode.
> - [Medium] UNIPHY mode is incorrectly excluded from rate rounding for near-match requests.
> - [Medium] Missing error check on regmap_read in clk_pon_refclk_recalc_rate leads to use of uninitialized variable.
> --
> 
> --- Patch [6]: [PATCH 06/12] clk: qcom: ipq-cmn-pll: Add PON reference clock support ---
> commit 66c5c5bce6266424eec1b5f6855bb022b40a0322
> Author: Luo Jie <[email protected]>
> 
> clk: qcom: ipq-cmn-pll: Add PON reference clock support
> 
> This commit adds support for the PON reference clock to the IPQ CMN PLL driver.
> The clock supports UNIPHY and PON modes, with configurable dividers.
> 
>> diff --git a/drivers/clk/qcom/ipq-cmn-pll.c b/drivers/clk/qcom/ipq-cmn-pll.c
>> index 020ad12ca1f2b..1ae32052b5634 100644
>> --- a/drivers/clk/qcom/ipq-cmn-pll.c
>> +++ b/drivers/clk/qcom/ipq-cmn-pll.c
> 
> [ ... ]
> 
>> @@ -540,6 +545,155 @@ static struct clk_hw *ipq_cmn_pll_ppe_register(struct platform_device *pdev,
>>  	return &ppe_clk->hw;
>>  }
>>  
>> +/*
>> + * PON (Passive Optical Network) reference clock operations.
>> + * The PON refclk is derived from CMN PLL rate / 2, then divided by
>> + * a configurable 8-bit divider (1-255).
>> + */
> 
> [ ... ]
> 
>> +static unsigned long clk_pon_refclk_recalc_rate(struct clk_hw *hw,
>> +						unsigned long parent_rate)
>> +{
>> +	struct clk_cmn_pll *pon_clk = to_clk_cmn_pll(hw);
>> +	u32 val, div;
>> +
>> +	regmap_read(pon_clk->regmap, CMN_PLL_PON_CONFIG, &val);
>> +
>> +	/* Check if in UNIPHY mode (bit 9 = 0) - fixed 31.25 MHz */
>> +	if (!(val & CMN_PLL_PON_MODE_SEL))
> 
> [Severity: Medium]
> Could this use uninitialized memory if the underlying MMIO read fails?
> 
> The return value of regmap_read() is a standard negative error code on failure.
> Since it is not checked, val may be left uninitialized if the read fails,
> causing garbage clock rates to be returned to the framework.
> 

Good catch. Will check ret in the next version.

>> +		return 31250000UL;
>> +
>> +	/* PON mode: calculate from divider */
>> +	div = FIELD_GET(CMN_PLL_PON_DIV_CTRL, val);
>> +	if (WARN_ON_ONCE(!div))
>> +		return 0;
>> +
>> +	return DIV_ROUND_CLOSEST_ULL((u64)parent_rate, 2ULL * div);
>> +}
>> +
>> +static int clk_pon_refclk_determine_rate(struct clk_hw *hw,
>> +					 struct clk_rate_request *req)
>> +{
>> +	unsigned long div;
>> +
>> +	if (WARN_ON_ONCE(!req->rate))
>> +		return 0;
> 
> [Severity: Medium]
> Should this return a negative error code (like -EINVAL) instead of 0?
> 
> Returning 0 tells the Common Clock Framework that the rate request was
> successfully determined and adapted, which will cause it to proceed to call
> set_rate() with rate = 0.
> 

Will change this to return -EINVAL.

>> +
>> +	/* UNIPHY fixed mode */
>> +	if (req->rate == 31250000UL)
>> +		return 0;
> 
> [Severity: Medium]
> Does this exact match prevent near-match rounding to UNIPHY mode?
> 
> If a consumer requests 31249999 Hz, it seems it would bypass this exact match
> and fall back to calculating a PON mode divider. This might yield a worse
> rate, even though the hardware inherently supports exactly 31.25 MHz.

Good catch — Will update clk_pon_refclk_determine_rate() to compute both
the fixed UNIPHY-mode rate (31.25 MHz) and the closest achievable PON-
mode rate for the requested divider, then selects whichever one is
closer to the requested rate.


> 
>> +
>> +	div = DIV_ROUND_CLOSEST_ULL((u64)req->best_parent_rate, 2ULL * req->rate);
> 
> [Severity: High]
> Can this cause a divide-by-zero panic on 32-bit platforms?
> 
> DIV_ROUND_CLOSEST_ULL() internally uses do_div(), which fundamentally
> requires a 32-bit divisor. When the requested rate is large enough,
> 2ULL * req->rate can exceed 32 bits. On 32-bit platforms, do_div() silently
> truncates the 64-bit divisor to 32 bits, which could result in a divisor of 0.
> 

Will switch to use DIV64_U64_ROUND_CLOSEST() instead.

>> +
>> +	/* Clamp to valid range (1-255) */
>> +	div = clamp_t(unsigned long, div, 1, 255);
>> +
>> +	req->rate = DIV_ROUND_CLOSEST_ULL((u64)req->best_parent_rate, 2ULL * div);
>> +	return 0;
>> +}
>> +
>> +static int clk_pon_refclk_set_rate(struct clk_hw *hw, unsigned long rate,
>> +				   unsigned long parent_rate)
>> +{
>> +	struct clk_cmn_pll *pon_clk = to_clk_cmn_pll(hw);
>> +	unsigned long div;
>> +	int ret;
>> +
>> +	if (rate == 0)
>> +		return -EINVAL;
>> +
>> +	/* UNIPHY fixed mode (31.25 MHz) */
>> +	if (rate == 31250000UL)
>> +		return regmap_clear_bits(pon_clk->regmap, CMN_PLL_PON_CONFIG,
>> +					 CMN_PLL_PON_MODE_SEL);
> 
> [Severity: Medium]
> Is it intentional to bypass the analog soft reset here?
> 
> The soft reset is performed at the end of the function for PON mode. If
> entering UNIPHY mode also requires a soft reset to reliably latch or
> stabilize the new clock output, the hardware might be left in an
> inconsistent state.
> 

This is intentional — the analog soft reset will be removed from all
derived clock output paths entirely, not just for UNIPHY mode. Keeping
it risks disturbing other already-stable clocks that share the same PLL.

>> +
>> +	div = DIV_ROUND_CLOSEST_ULL((u64)parent_rate, 2ULL * rate);
> 
> [Severity: High]
> Can this cause a divide-by-zero panic on 32-bit platforms?
> 
> Similar to the determine_rate callback, the 64-bit expression 2ULL * rate
> is passed to a macro that expects a 32-bit divisor.
>

>> +	if (div == 0 || div > 255)
>> +		return -EINVAL;
>> +
>> +	/* Switch to PON mode */
>> +	ret = regmap_set_bits(pon_clk->regmap, CMN_PLL_PON_CONFIG,
>> +			      CMN_PLL_PON_MODE_SEL);
>> +	if (ret)
>> +		return ret;
>> +
>> +	ret = regmap_update_bits(pon_clk->regmap, CMN_PLL_PON_CONFIG,
>> +				 CMN_PLL_PON_DIV_CTRL,
>> +				 FIELD_PREP(CMN_PLL_PON_DIV_CTRL, div));
> 
> [Severity: Medium]
> Can this sequential update cause transient invalid clock states?
> 
> There is a brief window where the mode bit is set to PON mode, but the
> divider has not yet been updated. If the previous divider was 0 or
> inappropriate, it might expose downstream hardware to an invalid
> frequency or glitch.
> 

This is intentional and acceptable by design — the clock rate is
configured before the connected PON/UNIPHY is brought out of reset and
reconfigured.

>> +	if (ret)
>> +		return ret;
>> +
>> +	return clk_cmn_pll_ana_soft_reset(pon_clk->regmap);
>> +}
>