From: sashiko-bot@kernel.org
To: "Long Zhao" <longzhao@ambarella.com>
Cc: robh@kernel.org, devicetree@vger.kernel.org, conor+dt@kernel.org
Subject: Re: [PATCH v2 06/10] clk: ambarella: add CV75 CCU driver
Date: Thu, 06 Aug 2026 09:45:53 +0000 [thread overview]
Message-ID: <20260806094554.CB0161F00A3A@smtp.kernel.org> (raw)
In-Reply-To: <20260806-longzhao-upstream-cv75-v2-v2-6-6b09707c5fe9@ambarella.com>
Thank you for your contribution! Sashiko AI review found 8 potential issue(s) to consider:
- [High] `do_div()` is incorrectly used on `unsigned long` variables, causing stack memory corruption on 32-bit architectures.
- [High] The generic divider implementation contains logic bugs that cause it to check the wrong bit for status, fail to disable clocks, and corrupt divider values.
- [High] A zero or invalid parent rate causes a fatal divide-by-zero panic during PLL parameter calculation.
- [High] Infinite loop in `amb_pll_calc_params` when `parent_rate` is smaller than the maximum post-divider.
- [Medium] `amb_pll_calc_params` omits static `fsdiv` and `fsout` dividers from its rational target approximation, resulting in incorrect rates.
- [Medium] `amb_pll_set_rate` fails to apply `pre_scaler` and `post_scaler` to target calculations, breaking rate configurations.
- [Medium] Stale hardware register read causes the incorrect VCO range to be selected for fractional PLL modes.
- [Medium] Integer PLL rates are unnecessarily restricted to coarse multiples of `parent_rate / 2`.
--
--- Patch [6]: [PATCH v2 06/10] clk: ambarella: add CV75 CCU driver ---
commit 3c34aa8f15f802b4bcbd9606eec16b27a37dd8d9
Author: Long Zhao <longzhao@ambarella.com>
clk: ambarella: add CV75 CCU driver
This commit adds a minimal Ambarella clock controller driver for the CV75 SoC,
providing core PLL, bus, and UART clocks required for early console bring-up.
> diff --git a/drivers/clk/ambarella/ccu_mux_div.c b/drivers/clk/ambarella/ccu_mux_div.c
> new file mode 100644
> index 0000000000000..33430506f8ed1
> --- /dev/null
> +++ b/drivers/clk/ambarella/ccu_mux_div.c
[ ... ]
> +static unsigned long amb_div_recalc_rate(struct clk_hw *hw,
> + unsigned long parent_rate)
> +{
> + struct amb_div *div = to_amb_div(hw);
> + unsigned long rate;
> + u32 val;
> +
> + regmap_read(div->map, div->offset, &val);
> +
> + if (val & BIT(div->width))
> + return 0;
[Severity: High]
Does this check the correct bit? The code checks BIT(div->width) directly
against the raw register value before shifting it by div->shift. If
div->shift is non-zero, this evaluates the wrong hardware bit.
> +
> + val >>= div->shift;
> + val &= clk_div_mask(div->width);
> +
> + rate = divider_recalc_rate(hw, parent_rate, val, NULL,
> + div->flags, div->width);
> + if (div->fix_divider)
> + do_div(rate, div->fix_divider);
[Severity: High]
Can this cause a stack buffer overflow on 32-bit architectures? The do_div
macro expects a 64-bit u64 pointer. Because this driver can be built for
32-bit targets via COMPILE_TEST, unsigned long is 32 bits on those systems.
Passing a 32-bit variable causes the macro to write 64 bits, overwriting
adjacent stack memory.
> +
> + return rate;
> +}
> +
> +static int amb_div_determine_rate(struct clk_hw *hw,
> + struct clk_rate_request *req)
> +{
[ ... ]
> + req->rate = scaled.rate;
> + req->best_parent_rate = scaled.best_parent_rate;
> + req->best_parent_hw = scaled.best_parent_hw;
> + if (div->fix_divider)
> + do_div(req->rate, div->fix_divider);
[Severity: High]
Could this corrupt the adjacent fields in the clk_rate_request struct? The
do_div macro writes 64 bits to req->rate, which is an unsigned long and
thus only 32 bits on 32-bit builds.
> +
> + return 0;
> +}
> +
> +static int amb_div_set_rate(struct clk_hw *hw, unsigned long rate,
> + unsigned long parent_rate)
> +{
> + struct amb_div *div = to_amb_div(hw);
> + int val, mask;
> +
> + if (div->fix_divider)
> + rate *= div->fix_divider;
> +
> + if (!rate) {
> + val = BIT(div->width);
> + } else {
> + val = divider_get_val(rate, parent_rate, NULL,
> + div->width, div->flags);
> + if (val < 0)
> + return val;
> + }
> +
> + mask = (div->flags & CLK_DIVIDER_ONE_BASED) ?
> + clk_div_mask(div->width + 1) : clk_div_mask(div->width);
> +
> + regmap_update_bits(div->map, div->offset, mask << div->shift,
> + val << div->shift);
[Severity: High]
Can this prevent the clock from being disabled? The update mask for
zero-based dividers uses clk_div_mask(div->width), which excludes
BIT(div->width). When setting a rate of 0, val evaluates to BIT(div->width),
but it gets masked out here, silently leaving the clock enabled with a zero
divider.
> +
> + if (!(div->flags & CLK_DIVIDER_ONE_BASED)) {
> + regmap_update_bits(div->map, div->offset, BIT(0), BIT(0));
> + regmap_update_bits(div->map, div->offset, BIT(0), 0);
> + }
[Severity: High]
Will toggling BIT(0) directly on the register corrupt the actual divider
field if div->shift is 0?
> diff --git a/drivers/clk/ambarella/ccu_pll.c b/drivers/clk/ambarella/ccu_pll.c
> new file mode 100644
> index 0000000000000..1293836e222e5
> --- /dev/null
> +++ b/drivers/clk/ambarella/ccu_pll.c
[ ... ]
> +static int amb_pll_determine_rate(struct clk_hw *hw,
> + struct clk_rate_request *req)
> +{
> + struct amb_pll *pll = to_amb_pll(hw);
> + unsigned long half_refclk = req->best_parent_rate / 2;
> +
> + if (pll->frac_mode)
> + return 0;
> +
> + if (!half_refclk)
> + return 0;
[Severity: High]
If the best parent rate is 0 or 1, half_refclk evaluates to 0, returning 0
(success). Doesn't this allow a 0 parent rate to reach amb_pll_set_rate,
which then calls amb_pll_calc_params and triggers a divide-by-zero panic?
> +
> + req->rate = roundup(req->rate, half_refclk);
> +
> + return 0;
> +}
[Severity: Medium]
Does this artificially restrict the integer PLL solver? By forcibly rounding
up the requested frequency to multiples of parent_rate / 2, this prevents
the framework from utilizing the fine-grained post-divider (sout) inside
amb_pll_calc_params to generate precise integer ratios.
> +static int amb_pll_calc_params(struct amb_pll *pll, unsigned long rate,
> + unsigned long parent_rate, u32 ctrl2_val,
> + u32 *intp, u32 *sdiv, u32 *sout,
> + u32 *vcodiv, u32 *fsdiv, u32 *fsout)
> +{
[ ... ]
> + max_numerator = min(128UL, max_numerator);
> + if (!max_numerator)
> + return -EINVAL;
> +
> + max_denominator = AMB_PLL_MAX_SOUT;
> + rate_tmp = rate;
> + rational_best_approximation(rate_tmp, parent_rate, max_numerator,
> + max_denominator, &intp_ul, &sout_ul);
[Severity: Medium]
Should the rate approximation factor in the static fsdiv and fsout dividers?
The rational_best_approximation is called assuming the output rate
relationship is solely parent_rate * intp / sout. Because the true hardware
equation includes fsdiv and fsout, omitting them from the target ratio
approximation results in incorrectly calculated parameters.
> +
> + while (parent_rate * *fsdiv * intp_ul * *sdiv / *fsout / sout_ul >
> + rate) {
> + unsigned long resolution = parent_rate / AMB_PLL_MAX_SOUT;
> +
> + if (rate_tmp <= resolution)
> + return -EINVAL;
> +
> + rate_tmp -= resolution;
[Severity: High]
Is there a risk of an infinite loop here if parent_rate is very low? If
parent_rate is less than AMB_PLL_MAX_SOUT (16), resolution truncates to 0.
Subtracting 0 leaves rate_tmp unchanged, causing the loop condition to
evaluate as permanently true.
> + rational_best_approximation(rate_tmp, parent_rate,
> + max_numerator, max_denominator,
> + &intp_ul, &sout_ul);
> + }
[ ... ]
> +static int amb_pll_set_rate(struct clk_hw *hw, unsigned long rate,
> + unsigned long parent_rate)
> +{
> + struct amb_pll *pll = to_amb_pll(hw);
[ ... ]
> + ret = amb_pll_calc_params(pll, rate, parent_rate, ctrl2_val,
> + &intp, &sdiv, &sout, &vcodiv, &fsdiv,
> + &fsout);
[Severity: Medium]
Should this adjust the requested rate and parent_rate using the hardware
pre_scaler and post_scaler? Unlike amb_pll_recalc_rate which reads these
scalers from hardware registers, amb_pll_set_rate passes the raw rate and
parent_rate directly into amb_pll_calc_params, which will configure the PLL
clocks to incorrect frequencies.
> + if (ret)
> + return ret;
> +
> + if (soc_data->ctrl2_val)
> + regmap_write(pll->map, reg[CTRL2_OFFSET], soc_data->ctrl2_val);
> +
> + ctrl_val = ((intp - 1) & 0x7f) << 24;
> + ctrl_val |= ((sdiv - 1) & 0xf) << 12;
> + ctrl_val |= ((sout - 1) & 0xf) << 16;
> + if (soc_data->pll_version >= 2) {
> + ctrl_val |= vcodiv == 2 ? soc_data->vcodiv_val : 0;
> + ctrl_val |= fsdiv == 2 ? soc_data->fsdiv_val : 0;
> + ctrl_val |= fsout == 2 ? soc_data->fsout_val : 0;
> + }
> +
> + regmap_write(pll->map, reg[CTRL_OFFSET], ctrl_val);
> + regmap_write(pll->map, reg[FRAC_OFFSET], 0);
> +
> + old_rate = amb_pll_recalc_rate(hw, parent_rate) * pll->fix_divider;
> + rate_tmp = old_rate > rate ? 0 : rate - old_rate;
> + if (rate_tmp && pll->frac_mode) {
> + u64 dividend, divider;
> +
> + dividend = (u64)rate_tmp * sout * fsout;
> + dividend <<= 32;
> + divider = (u64)sdiv * fsdiv * parent_rate;
> + frac_val = DIV_ROUND_CLOSEST_ULL(dividend, divider);
> + regmap_write(pll->map, reg[FRAC_OFFSET], frac_val);
> + ctrl_val |= CTRL_FRAC_MODE;
> + }
> +
> + if (soc_data->pll_version >= 2) {
[ ... ]
> + } else {
> + u32 fvco_mhz, range;
> +
> + fvco_mhz = amb_pll_calc_vco(pll, parent_rate) / 1000000UL;
[Severity: Medium]
Does amb_pll_calc_vco() use a stale hardware register read to determine the
VCO frequency? The CTRL_FRAC_MODE bit is enabled in the local ctrl_val
variable above, but not yet written to the hardware register. When
amb_pll_calc_vco() reads the register directly, it misses the uncommitted
fractional change, which can lead to the driver selecting the wrong
VCO_RANGE boundary.
> + for (range = 0; range < ARRAY_SIZE(soc_data->vco_range);
> + range++) {
> + if (fvco_mhz > soc_data->vco_range[range])
> + break;
> + }
> + range = ARRAY_SIZE(soc_data->vco_range) - range - 1;
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260806-longzhao-upstream-cv75-v2-v2-0-6b09707c5fe9@ambarella.com?part=6
next prev parent reply other threads:[~2026-08-06 9:45 UTC|newest]
Thread overview: 27+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-06 9:34 [PATCH v2 00/10] Ambarella CV75 SoC minimal bring-up Long Zhao via B4 Relay
2026-08-06 9:34 ` [PATCH v2 01/10] dt-bindings: arm: add Ambarella CV75 platforms Long Zhao via B4 Relay
2026-08-07 6:00 ` Krzysztof Kozlowski
2026-08-06 9:34 ` [PATCH v2 02/10] dt-bindings: soc: add Ambarella secure scratchpad Long Zhao via B4 Relay
2026-08-07 6:01 ` Krzysztof Kozlowski
2026-08-07 8:29 ` Long Zhao
2026-08-06 9:34 ` [PATCH v2 03/10] dt-bindings: clock: add Ambarella CV75 RCT clock controller Long Zhao via B4 Relay
2026-08-06 9:42 ` sashiko-bot
2026-08-06 10:56 ` Rob Herring (Arm)
2026-08-07 6:02 ` Krzysztof Kozlowski
2026-08-06 9:34 ` [PATCH v2 04/10] dt-bindings: pinctrl: add Ambarella CV75 pinctrl Long Zhao via B4 Relay
2026-08-06 9:41 ` sashiko-bot
2026-08-07 6:06 ` Krzysztof Kozlowski
2026-08-06 9:34 ` [PATCH v2 05/10] dt-bindings: serial: add Ambarella UART Long Zhao via B4 Relay
2026-08-06 9:34 ` [PATCH v2 06/10] clk: ambarella: add CV75 CCU driver Long Zhao via B4 Relay
2026-08-06 9:45 ` sashiko-bot [this message]
2026-08-06 9:34 ` [PATCH v2 07/10] pinctrl: ambarella: add Ambarella pin controller Long Zhao via B4 Relay
2026-08-06 9:46 ` sashiko-bot
2026-08-07 17:45 ` Linus Walleij
2026-08-06 9:34 ` [PATCH v2 08/10] serial: ambarella: add Ambarella UART driver Long Zhao via B4 Relay
2026-08-06 9:49 ` sashiko-bot
2026-08-07 6:18 ` Jiri Slaby
2026-08-07 18:37 ` Linus Walleij
2026-08-06 9:34 ` [PATCH v2 09/10] arm64: ambarella: add ARCH_AMBARELLA and CV75 EVK DT Long Zhao via B4 Relay
2026-08-06 9:51 ` sashiko-bot
2026-08-07 6:11 ` Krzysztof Kozlowski
2026-08-06 9:34 ` [PATCH v2 10/10] MAINTAINERS: add ARM/AMBARELLA SoC support Long Zhao via B4 Relay
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260806094554.CB0161F00A3A@smtp.kernel.org \
--to=sashiko-bot@kernel.org \
--cc=conor+dt@kernel.org \
--cc=devicetree@vger.kernel.org \
--cc=longzhao@ambarella.com \
--cc=robh@kernel.org \
--cc=sashiko-reviews@lists.linux.dev \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox