Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

clk: qcom: gcc-sdm845: Add general purpose clock ops

SDM845 has "General Purpose" clocks that can be muxed to
SoC pins to clock various external devices.
Those clocks may be used as e.g. PWM sources for external peripherals.

GPCLK can in theory have arbitrary value depending on the use case, so
the concept of frequency tables, used in rcg2 clock driver, is not
efficient, because it allows only defined frequencies.

Introduce clk_rcg2_gp_ops, which automatically calculate clock
mnd values for arbitrary clock rate. The calculation done as follows:
- upon determine rate request, we calculate m/n/pre_div as follows:
- find parent(from our client's assigned-clock-parent) rate
- find scaled rates by dividing rates on its greatest common divisor
- assign requested scaled rate to m
- factorize scaled parent rate, put multipliers to n till max value
(determined by mnd_width)
- validate calculated values with *_width:
- if doesn't fit, delete divisor and multiplier by 2 until fit
- return determined rate

Limitations:
- The driver doesn't select a parent clock (it may be selected by client
in device tree with assigned-clocks, assigned-clock-parents properties)

Signed-off-by: Dzmitry Sankouski <dsankouski@gmail.com>
Link: https://lore.kernel.org/r/20241118-starqltechn_integration_upstream-v8-3-ac8e36a3aa65@gmail.com
Signed-off-by: Bjorn Andersson <andersson@kernel.org>

authored by

Dzmitry Sankouski and committed by
Bjorn Andersson
898b72fa faddad52

