diff --git a/Documentation/devicetree/bindings/pwm/clk-pwm.yaml b/Documentation/devicetree/bindings/pwm/clk-pwm.yaml index ec1768291503c..2a0e3e02d27bf 100644 --- a/Documentation/devicetree/bindings/pwm/clk-pwm.yaml +++ b/Documentation/devicetree/bindings/pwm/clk-pwm.yaml @@ -15,6 +15,11 @@ description: | It's often possible to control duty-cycle of such clocks which makes them suitable for generating PWM signal. + Optionally, a GPIO and pinctrl states can be provided. When a constant + output level is needed (0%, 100%, or disabled), the pin is switched to + GPIO mode to drive the level directly. For normal PWM output the pin is + switched back to its clock function mux. + allOf: - $ref: pwm.yaml# @@ -29,6 +34,26 @@ properties: "#pwm-cells": const: 2 + gpios: + description: + Optional GPIO used to drive a constant level when the PWM output is + disabled or set to 0% / 100% duty cycle. When provided, pinctrl states + "default" (clock mux) and "gpio" must also be defined. + maxItems: 1 + + pinctrl-names: true + + pinctrl-0: + description: Pin configuration for clock function mux (normal PWM). + maxItems: 1 + + pinctrl-1: + description: Pin configuration for GPIO mode (constant level output). + maxItems: 1 + +dependencies: + gpios: [ pinctrl-0, pinctrl-1 ] + unevaluatedProperties: false required: @@ -41,6 +66,15 @@ examples: compatible = "clk-pwm"; #pwm-cells = <2>; clocks = <&gcc 0>; - pinctrl-names = "default"; + }; + + - | + pwm { + compatible = "clk-pwm"; + #pwm-cells = <2>; + clocks = <&gcc 0>; + pinctrl-names = "default", "gpio"; pinctrl-0 = <&pwm_clk_flash_default>; + pinctrl-1 = <&pwm_clk_flash_gpio>; + gpios = <&tlmm 32 0>; }; diff --git a/drivers/clk/qcom/clk-branch.c b/drivers/clk/qcom/clk-branch.c index e213a0284c21c..5632f45131d98 100644 --- a/drivers/clk/qcom/clk-branch.c +++ b/drivers/clk/qcom/clk-branch.c @@ -60,9 +60,27 @@ static bool clk_branch2_check_halt(const struct clk_branch *br, bool enabling) return (val & CBCR_CLK_OFF) == (invert ? 0 : CBCR_CLK_OFF); } +static int get_branch_timeout(const struct clk_branch *br) +{ + unsigned long rate; + int timeout; + + /* + * The time it takes a clock branch to toggle is roughly 3 clock cycles. + */ + rate = clk_hw_get_rate(&br->clkr.hw); + if (!rate) + return 200; + + timeout = 3 * (USEC_PER_SEC / rate); + + return max(timeout, 200); +} + static int clk_branch_wait(const struct clk_branch *br, bool enabling, bool (check_halt)(const struct clk_branch *, bool)) { + int timeout, count; bool voted = br->halt_check & BRANCH_VOTED; const char *name = clk_hw_get_name(&br->clkr.hw); @@ -78,9 +96,9 @@ static int clk_branch_wait(const struct clk_branch *br, bool enabling, } else if (br->halt_check == BRANCH_HALT_ENABLE || br->halt_check == BRANCH_HALT || (enabling && voted)) { - int count = 200; + timeout = get_branch_timeout(br); - while (count-- > 0) { + for (count = timeout; count > 0; count--) { if (check_halt(br, enabling)) return 0; udelay(1); diff --git a/drivers/clk/qcom/clk-rcg.h b/drivers/clk/qcom/clk-rcg.h index 7d0f925960559..8a48b318344aa 100644 --- a/drivers/clk/qcom/clk-rcg.h +++ b/drivers/clk/qcom/clk-rcg.h @@ -141,6 +141,7 @@ extern const struct clk_ops clk_dyn_rcg_ops; * @clkr: regmap clock handle * @cfg_off: defines the cfg register offset from the CMD_RCGR + CFG_REG * @parked_cfg: cached value of the CFG register for parked RCGs + * @configured_freq: last configured frequency, used for timeout calculation * @hw_clk_ctrl: whether to enable hardware clock control */ struct clk_rcg2 { @@ -153,6 +154,7 @@ struct clk_rcg2 { struct clk_regmap clkr; u8 cfg_off; u32 parked_cfg; + unsigned long configured_freq; bool hw_clk_ctrl; }; @@ -168,6 +170,7 @@ struct clk_rcg2_gfx3d { container_of(to_clk_rcg2(_hw), struct clk_rcg2_gfx3d, rcg) extern const struct clk_ops clk_rcg2_ops; +extern const struct clk_ops clk_rcg2_gp_ops; extern const struct clk_ops clk_rcg2_floor_ops; extern const struct clk_ops clk_rcg2_mux_closest_ops; extern const struct clk_ops clk_edp_pixel_ops; diff --git a/drivers/clk/qcom/clk-rcg2.c b/drivers/clk/qcom/clk-rcg2.c index fae1c07982aba..8838a6ff92423 100644 --- a/drivers/clk/qcom/clk-rcg2.c +++ b/drivers/clk/qcom/clk-rcg2.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -108,9 +109,27 @@ static u8 clk_rcg2_get_parent(struct clk_hw *hw) return __clk_rcg2_get_parent(hw, cfg); } +static int get_update_timeout(const struct clk_rcg2 *rcg) +{ + int timeout = 0; + unsigned long current_freq; + + /* + * The time it takes an RCG to update is roughly 3 clock cycles of the + * old and new clock rates. + */ + current_freq = clk_hw_get_rate(&rcg->clkr.hw); + if (current_freq) + timeout += 3 * (USEC_PER_SEC / current_freq); + if (rcg->configured_freq) + timeout += 3 * (USEC_PER_SEC / rcg->configured_freq); + + return max(timeout, 500); +} + static int update_config(struct clk_rcg2 *rcg) { - int count, ret; + int timeout, count, ret; u32 cmd; struct clk_hw *hw = &rcg->clkr.hw; const char *name = clk_hw_get_name(hw); @@ -120,8 +139,10 @@ static int update_config(struct clk_rcg2 *rcg) if (ret) return ret; + timeout = get_update_timeout(rcg); + /* Wait for update to take effect */ - for (count = 500; count > 0; count--) { + for (count = timeout; count > 0; count--) { ret = regmap_read(rcg->clkr.regmap, rcg->cmd_rcgr + CMD_REG, &cmd); if (ret) return ret; @@ -148,12 +169,21 @@ static int clk_rcg2_set_parent(struct clk_hw *hw, u8 index) return update_config(rcg); } -/* - * Calculate m/n:d rate +/** + * calc_rate() - Calculate rate based on m/n:d values + * + * @rate: Parent rate. + * @m: Multiplier. + * @n: Divisor. + * @mode: Use zero to ignore m/n calculation. + * @hid_div: Pre divisor register value. Pre divisor value + * relates to hid_div as pre_div = (hid_div + 1) / 2. + * + * Return calculated rate according to formula: * * parent_rate m * rate = ----------- x --- - * hid_div n + * pre_div n */ static unsigned long calc_rate(unsigned long rate, u32 m, u32 n, u32 mode, u32 hid_div) @@ -319,8 +349,6 @@ static int __clk_rcg2_configure(struct clk_rcg2 *rcg, const struct freq_tbl *f, cfg |= rcg->parent_map[index].cfg << CFG_SRC_SEL_SHIFT; if (rcg->mnd_width && f->n && (f->m != f->n)) cfg |= CFG_MODE_DUAL_EDGE; - if (rcg->hw_clk_ctrl) - cfg |= CFG_HW_CLK_CTRL_MASK; *_cfg &= ~mask; *_cfg |= cfg; @@ -328,11 +356,163 @@ static int __clk_rcg2_configure(struct clk_rcg2 *rcg, const struct freq_tbl *f, return 0; } +static void convert_to_reg_val(struct freq_tbl *f) +{ + if (f->pre_div) + f->pre_div = 2 * f->pre_div - 1; +} + +static inline void clk_rcg2_split_div(int multiplier, unsigned int *pre_div, + u16 *n, unsigned int pre_div_max) +{ + *n = mult_frac(multiplier * *n, *pre_div, pre_div_max); + *pre_div = pre_div_max; +} + +static void clk_rcg2_calc_mnd(u64 parent_rate, u64 rate, struct freq_tbl *f, + unsigned int mnd_max, unsigned int pre_div_max) +{ + int i = 2; + unsigned int pre_div = 1; + unsigned long rates_gcd, scaled_parent_rate; + u16 m, n = 1, n_candidate = 1, n_max; + + if (!parent_rate || !rate) + return; + + rates_gcd = gcd(parent_rate, rate); + m = div64_u64(rate, rates_gcd); + scaled_parent_rate = div64_u64(parent_rate, rates_gcd); + while (scaled_parent_rate > (mnd_max + m) * pre_div_max) { + if (m > 1) { + m--; + scaled_parent_rate = mult_frac(scaled_parent_rate, m, (m + 1)); + } else { + f->n = mnd_max + m; + f->pre_div = pre_div_max; + f->m = m; + return; + } + } + + n_max = m + mnd_max; + + while (scaled_parent_rate > 1) { + while (scaled_parent_rate % i == 0) { + n_candidate *= i; + if (n_candidate < n_max) + n = n_candidate; + else if (pre_div * i < pre_div_max) + pre_div *= i; + else + clk_rcg2_split_div(i, &pre_div, &n, pre_div_max); + + scaled_parent_rate /= i; + } + i++; + } + + f->m = m; + f->n = n; + f->pre_div = pre_div; +} + +static int clk_rcg2_determine_gp_rate(struct clk_hw *hw, + struct clk_rate_request *req) +{ + struct clk_rcg2 *rcg = to_clk_rcg2(hw); + int mnd_max = BIT(rcg->mnd_width) - 1; + int hid_max = BIT(rcg->hid_width) - 1; + int num_parents = clk_hw_get_num_parents(hw); + struct clk_hw *best_parent = NULL, *best_over_parent = NULL; + u64 best_parent_rate = 0, best_over_parent_rate = 0; + unsigned long best_rate = 0, best_over_rate = ULONG_MAX; + int i; + + /* + * Iterate over all candidate parents and pick the closest achievable + * rate. Prefer the best rate at or below the request. If no parent can + * satisfy the request without overshooting, fall back to the smallest + * achievable rate above the request. + */ + for (i = 0; i < num_parents; i++) { + struct freq_tbl f_tbl = {}, *f = &f_tbl; + struct clk_hw *parent = clk_hw_get_parent_by_index(hw, i); + u64 prate; + unsigned long achieved; + + if (!parent) + continue; + prate = clk_hw_get_rate(parent); + if (!prate || req->rate > prate) + continue; + + clk_rcg2_calc_mnd(prate, req->rate, f, mnd_max, hid_max / 2); + if (!f->m || !f->n || f->m > f->n) + continue; + convert_to_reg_val(f); + achieved = calc_rate(prate, f->m, f->n, f->n, f->pre_div); + if (!achieved || achieved > prate) + continue; + + if (achieved <= req->rate) { + if (!best_parent || achieved > best_rate || + (achieved == best_rate && prate < best_parent_rate)) { + best_parent = parent; + best_parent_rate = prate; + best_rate = achieved; + } + } else if (!best_over_parent || achieved < best_over_rate || + (achieved == best_over_rate && prate < best_over_parent_rate)) { + best_over_parent = parent; + best_over_parent_rate = prate; + best_over_rate = achieved; + } + } + + if (!best_parent) { + best_parent = best_over_parent; + best_parent_rate = best_over_parent_rate; + best_rate = best_over_rate; + } + + if (!best_parent) + return -EINVAL; + + req->best_parent_hw = best_parent; + req->best_parent_rate = best_parent_rate; + req->rate = best_rate; + + return 0; +} + static int clk_rcg2_configure(struct clk_rcg2 *rcg, const struct freq_tbl *f) { u32 cfg; int ret; + ret = regmap_read(rcg->clkr.regmap, RCG_CFG_OFFSET(rcg), &cfg); + if (ret) + return ret; + + ret = __clk_rcg2_configure(rcg, f, &cfg); + if (ret) + return ret; + + ret = regmap_write(rcg->clkr.regmap, RCG_CFG_OFFSET(rcg), cfg); + if (ret) + return ret; + + rcg->configured_freq = f->freq; + + return update_config(rcg); +} + +static int clk_rcg2_configure_gp(struct clk_rcg2 *rcg, const struct freq_tbl *f) +{ + u32 cfg; + int ret; + ret = regmap_read(rcg->clkr.regmap, RCG_CFG_OFFSET(rcg), &cfg); if (ret) return ret; @@ -377,6 +557,60 @@ static int clk_rcg2_set_rate(struct clk_hw *hw, unsigned long rate, return __clk_rcg2_set_rate(hw, rate, CEIL); } +static int clk_rcg2_set_gp_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct clk_rcg2 *rcg = to_clk_rcg2(hw); + int mnd_max = BIT(rcg->mnd_width) - 1; + int hid_max = BIT(rcg->hid_width) - 1; + struct freq_tbl f_tbl = {}, *f = &f_tbl; + u8 index; + int ret; + + if (!parent_rate || rate > parent_rate) + return -EINVAL; + + clk_rcg2_calc_mnd(parent_rate, rate, f, mnd_max, hid_max / 2); + if (!f->m || !f->n || f->m > f->n) + return -EINVAL; + + index = clk_rcg2_get_parent(hw); + if (index >= clk_hw_get_num_parents(hw)) + return -EINVAL; + f->src = rcg->parent_map[index].src; + convert_to_reg_val(f); + rcg->configured_freq = rate; + ret = clk_rcg2_configure_gp(rcg, f); + + return ret; +} + +static int clk_rcg2_set_gp_rate_and_parent(struct clk_hw *hw, + unsigned long rate, unsigned long parent_rate, u8 index) +{ + struct clk_rcg2 *rcg = to_clk_rcg2(hw); + int mnd_max = BIT(rcg->mnd_width) - 1; + int hid_max = BIT(rcg->hid_width) - 1; + struct freq_tbl f_tbl = {}, *f = &f_tbl; + int ret; + + if (!parent_rate || rate > parent_rate) + return -EINVAL; + if (index >= clk_hw_get_num_parents(hw)) + return -EINVAL; + + clk_rcg2_calc_mnd(parent_rate, rate, f, mnd_max, hid_max / 2); + if (!f->m || !f->n || f->m > f->n) + return -EINVAL; + + f->src = rcg->parent_map[index].src; + convert_to_reg_val(f); + rcg->configured_freq = rate; + ret = clk_rcg2_configure_gp(rcg, f); + + return ret; +} + static int clk_rcg2_set_floor_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) { @@ -434,7 +668,7 @@ static int clk_rcg2_get_duty_cycle(struct clk_hw *hw, struct clk_duty *duty) static int clk_rcg2_set_duty_cycle(struct clk_hw *hw, struct clk_duty *duty) { struct clk_rcg2 *rcg = to_clk_rcg2(hw); - u32 notn_m, n, m, d, not2d, mask, duty_per, cfg; + u32 notn_m, n, m, d, not2d, mask, cfg; int ret; /* Duty-cycle cannot be modified for non-MND RCGs */ @@ -453,10 +687,8 @@ static int clk_rcg2_set_duty_cycle(struct clk_hw *hw, struct clk_duty *duty) n = (~(notn_m) + m) & mask; - duty_per = (duty->num * 100) / duty->den; - /* Calculate 2d value */ - d = DIV_ROUND_CLOSEST(n * duty_per * 2, 100); + d = DIV_ROUND_CLOSEST_ULL((u64)n * duty->num * 2, duty->den); /* * Check bit widths of 2d. If D is too big reduce duty cycle. @@ -492,6 +724,19 @@ const struct clk_ops clk_rcg2_ops = { }; EXPORT_SYMBOL_GPL(clk_rcg2_ops); +const struct clk_ops clk_rcg2_gp_ops = { + .is_enabled = clk_rcg2_is_enabled, + .get_parent = clk_rcg2_get_parent, + .set_parent = clk_rcg2_set_parent, + .recalc_rate = clk_rcg2_recalc_rate, + .determine_rate = clk_rcg2_determine_gp_rate, + .set_rate = clk_rcg2_set_gp_rate, + .set_rate_and_parent = clk_rcg2_set_gp_rate_and_parent, + .get_duty_cycle = clk_rcg2_get_duty_cycle, + .set_duty_cycle = clk_rcg2_set_duty_cycle, +}; +EXPORT_SYMBOL_GPL(clk_rcg2_gp_ops); + const struct clk_ops clk_rcg2_floor_ops = { .is_enabled = clk_rcg2_is_enabled, .get_parent = clk_rcg2_get_parent, diff --git a/drivers/clk/qcom/gcc-sa8775p.c b/drivers/clk/qcom/gcc-sa8775p.c index 11c72a10ba698..a5e574e0c7167 100644 --- a/drivers/clk/qcom/gcc-sa8775p.c +++ b/drivers/clk/qcom/gcc-sa8775p.c @@ -721,8 +721,6 @@ static struct clk_rcg2 gcc_emac1_rgmii_clk_src = { }; static const struct freq_tbl ftbl_gcc_gp1_clk_src[] = { - F(100000000, P_GCC_GPLL0_OUT_MAIN, 6, 0, 0), - F(200000000, P_GCC_GPLL0_OUT_MAIN, 3, 0, 0), { } }; @@ -730,13 +728,13 @@ static struct clk_rcg2 gcc_gp1_clk_src = { .cmd_rcgr = 0x70004, .mnd_width = 16, .hid_width = 5, - .parent_map = gcc_parent_map_2, + .parent_map = gcc_parent_map_4, .freq_tbl = ftbl_gcc_gp1_clk_src, .clkr.hw.init = &(const struct clk_init_data){ .name = "gcc_gp1_clk_src", - .parent_data = gcc_parent_data_2, - .num_parents = ARRAY_SIZE(gcc_parent_data_2), - .ops = &clk_rcg2_shared_ops, + .parent_data = gcc_parent_data_4, + .num_parents = ARRAY_SIZE(gcc_parent_data_4), + .ops = &clk_rcg2_gp_ops, }, }; @@ -744,13 +742,13 @@ static struct clk_rcg2 gcc_gp2_clk_src = { .cmd_rcgr = 0x71004, .mnd_width = 16, .hid_width = 5, - .parent_map = gcc_parent_map_2, + .parent_map = gcc_parent_map_4, .freq_tbl = ftbl_gcc_gp1_clk_src, .clkr.hw.init = &(const struct clk_init_data){ .name = "gcc_gp2_clk_src", - .parent_data = gcc_parent_data_2, - .num_parents = ARRAY_SIZE(gcc_parent_data_2), - .ops = &clk_rcg2_shared_ops, + .parent_data = gcc_parent_data_4, + .num_parents = ARRAY_SIZE(gcc_parent_data_4), + .ops = &clk_rcg2_gp_ops, }, }; @@ -758,13 +756,13 @@ static struct clk_rcg2 gcc_gp3_clk_src = { .cmd_rcgr = 0x62004, .mnd_width = 16, .hid_width = 5, - .parent_map = gcc_parent_map_2, + .parent_map = gcc_parent_map_4, .freq_tbl = ftbl_gcc_gp1_clk_src, .clkr.hw.init = &(const struct clk_init_data){ .name = "gcc_gp3_clk_src", - .parent_data = gcc_parent_data_2, - .num_parents = ARRAY_SIZE(gcc_parent_data_2), - .ops = &clk_rcg2_shared_ops, + .parent_data = gcc_parent_data_4, + .num_parents = ARRAY_SIZE(gcc_parent_data_4), + .ops = &clk_rcg2_gp_ops, }, }; @@ -772,13 +770,13 @@ static struct clk_rcg2 gcc_gp4_clk_src = { .cmd_rcgr = 0x1e004, .mnd_width = 16, .hid_width = 5, - .parent_map = gcc_parent_map_2, + .parent_map = gcc_parent_map_4, .freq_tbl = ftbl_gcc_gp1_clk_src, .clkr.hw.init = &(const struct clk_init_data){ .name = "gcc_gp4_clk_src", - .parent_data = gcc_parent_data_2, - .num_parents = ARRAY_SIZE(gcc_parent_data_2), - .ops = &clk_rcg2_shared_ops, + .parent_data = gcc_parent_data_4, + .num_parents = ARRAY_SIZE(gcc_parent_data_4), + .ops = &clk_rcg2_gp_ops, }, }; @@ -786,13 +784,13 @@ static struct clk_rcg2 gcc_gp5_clk_src = { .cmd_rcgr = 0x1f004, .mnd_width = 16, .hid_width = 5, - .parent_map = gcc_parent_map_2, + .parent_map = gcc_parent_map_4, .freq_tbl = ftbl_gcc_gp1_clk_src, .clkr.hw.init = &(const struct clk_init_data){ .name = "gcc_gp5_clk_src", - .parent_data = gcc_parent_data_2, - .num_parents = ARRAY_SIZE(gcc_parent_data_2), - .ops = &clk_rcg2_shared_ops, + .parent_data = gcc_parent_data_4, + .num_parents = ARRAY_SIZE(gcc_parent_data_4), + .ops = &clk_rcg2_gp_ops, }, }; diff --git a/drivers/clk/qcom/gcc-sc7280.c b/drivers/clk/qcom/gcc-sc7280.c index ad7b8463f840e..d601ed989882b 100644 --- a/drivers/clk/qcom/gcc-sc7280.c +++ b/drivers/clk/qcom/gcc-sc7280.c @@ -457,9 +457,6 @@ static struct clk_regmap_mux gcc_usb3_sec_phy_pipe_clk_src = { }; static const struct freq_tbl ftbl_gcc_gp1_clk_src[] = { - F(50000000, P_GCC_GPLL0_OUT_EVEN, 6, 0, 0), - F(100000000, P_GCC_GPLL0_OUT_EVEN, 3, 0, 0), - F(200000000, P_GCC_GPLL0_OUT_ODD, 1, 0, 0), { } }; @@ -473,7 +470,7 @@ static struct clk_rcg2 gcc_gp1_clk_src = { .name = "gcc_gp1_clk_src", .parent_data = gcc_parent_data_4, .num_parents = ARRAY_SIZE(gcc_parent_data_4), - .ops = &clk_rcg2_ops, + .ops = &clk_rcg2_gp_ops, }, }; @@ -487,7 +484,7 @@ static struct clk_rcg2 gcc_gp2_clk_src = { .name = "gcc_gp2_clk_src", .parent_data = gcc_parent_data_4, .num_parents = ARRAY_SIZE(gcc_parent_data_4), - .ops = &clk_rcg2_ops, + .ops = &clk_rcg2_gp_ops, }, }; @@ -501,7 +498,7 @@ static struct clk_rcg2 gcc_gp3_clk_src = { .name = "gcc_gp3_clk_src", .parent_data = gcc_parent_data_4, .num_parents = ARRAY_SIZE(gcc_parent_data_4), - .ops = &clk_rcg2_ops, + .ops = &clk_rcg2_gp_ops, }, }; diff --git a/drivers/pwm/pwm-clk.c b/drivers/pwm/pwm-clk.c index 9dd88b386907c..7fb6a16038012 100644 --- a/drivers/pwm/pwm-clk.c +++ b/drivers/pwm/pwm-clk.c @@ -39,30 +39,30 @@ static int pwm_clk_apply(struct pwm_chip *chip, struct pwm_device *pwm, const struct pwm_state *state) { struct pwm_clk_chip *pcchip = to_pwm_clk_chip(chip); + bool was_enabled = pwm->state.enabled; + bool needs_duty_program; int ret; u32 rate; u64 period = state->period; u64 duty_cycle = state->duty_cycle; if (!state->enabled) { - if (pwm->state.enabled) { + if (was_enabled) { clk_disable(pcchip->clk); pcchip->clk_enabled = false; } return 0; - } else if (!pwm->state.enabled) { - ret = clk_enable(pcchip->clk); - if (ret) - return ret; - pcchip->clk_enabled = true; } /* - * We have to enable the clk before setting the rate and duty_cycle, - * that however results in a window where the clk is on with a - * (potentially) different setting. Also setting period and duty_cycle - * are two separate calls, so that probably isn't atomic either. + * Some clock providers cannot safely update their rate while the output + * is running. Quiesce the clock first, program rate and duty cycle, and + * then re-enable it with the new settings. */ + if (was_enabled) { + clk_disable(pcchip->clk); + pcchip->clk_enabled = false; + } rate = DIV64_U64_ROUND_UP(NSEC_PER_SEC, period); ret = clk_set_rate(pcchip->clk, rate); @@ -72,7 +72,24 @@ static int pwm_clk_apply(struct pwm_chip *chip, struct pwm_device *pwm, if (state->polarity == PWM_POLARITY_INVERSED) duty_cycle = period - duty_cycle; - return clk_set_duty_cycle(pcchip->clk, duty_cycle, period); + /* + * clk_set_rate() programs Qualcomm GP RCGs with a default 50% duty + * cycle. Avoid a redundant second update when the request is already + * exactly 50%, because some instances time out on that extra reprogram. + */ + needs_duty_program = duty_cycle != period - duty_cycle; + if (needs_duty_program) { + ret = clk_set_duty_cycle(pcchip->clk, duty_cycle, period); + if (ret) + return ret; + } + + ret = clk_enable(pcchip->clk); + if (ret) + return ret; + + pcchip->clk_enabled = true; + return 0; } static const struct pwm_ops pwm_clk_ops = {