Open
Conversation
Fix three related bugs in clk_rcg2_calc_mnd() that cause the GP clock MND divider to produce incorrect frequencies and duty cycles: 1) n_max and n_candidate are declared as u16 but can hold values exceeding 65535 during computation. When mnd_width is 16 and mnd_max is 65535, even m=1 gives n_max=65536 which overflows u16 to 0, making "n_candidate < n_max" always false. Similarly n_candidate overflows on intermediate products (e.g., 15360 * 5 = 76800 wraps to 11264), causing the wrong value to be accepted as n. Fix by changing both n_max and n_candidate from u16 to u32. 2) n_max is computed as (m + mnd_max), which only accounts for the N register constraint (n - m must fit in mnd_width bits). However, the D register shares the same mnd_width bits but must store values up to 2*n for duty cycle control. When n > mnd_max/2, the D register cannot represent high duty cycles, silently clamping the maximum achievable duty cycle (e.g. to 85% instead of 99%). Fix by computing n_max as (mnd_max + 1) / 2, ensuring 2*n always fits within the D register's bit width. 3) When no pre-division is needed (pre_div stays at its initial value of 1), "pre_div > 1 ? pre_div : 0" sets f->pre_div to 0. The subsequent convert_to_reg_val() computes (0 * 2 - 1) which underflows the u8 pre_div field to 255, programming a 128x pre-divider into hardware. Fix by assigning pre_div unconditionally. Since pre_div is initialized to 1 and only multiplied, it is always >= 1. A value of 1 correctly converts to register value 1 via convert_to_reg_val(), which means no pre-division in calc_rate(). Example with parent=19.2 MHz XO, requesting 25 kHz: Before: m=1, n=48, pre_div=15 -> 26666 Hz (6.7% error) After: m=1, n=768, pre_div=1 -> 25000 Hz (exact) Fixes: 898b72f ("clk: qcom: gcc-sdm845: Add general purpose clock ops") Signed-off-by: Xilin Wu <sophon@radxa.com>
The duty cycle calculation in clk_rcg2_set_duty_cycle() computes "n * duty->num * 2" using u32 arithmetic. When n is large and duty->num is also large, the intermediate result overflows u32. For example, requesting 50% duty on a 1 kHz output derived from a 19.2 MHz parent gives n=19200, duty->num=500000, duty->den=1000000: 19200 * 500000 * 2 = 19,200,000,000 > U32_MAX (4,294,967,295) The truncated result produces a completely wrong duty cycle (5.26% instead of the requested 50%). Use DIV_ROUND_CLOSEST_ULL() with an explicit (u64) cast to prevent the overflow. Fixes: 7f891fa ("clk: qcom: clk-rcg2: Add support for duty-cycle for RCG") Signed-off-by: Xilin Wu <sophon@radxa.com>
Clock branches with extremely low rates (tens of Hz to low kHz) take much longer to toggle than the fixed 200 us timeout allows. A 1 kHz clock needs at least 3 ms (3 cycles) to toggle. Instead of increasing the timeout to a huge fixed value for all clocks, dynamically compute the required timeout based on the current clock rate, accounting for 3 cycles at the current clock rate. Based on a downstream patch by Mike Tipton: https://git.codelinaro.org/clo/la/kernel/qcom/-/commit/aa899c2d1fa31e247f04810f125ac9c60927c901 Fixes: 6e0ad1b ("clk: qcom: Add support for branches/gate clocks") Signed-off-by: Mike Tipton <quic_mdtipton@quicinc.com> Signed-off-by: Xilin Wu <sophon@radxa.com>
RCGs with extremely low rates (tens of Hz to low kHz) take much longer to update than the fixed 500 us timeout allows. A 1 kHz clock needs at least 3 ms (3 cycles) for the configuration handshake. Instead of increasing the timeout to a huge fixed value for all clocks, dynamically compute the required timeout based on both the old and new clock rates, accounting for 3 cycles at each rate. Based on a downstream patch by Mike Tipton: https://git.codelinaro.org/clo/la/kernel/qcom/-/commit/aa899c2d1fa31e247f04810f125ac9c60927c901 Fixes: bcd61c0 ("clk: qcom: Add support for root clock generators (RCGs)") Signed-off-by: Mike Tipton <quic_mdtipton@quicinc.com> Signed-off-by: Xilin Wu <sophon@radxa.com>
…y checks
The duty cycle boundary checks in clk_rcg2_set_duty_cycle() use
integer division to compare the 2d value against hardware limits:
if ((d / 2) > (n - m))
d = (n - m) * 2;
else if ((d / 2) < (m / 2))
d = m;
When d is odd, d/2 truncates, allowing values one beyond the hardware
maximum to pass. For example with n=7680, m=1, requesting 99.995%
duty:
d = 15359 (raw 2d value)
d / 2 = 7679 (truncated)
n - m = 7679
7679 > 7679 → false, check passes
But d=15359 exceeds the hardware limit of 2*(n-m)=15358. Writing this
invalid value causes the RCG to fail its configuration update, the
CMD_UPDATE bit never clears, and the clock output stops entirely.
The initial D value in __clk_rcg2_configure_mnd() correctly uses
direct comparison without division:
d_val = clamp_t(u32, d_val, f->m, 2 * (f->n - f->m));
Align set_duty_cycle() with the same bounds by comparing directly:
if (d > (n - m) * 2)
else if (d < m)
Fixes: 7f891fa ("clk: qcom: clk-rcg2: Add support for duty-cycle for RCG")
Signed-off-by: Xilin Wu <sophon@radxa.com>
The GP1/GP2/GP3 clock sources are general-purpose timer/PWM clocks that require runtime-computed MND divider values and duty cycle control. They are currently using clk_rcg2_ops with a frequency table containing only a few fixed entries (50/100/200 MHz), which: - Cannot produce arbitrary frequencies needed for PWM periods - Bypasses the MND divider (m=0, n=0), making duty cycle control impossible (MND is in bypass mode, set_duty_cycle returns -EINVAL) Switch to clk_rcg2_gp_ops which uses clk_rcg2_calc_mnd() to dynamically compute optimal M/N/pre_div values from any requested frequency, and empty the frequency table since it is not used by the GP ops path. Signed-off-by: Xilin Wu <sophon@radxa.com>
The clk-pwm driver cannot produce constant output levels (0% or 100% duty cycle, or disabled state) through the clock hardware alone - the actual pin level when the clock is off is undefined and hardware-dependent. Document optional gpios, pinctrl-names, pinctrl-0, and pinctrl-1 properties that allow the driver to switch the pin between clock function mux (for normal PWM output) and GPIO mode (to drive a deterministic constant level). Signed-off-by: Xilin Wu <sophon@radxa.com>
Update calc_rate docs to reflect, that pre_div is not pure divisor, but a register value, and requires conversion. Signed-off-by: Dzmitry Sankouski <dsankouski@gmail.com> Link: https://lore.kernel.org/r/20241118-starqltechn_integration_upstream-v8-1-ac8e36a3aa65@gmail.com Signed-off-by: Bjorn Andersson <andersson@kernel.org>
Teach the GP RCG path to evaluate all candidate parents, program the selected parent and M/N state in one configuration update, and expose a GP-specific clk_ops instance with set_rate_and_parent support. Signed-off-by: William Norman <nascs@radxa.com>
Switch the SA8775P GP clock sources over to the dedicated GP RCG ops and full parent map so they can take advantage of parent-aware rate selection instead of relying on the previous fixed-rate table. Signed-off-by: William Norman <nascs@radxa.com>
Disable the backing clock before changing its rate and duty cycle, then re-enable it after the new configuration is in place. When the request is already for an exact 50%% duty cycle, skip the redundant clk_set_duty_cycle() call and leave the clock's default 50%% output untouched. Signed-off-by: William Norman <nascs@radxa.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.