+150 -8
+1
drivers/clk/qcom/clk-rcg.h
··· 189 189 container_of(to_clk_rcg2(_hw), struct clk_rcg2_gfx3d, rcg) 190 190 191 191 extern const struct clk_ops clk_rcg2_ops; 192 + extern const struct clk_ops clk_rcg2_gp_ops; 192 193 extern const struct clk_ops clk_rcg2_floor_ops; 193 194 extern const struct clk_ops clk_rcg2_fm_ops; 194 195 extern const struct clk_ops clk_rcg2_mux_closest_ops;
+146
drivers/clk/qcom/clk-rcg2.c
··· 8 8 #include <linux/err.h> 9 9 #include <linux/bug.h> 10 10 #include <linux/export.h> 11 + #include <linux/clk.h> 11 12 #include <linux/clk-provider.h> 12 13 #include <linux/delay.h> 13 14 #include <linux/rational.h> 14 15 #include <linux/regmap.h> 15 16 #include <linux/math64.h> 17 + #include <linux/gcd.h> 16 18 #include <linux/minmax.h> 17 19 #include <linux/slab.h> 18 20 ··· 34 32 35 33 #define CFG_REG 0x4 36 34 #define CFG_SRC_DIV_SHIFT 0 35 + #define CFG_SRC_DIV_LENGTH 8 37 36 #define CFG_SRC_SEL_SHIFT 8 38 37 #define CFG_SRC_SEL_MASK (0x7 << CFG_SRC_SEL_SHIFT) 39 38 #define CFG_MODE_SHIFT 12 ··· 149 146 return ret; 150 147 151 148 return update_config(rcg); 149 + } 150 + 151 + /** 152 + * convert_to_reg_val() - Convert divisor values to hardware values. 153 + * 154 + * @f: Frequency table with pure m/n/pre_div parameters. 155 + */ 156 + static void convert_to_reg_val(struct freq_tbl *f) 157 + { 158 + f->pre_div *= 2; 159 + f->pre_div -= 1; 152 160 } 153 161 154 162 /** ··· 416 402 return _freq_tbl_fm_determine_rate(hw, rcg->freq_multi_tbl, req); 417 403 } 418 404 405 + /** 406 + * clk_rcg2_split_div() - Split multiplier that doesn't fit in n neither in pre_div. 407 + * 408 + * @multiplier: Multiplier to split between n and pre_div. 409 + * @pre_div: Pointer to pre divisor value. 410 + * @n: Pointer to n divisor value. 411 + * @pre_div_max: Pre divisor maximum value. 412 + */ 413 + static inline void clk_rcg2_split_div(int multiplier, unsigned int *pre_div, 414 + u16 *n, unsigned int pre_div_max) 415 + { 416 + *n = mult_frac(multiplier * *n, *pre_div, pre_div_max); 417 + *pre_div = pre_div_max; 418 + } 419 + 420 + static void clk_rcg2_calc_mnd(u64 parent_rate, u64 rate, struct freq_tbl *f, 421 + unsigned int mnd_max, unsigned int pre_div_max) 422 + { 423 + int i = 2; 424 + unsigned int pre_div = 1; 425 + unsigned long rates_gcd, scaled_parent_rate; 426 + u16 m, n = 1, n_candidate = 1, n_max; 427 + 428 + rates_gcd = gcd(parent_rate, rate); 429 + m = div64_u64(rate, rates_gcd); 430 + scaled_parent_rate = div64_u64(parent_rate, rates_gcd); 431 + while (scaled_parent_rate > (mnd_max + m) * pre_div_max) { 432 + // we're exceeding divisor's range, trying lower scale. 433 + if (m > 1) { 434 + m--; 435 + scaled_parent_rate = mult_frac(scaled_parent_rate, m, (m + 1)); 436 + } else { 437 + // cannot lower scale, just set max divisor values. 438 + f->n = mnd_max + m; 439 + f->pre_div = pre_div_max; 440 + f->m = m; 441 + return; 442 + } 443 + } 444 + 445 + n_max = m + mnd_max; 446 + 447 + while (scaled_parent_rate > 1) { 448 + while (scaled_parent_rate % i == 0) { 449 + n_candidate *= i; 450 + if (n_candidate < n_max) 451 + n = n_candidate; 452 + else if (pre_div * i < pre_div_max) 453 + pre_div *= i; 454 + else 455 + clk_rcg2_split_div(i, &pre_div, &n, pre_div_max); 456 + 457 + scaled_parent_rate /= i; 458 + } 459 + i++; 460 + } 461 + 462 + f->m = m; 463 + f->n = n; 464 + f->pre_div = pre_div > 1 ? pre_div : 0; 465 + } 466 + 467 + static int clk_rcg2_determine_gp_rate(struct clk_hw *hw, 468 + struct clk_rate_request *req) 469 + { 470 + struct clk_rcg2 *rcg = to_clk_rcg2(hw); 471 + struct freq_tbl f_tbl = {}, *f = &f_tbl; 472 + int mnd_max = BIT(rcg->mnd_width) - 1; 473 + int hid_max = BIT(rcg->hid_width) - 1; 474 + struct clk_hw *parent; 475 + u64 parent_rate; 476 + 477 + parent = clk_hw_get_parent(hw); 478 + parent_rate = clk_get_rate(parent->clk); 479 + if (!parent_rate) 480 + return -EINVAL; 481 + 482 + clk_rcg2_calc_mnd(parent_rate, req->rate, f, mnd_max, hid_max / 2); 483 + convert_to_reg_val(f); 484 + req->rate = calc_rate(parent_rate, f->m, f->n, f->n, f->pre_div); 485 + 486 + return 0; 487 + } 488 + 419 489 static int __clk_rcg2_configure_parent(struct clk_rcg2 *rcg, u8 src, u32 *_cfg) 420 490 { 421 491 struct clk_hw *hw = &rcg->clkr.hw; ··· 597 499 return update_config(rcg); 598 500 } 599 501 502 + static int clk_rcg2_configure_gp(struct clk_rcg2 *rcg, const struct freq_tbl *f) 503 + { 504 + u32 cfg; 505 + int ret; 506 + 507 + ret = regmap_read(rcg->clkr.regmap, RCG_CFG_OFFSET(rcg), &cfg); 508 + if (ret) 509 + return ret; 510 + 511 + ret = __clk_rcg2_configure_mnd(rcg, f, &cfg); 512 + if (ret) 513 + return ret; 514 + 515 + ret = regmap_write(rcg->clkr.regmap, RCG_CFG_OFFSET(rcg), cfg); 516 + if (ret) 517 + return ret; 518 + 519 + return update_config(rcg); 520 + } 521 + 600 522 static int __clk_rcg2_set_rate(struct clk_hw *hw, unsigned long rate, 601 523 enum freq_policy policy) 602 524 { ··· 668 550 unsigned long parent_rate) 669 551 { 670 552 return __clk_rcg2_set_rate(hw, rate, CEIL); 553 + } 554 + 555 + static int clk_rcg2_set_gp_rate(struct clk_hw *hw, unsigned long rate, 556 + unsigned long parent_rate) 557 + { 558 + struct clk_rcg2 *rcg = to_clk_rcg2(hw); 559 + int mnd_max = BIT(rcg->mnd_width) - 1; 560 + int hid_max = BIT(rcg->hid_width) - 1; 561 + struct freq_tbl f_tbl = {}, *f = &f_tbl; 562 + int ret; 563 + 564 + clk_rcg2_calc_mnd(parent_rate, rate, f, mnd_max, hid_max / 2); 565 + convert_to_reg_val(f); 566 + ret = clk_rcg2_configure_gp(rcg, f); 567 + 568 + return ret; 671 569 } 672 570 673 571 static int clk_rcg2_set_floor_rate(struct clk_hw *hw, unsigned long rate, ··· 812 678 .set_duty_cycle = clk_rcg2_set_duty_cycle, 813 679 }; 814 680 EXPORT_SYMBOL_GPL(clk_rcg2_ops); 681 + 682 + const struct clk_ops clk_rcg2_gp_ops = { 683 + .is_enabled = clk_rcg2_is_enabled, 684 + .get_parent = clk_rcg2_get_parent, 685 + .set_parent = clk_rcg2_set_parent, 686 + .recalc_rate = clk_rcg2_recalc_rate, 687 + .determine_rate = clk_rcg2_determine_gp_rate, 688 + .set_rate = clk_rcg2_set_gp_rate, 689 + .get_duty_cycle = clk_rcg2_get_duty_cycle, 690 + .set_duty_cycle = clk_rcg2_set_duty_cycle, 691 + }; 692 + EXPORT_SYMBOL_GPL(clk_rcg2_gp_ops); 815 693 816 694 const struct clk_ops clk_rcg2_floor_ops = { 817 695 .is_enabled = clk_rcg2_is_enabled,
+3 -8
drivers/clk/qcom/gcc-sdm845.c
··· 284 284 }; 285 285 286 286 static const struct freq_tbl ftbl_gcc_gp1_clk_src[] = { 287 - F(19200000, P_BI_TCXO, 1, 0, 0), 288 - F(25000000, P_GPLL0_OUT_EVEN, 12, 0, 0), 289 - F(50000000, P_GPLL0_OUT_EVEN, 6, 0, 0), 290 - F(100000000, P_GPLL0_OUT_MAIN, 6, 0, 0), 291 - F(200000000, P_GPLL0_OUT_MAIN, 3, 0, 0), 292 287 { } 293 288 }; 294 289 ··· 297 302 .name = "gcc_gp1_clk_src", 298 303 .parent_data = gcc_parent_data_1, 299 304 .num_parents = ARRAY_SIZE(gcc_parent_data_1), 300 - .ops = &clk_rcg2_ops, 305 + .ops = &clk_rcg2_gp_ops, 301 306 }, 302 307 }; 303 308 ··· 311 316 .name = "gcc_gp2_clk_src", 312 317 .parent_data = gcc_parent_data_1, 313 318 .num_parents = ARRAY_SIZE(gcc_parent_data_1), 314 - .ops = &clk_rcg2_ops, 319 + .ops = &clk_rcg2_gp_ops, 315 320 }, 316 321 }; 317 322 ··· 325 330 .name = "gcc_gp3_clk_src", 326 331 .parent_data = gcc_parent_data_1, 327 332 .num_parents = ARRAY_SIZE(gcc_parent_data_1), 328 - .ops = &clk_rcg2_ops, 333 + .ops = &clk_rcg2_gp_ops, 329 334 }, 330 335 }; 331 336