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

clk: renesas: rzv2h: Add support for DSI clocks

Add support for PLLDSI and its post-dividers in the RZ/V2H CPG driver and
export helper APIs for use by the DSI driver.

Introduce per-PLL-DSI state in the CPG private structure and provide a
set of helper functions that find valid PLL parameter combinations for
a requested frequency. The new helpers are rzv2h_get_pll_pars(),
rzv2h_get_pll_div_pars(), rzv2h_get_pll_divs_pars() and
rzv2h_get_pll_dtable_pars() and they are exported in the "RZV2H_CPG"
namespace for use by other consumers (notably the DSI driver). These
helpers perform iterative searches over PLL parameters (M, K, P, S)
and optional post-dividers and return the best match (or an exact
match when possible).

Move PLL/CLK related limits and parameter types into the shared
include (include/linux/clk/renesas.h) by adding struct rzv2h_pll_limits,
struct rzv2h_pll_pars and struct rzv2h_pll_div_pars plus the
RZV2H_CPG_PLL_DSI_LIMITS() helper macro to define DSI PLL limits.

This change centralises the PLLDSI algorithms so the CPG and DSI
drivers compute PLL parameters consistently and allows the DSI driver
to accurately request rates and program its PLL.

Co-developed-by: Fabrizio Castro <fabrizio.castro.jz@renesas.com>
Signed-off-by: Fabrizio Castro <fabrizio.castro.jz@renesas.com>
Signed-off-by: Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>
Acked-by: Tomi Valkeinen <tomi.valkeinen+renesas@ideasonboard.com>
Reviewed-by: Geert Uytterhoeven <geert+renesas@glider.be>
Link: https://patch.msgid.link/20251015192611.241920-4-prabhakar.mahadev-lad.rj@bp.renesas.com
Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>

authored by

Lad Prabhakar and committed by
Geert Uytterhoeven
f864e4b7 a8943f7f

+659 -2
+497
drivers/clk/renesas/rzv2h-cpg.c
··· 14 14 #include <linux/bitfield.h> 15 15 #include <linux/clk.h> 16 16 #include <linux/clk-provider.h> 17 + #include <linux/clk/renesas.h> 17 18 #include <linux/delay.h> 18 19 #include <linux/init.h> 19 20 #include <linux/iopoll.h> 21 + #include <linux/limits.h> 22 + #include <linux/math.h> 23 + #include <linux/math64.h> 24 + #include <linux/minmax.h> 20 25 #include <linux/mod_devicetable.h> 21 26 #include <linux/module.h> 22 27 #include <linux/of.h> ··· 31 26 #include <linux/refcount.h> 32 27 #include <linux/reset-controller.h> 33 28 #include <linux/string_choices.h> 29 + #include <linux/units.h> 34 30 35 31 #include <dt-bindings/clock/renesas-cpg-mssr.h> 36 32 ··· 53 47 54 48 #define CPG_PLL_STBY(x) ((x)) 55 49 #define CPG_PLL_STBY_RESETB BIT(0) 50 + #define CPG_PLL_STBY_SSC_EN BIT(2) 56 51 #define CPG_PLL_STBY_RESETB_WEN BIT(16) 52 + #define CPG_PLL_STBY_SSC_EN_WEN BIT(18) 57 53 #define CPG_PLL_CLK1(x) ((x) + 0x004) 58 54 #define CPG_PLL_CLK1_KDIV GENMASK(31, 16) 59 55 #define CPG_PLL_CLK1_MDIV GENMASK(15, 6) ··· 73 65 74 66 #define CPG_CLKSTATUS0 (0x700) 75 67 68 + /* On RZ/G3E SoC we have two DSI PLLs */ 69 + #define MAX_CPG_DSI_PLL 2 70 + 71 + /** 72 + * struct rzv2h_pll_dsi_info - PLL DSI information, holds the limits and parameters 73 + * 74 + * @pll_dsi_limits: PLL DSI parameters limits 75 + * @pll_dsi_parameters: Calculated PLL DSI parameters 76 + * @req_pll_dsi_rate: Requested PLL DSI rate 77 + */ 78 + struct rzv2h_pll_dsi_info { 79 + const struct rzv2h_pll_limits *pll_dsi_limits; 80 + struct rzv2h_pll_div_pars pll_dsi_parameters; 81 + unsigned long req_pll_dsi_rate; 82 + }; 83 + 76 84 /** 77 85 * struct rzv2h_cpg_priv - Clock Pulse Generator Private Data 78 86 * ··· 104 80 * @ff_mod_status_ops: Fixed Factor Module Status Clock operations 105 81 * @mstop_count: Array of mstop values 106 82 * @rcdev: Reset controller entity 83 + * @pll_dsi_info: Array of PLL DSI information, holds the limits and parameters 107 84 */ 108 85 struct rzv2h_cpg_priv { 109 86 struct device *dev; ··· 123 98 atomic_t *mstop_count; 124 99 125 100 struct reset_controller_dev rcdev; 101 + 102 + struct rzv2h_pll_dsi_info pll_dsi_info[MAX_CPG_DSI_PLL]; 126 103 }; 127 104 128 105 #define rcdev_to_priv(x) container_of(x, struct rzv2h_cpg_priv, rcdev) ··· 194 167 195 168 #define to_rzv2h_ff_mod_status_clk(_hw) \ 196 169 container_of(_hw, struct rzv2h_ff_mod_status_clk, fix.hw) 170 + 171 + /** 172 + * struct rzv2h_plldsi_div_clk - PLL DSI DDIV clock 173 + * 174 + * @dtable: divider table 175 + * @priv: CPG private data 176 + * @hw: divider clk 177 + * @ddiv: divider configuration 178 + */ 179 + struct rzv2h_plldsi_div_clk { 180 + const struct clk_div_table *dtable; 181 + struct rzv2h_cpg_priv *priv; 182 + struct clk_hw hw; 183 + struct ddiv ddiv; 184 + }; 185 + 186 + #define to_plldsi_div_clk(_hw) \ 187 + container_of(_hw, struct rzv2h_plldsi_div_clk, hw) 188 + 189 + #define RZ_V2H_OSC_CLK_IN_MEGA (24 * MEGA) 190 + #define RZV2H_MAX_DIV_TABLES (16) 191 + 192 + /** 193 + * rzv2h_get_pll_pars - Finds the best combination of PLL parameters 194 + * for a given frequency. 195 + * 196 + * @limits: Pointer to the structure containing the limits for the PLL parameters 197 + * @pars: Pointer to the structure where the best calculated PLL parameters values 198 + * will be stored 199 + * @freq_millihz: Target output frequency in millihertz 200 + * 201 + * This function calculates the best set of PLL parameters (M, K, P, S) to achieve 202 + * the desired frequency. 203 + * There is no direct formula to calculate the PLL parameters, as it's an open 204 + * system of equations, therefore this function uses an iterative approach to 205 + * determine the best solution. The best solution is one that minimizes the error 206 + * (desired frequency - actual frequency). 207 + * 208 + * Return: true if a valid set of parameters values is found, false otherwise. 209 + */ 210 + bool rzv2h_get_pll_pars(const struct rzv2h_pll_limits *limits, 211 + struct rzv2h_pll_pars *pars, u64 freq_millihz) 212 + { 213 + u64 fout_min_millihz = mul_u32_u32(limits->fout.min, MILLI); 214 + u64 fout_max_millihz = mul_u32_u32(limits->fout.max, MILLI); 215 + struct rzv2h_pll_pars p, best; 216 + 217 + if (freq_millihz > fout_max_millihz || 218 + freq_millihz < fout_min_millihz) 219 + return false; 220 + 221 + /* Initialize best error to maximum possible value */ 222 + best.error_millihz = S64_MAX; 223 + 224 + for (p.p = limits->p.min; p.p <= limits->p.max; p.p++) { 225 + u32 fref = RZ_V2H_OSC_CLK_IN_MEGA / p.p; 226 + u16 divider; 227 + 228 + for (divider = 1 << limits->s.min, p.s = limits->s.min; 229 + p.s <= limits->s.max; p.s++, divider <<= 1) { 230 + for (p.m = limits->m.min; p.m <= limits->m.max; p.m++) { 231 + u64 output_m, output_k_range; 232 + s64 pll_k, output_k; 233 + u64 fvco, output; 234 + 235 + /* 236 + * The frequency generated by the PLL + divider 237 + * is calculated as follows: 238 + * 239 + * With: 240 + * Freq = Ffout = Ffvco / 2^(pll_s) 241 + * Ffvco = (pll_m + (pll_k / 65536)) * Ffref 242 + * Ffref = 24MHz / pll_p 243 + * 244 + * Freq can also be rewritten as: 245 + * Freq = Ffvco / 2^(pll_s) 246 + * = ((pll_m + (pll_k / 65536)) * Ffref) / 2^(pll_s) 247 + * = (pll_m * Ffref) / 2^(pll_s) + ((pll_k / 65536) * Ffref) / 2^(pll_s) 248 + * = output_m + output_k 249 + * 250 + * Every parameter has been determined at this 251 + * point, but pll_k. 252 + * 253 + * Considering that: 254 + * limits->k.min <= pll_k <= limits->k.max 255 + * Then: 256 + * -0.5 <= (pll_k / 65536) < 0.5 257 + * Therefore: 258 + * -Ffref / (2 * 2^(pll_s)) <= output_k < Ffref / (2 * 2^(pll_s)) 259 + */ 260 + 261 + /* Compute output M component (in mHz) */ 262 + output_m = DIV_ROUND_CLOSEST_ULL(mul_u32_u32(p.m, fref) * MILLI, 263 + divider); 264 + /* Compute range for output K (in mHz) */ 265 + output_k_range = DIV_ROUND_CLOSEST_ULL(mul_u32_u32(fref, MILLI), 266 + 2 * divider); 267 + /* 268 + * No point in continuing if we can't achieve 269 + * the desired frequency 270 + */ 271 + if (freq_millihz < (output_m - output_k_range) || 272 + freq_millihz >= (output_m + output_k_range)) { 273 + continue; 274 + } 275 + 276 + /* 277 + * Compute the K component 278 + * 279 + * Since: 280 + * Freq = output_m + output_k 281 + * Then: 282 + * output_k = Freq - output_m 283 + * = ((pll_k / 65536) * Ffref) / 2^(pll_s) 284 + * Therefore: 285 + * pll_k = (output_k * 65536 * 2^(pll_s)) / Ffref 286 + */ 287 + output_k = freq_millihz - output_m; 288 + pll_k = div_s64(output_k * 65536ULL * divider, 289 + fref); 290 + pll_k = DIV_S64_ROUND_CLOSEST(pll_k, MILLI); 291 + 292 + /* Validate K value within allowed limits */ 293 + if (pll_k < limits->k.min || 294 + pll_k > limits->k.max) 295 + continue; 296 + 297 + p.k = pll_k; 298 + 299 + /* Compute (Ffvco * 65536) */ 300 + fvco = mul_u32_u32(p.m * 65536 + p.k, fref); 301 + if (fvco < mul_u32_u32(limits->fvco.min, 65536) || 302 + fvco > mul_u32_u32(limits->fvco.max, 65536)) 303 + continue; 304 + 305 + /* PLL_M component of (output * 65536 * PLL_P) */ 306 + output = mul_u32_u32(p.m * 65536, RZ_V2H_OSC_CLK_IN_MEGA); 307 + /* PLL_K component of (output * 65536 * PLL_P) */ 308 + output += p.k * RZ_V2H_OSC_CLK_IN_MEGA; 309 + /* Make it in mHz */ 310 + output *= MILLI; 311 + output = DIV_U64_ROUND_CLOSEST(output, 65536 * p.p * divider); 312 + 313 + /* Check output frequency against limits */ 314 + if (output < fout_min_millihz || 315 + output > fout_max_millihz) 316 + continue; 317 + 318 + p.error_millihz = freq_millihz - output; 319 + p.freq_millihz = output; 320 + 321 + /* If an exact match is found, return immediately */ 322 + if (p.error_millihz == 0) { 323 + *pars = p; 324 + return true; 325 + } 326 + 327 + /* Update best match if error is smaller */ 328 + if (abs(best.error_millihz) > abs(p.error_millihz)) 329 + best = p; 330 + } 331 + } 332 + } 333 + 334 + /* If no valid parameters were found, return false */ 335 + if (best.error_millihz == S64_MAX) 336 + return false; 337 + 338 + *pars = best; 339 + return true; 340 + } 341 + EXPORT_SYMBOL_NS_GPL(rzv2h_get_pll_pars, "RZV2H_CPG"); 342 + 343 + /* 344 + * rzv2h_get_pll_divs_pars - Finds the best combination of PLL parameters 345 + * and divider value for a given frequency. 346 + * 347 + * @limits: Pointer to the structure containing the limits for the PLL parameters 348 + * @pars: Pointer to the structure where the best calculated PLL parameters and 349 + * divider values will be stored 350 + * @table: Pointer to the array of valid divider values 351 + * @table_size: Size of the divider values array 352 + * @freq_millihz: Target output frequency in millihertz 353 + * 354 + * This function calculates the best set of PLL parameters (M, K, P, S) and divider 355 + * value to achieve the desired frequency. See rzv2h_get_pll_pars() for more details 356 + * on how the PLL parameters are calculated. 357 + * 358 + * freq_millihz is the desired frequency generated by the PLL followed by a 359 + * a gear. 360 + */ 361 + bool rzv2h_get_pll_divs_pars(const struct rzv2h_pll_limits *limits, 362 + struct rzv2h_pll_div_pars *pars, 363 + const u8 *table, u8 table_size, u64 freq_millihz) 364 + { 365 + struct rzv2h_pll_div_pars p, best; 366 + 367 + best.div.error_millihz = S64_MAX; 368 + p.div.error_millihz = S64_MAX; 369 + for (unsigned int i = 0; i < table_size; i++) { 370 + if (!rzv2h_get_pll_pars(limits, &p.pll, freq_millihz * table[i])) 371 + continue; 372 + 373 + p.div.divider_value = table[i]; 374 + p.div.freq_millihz = DIV_U64_ROUND_CLOSEST(p.pll.freq_millihz, table[i]); 375 + p.div.error_millihz = freq_millihz - p.div.freq_millihz; 376 + 377 + if (p.div.error_millihz == 0) { 378 + *pars = p; 379 + return true; 380 + } 381 + 382 + if (abs(best.div.error_millihz) > abs(p.div.error_millihz)) 383 + best = p; 384 + } 385 + 386 + if (best.div.error_millihz == S64_MAX) 387 + return false; 388 + 389 + *pars = best; 390 + return true; 391 + } 392 + EXPORT_SYMBOL_NS_GPL(rzv2h_get_pll_divs_pars, "RZV2H_CPG"); 393 + 394 + static unsigned long rzv2h_cpg_plldsi_div_recalc_rate(struct clk_hw *hw, 395 + unsigned long parent_rate) 396 + { 397 + struct rzv2h_plldsi_div_clk *dsi_div = to_plldsi_div_clk(hw); 398 + struct rzv2h_cpg_priv *priv = dsi_div->priv; 399 + struct ddiv ddiv = dsi_div->ddiv; 400 + u32 div; 401 + 402 + div = readl(priv->base + ddiv.offset); 403 + div >>= ddiv.shift; 404 + div &= clk_div_mask(ddiv.width); 405 + div = dsi_div->dtable[div].div; 406 + 407 + return DIV_ROUND_CLOSEST_ULL(parent_rate, div); 408 + } 409 + 410 + static int rzv2h_cpg_plldsi_div_determine_rate(struct clk_hw *hw, 411 + struct clk_rate_request *req) 412 + { 413 + struct rzv2h_plldsi_div_clk *dsi_div = to_plldsi_div_clk(hw); 414 + struct pll_clk *pll_clk = to_pll(clk_hw_get_parent(hw)); 415 + struct rzv2h_cpg_priv *priv = dsi_div->priv; 416 + u8 table[RZV2H_MAX_DIV_TABLES] = { 0 }; 417 + struct rzv2h_pll_div_pars *dsi_params; 418 + struct rzv2h_pll_dsi_info *dsi_info; 419 + const struct clk_div_table *div; 420 + unsigned int i = 0; 421 + u64 rate_millihz; 422 + 423 + dsi_info = &priv->pll_dsi_info[pll_clk->pll.instance]; 424 + dsi_params = &dsi_info->pll_dsi_parameters; 425 + 426 + rate_millihz = mul_u32_u32(req->rate, MILLI); 427 + if (rate_millihz == dsi_params->div.error_millihz + dsi_params->div.freq_millihz) 428 + goto exit_determine_rate; 429 + 430 + for (div = dsi_div->dtable; div->div; div++) { 431 + if (i >= RZV2H_MAX_DIV_TABLES) 432 + return -EINVAL; 433 + table[i++] = div->div; 434 + } 435 + 436 + if (!rzv2h_get_pll_divs_pars(dsi_info->pll_dsi_limits, dsi_params, table, i, 437 + rate_millihz)) { 438 + dev_err(priv->dev, "failed to determine rate for req->rate: %lu\n", 439 + req->rate); 440 + return -EINVAL; 441 + } 442 + 443 + exit_determine_rate: 444 + req->rate = DIV_ROUND_CLOSEST_ULL(dsi_params->div.freq_millihz, MILLI); 445 + req->best_parent_rate = req->rate * dsi_params->div.divider_value; 446 + dsi_info->req_pll_dsi_rate = req->best_parent_rate; 447 + 448 + return 0; 449 + } 450 + 451 + static int rzv2h_cpg_plldsi_div_set_rate(struct clk_hw *hw, 452 + unsigned long rate, 453 + unsigned long parent_rate) 454 + { 455 + struct rzv2h_plldsi_div_clk *dsi_div = to_plldsi_div_clk(hw); 456 + struct pll_clk *pll_clk = to_pll(clk_hw_get_parent(hw)); 457 + struct rzv2h_cpg_priv *priv = dsi_div->priv; 458 + struct rzv2h_pll_div_pars *dsi_params; 459 + struct rzv2h_pll_dsi_info *dsi_info; 460 + struct ddiv ddiv = dsi_div->ddiv; 461 + const struct clk_div_table *clkt; 462 + bool divider_found = false; 463 + u32 val, shift; 464 + 465 + dsi_info = &priv->pll_dsi_info[pll_clk->pll.instance]; 466 + dsi_params = &dsi_info->pll_dsi_parameters; 467 + 468 + for (clkt = dsi_div->dtable; clkt->div; clkt++) { 469 + if (clkt->div == dsi_params->div.divider_value) { 470 + divider_found = true; 471 + break; 472 + } 473 + } 474 + 475 + if (!divider_found) 476 + return -EINVAL; 477 + 478 + shift = ddiv.shift; 479 + val = readl(priv->base + ddiv.offset) | DDIV_DIVCTL_WEN(shift); 480 + val &= ~(clk_div_mask(ddiv.width) << shift); 481 + val |= clkt->val << shift; 482 + writel(val, priv->base + ddiv.offset); 483 + 484 + return 0; 485 + } 486 + 487 + static const struct clk_ops rzv2h_cpg_plldsi_div_ops = { 488 + .recalc_rate = rzv2h_cpg_plldsi_div_recalc_rate, 489 + .determine_rate = rzv2h_cpg_plldsi_div_determine_rate, 490 + .set_rate = rzv2h_cpg_plldsi_div_set_rate, 491 + }; 492 + 493 + static struct clk * __init 494 + rzv2h_cpg_plldsi_div_clk_register(const struct cpg_core_clk *core, 495 + struct rzv2h_cpg_priv *priv) 496 + { 497 + struct rzv2h_plldsi_div_clk *clk_hw_data; 498 + struct clk **clks = priv->clks; 499 + struct clk_init_data init; 500 + const struct clk *parent; 501 + const char *parent_name; 502 + struct clk_hw *clk_hw; 503 + int ret; 504 + 505 + parent = clks[core->parent]; 506 + if (IS_ERR(parent)) 507 + return ERR_CAST(parent); 508 + 509 + clk_hw_data = devm_kzalloc(priv->dev, sizeof(*clk_hw_data), GFP_KERNEL); 510 + if (!clk_hw_data) 511 + return ERR_PTR(-ENOMEM); 512 + 513 + clk_hw_data->priv = priv; 514 + clk_hw_data->ddiv = core->cfg.ddiv; 515 + clk_hw_data->dtable = core->dtable; 516 + 517 + parent_name = __clk_get_name(parent); 518 + init.name = core->name; 519 + init.ops = &rzv2h_cpg_plldsi_div_ops; 520 + init.flags = core->flag; 521 + init.parent_names = &parent_name; 522 + init.num_parents = 1; 523 + 524 + clk_hw = &clk_hw_data->hw; 525 + clk_hw->init = &init; 526 + 527 + ret = devm_clk_hw_register(priv->dev, clk_hw); 528 + if (ret) 529 + return ERR_PTR(ret); 530 + 531 + return clk_hw->clk; 532 + } 533 + 534 + static int rzv2h_cpg_plldsi_determine_rate(struct clk_hw *hw, 535 + struct clk_rate_request *req) 536 + { 537 + struct pll_clk *pll_clk = to_pll(hw); 538 + struct rzv2h_cpg_priv *priv = pll_clk->priv; 539 + struct rzv2h_pll_dsi_info *dsi_info; 540 + u64 rate_millihz; 541 + 542 + dsi_info = &priv->pll_dsi_info[pll_clk->pll.instance]; 543 + /* check if the divider has already invoked the algorithm */ 544 + if (req->rate == dsi_info->req_pll_dsi_rate) 545 + return 0; 546 + 547 + /* If the req->rate doesn't match we do the calculation assuming there is no divider */ 548 + rate_millihz = mul_u32_u32(req->rate, MILLI); 549 + if (!rzv2h_get_pll_pars(dsi_info->pll_dsi_limits, 550 + &dsi_info->pll_dsi_parameters.pll, rate_millihz)) { 551 + dev_err(priv->dev, 552 + "failed to determine rate for req->rate: %lu\n", 553 + req->rate); 554 + return -EINVAL; 555 + } 556 + 557 + req->rate = DIV_ROUND_CLOSEST_ULL(dsi_info->pll_dsi_parameters.pll.freq_millihz, MILLI); 558 + dsi_info->req_pll_dsi_rate = req->rate; 559 + 560 + return 0; 561 + } 562 + 563 + static int rzv2h_cpg_pll_set_rate(struct pll_clk *pll_clk, 564 + struct rzv2h_pll_pars *params, 565 + bool ssc_disable) 566 + { 567 + struct rzv2h_cpg_priv *priv = pll_clk->priv; 568 + u16 offset = pll_clk->pll.offset; 569 + u32 val; 570 + int ret; 571 + 572 + /* Put PLL into standby mode */ 573 + writel(CPG_PLL_STBY_RESETB_WEN, priv->base + CPG_PLL_STBY(offset)); 574 + ret = readl_poll_timeout_atomic(priv->base + CPG_PLL_MON(offset), 575 + val, !(val & CPG_PLL_MON_LOCK), 576 + 100, 2000); 577 + if (ret) { 578 + dev_err(priv->dev, "Failed to put PLLDSI into standby mode"); 579 + return ret; 580 + } 581 + 582 + /* Output clock setting 1 */ 583 + writel(FIELD_PREP(CPG_PLL_CLK1_KDIV, (u16)params->k) | 584 + FIELD_PREP(CPG_PLL_CLK1_MDIV, params->m) | 585 + FIELD_PREP(CPG_PLL_CLK1_PDIV, params->p), 586 + priv->base + CPG_PLL_CLK1(offset)); 587 + 588 + /* Output clock setting 2 */ 589 + val = readl(priv->base + CPG_PLL_CLK2(offset)); 590 + writel((val & ~CPG_PLL_CLK2_SDIV) | FIELD_PREP(CPG_PLL_CLK2_SDIV, params->s), 591 + priv->base + CPG_PLL_CLK2(offset)); 592 + 593 + /* Put PLL to normal mode */ 594 + if (ssc_disable) 595 + val = CPG_PLL_STBY_SSC_EN_WEN; 596 + else 597 + val = CPG_PLL_STBY_SSC_EN_WEN | CPG_PLL_STBY_SSC_EN; 598 + writel(val | CPG_PLL_STBY_RESETB_WEN | CPG_PLL_STBY_RESETB, 599 + priv->base + CPG_PLL_STBY(offset)); 600 + 601 + /* PLL normal mode transition, output clock stability check */ 602 + ret = readl_poll_timeout_atomic(priv->base + CPG_PLL_MON(offset), 603 + val, (val & CPG_PLL_MON_LOCK), 604 + 100, 2000); 605 + if (ret) { 606 + dev_err(priv->dev, "Failed to put PLLDSI into normal mode"); 607 + return ret; 608 + } 609 + 610 + return 0; 611 + } 612 + 613 + static int rzv2h_cpg_plldsi_set_rate(struct clk_hw *hw, unsigned long rate, 614 + unsigned long parent_rate) 615 + { 616 + struct pll_clk *pll_clk = to_pll(hw); 617 + struct rzv2h_pll_dsi_info *dsi_info; 618 + struct rzv2h_cpg_priv *priv = pll_clk->priv; 619 + 620 + dsi_info = &priv->pll_dsi_info[pll_clk->pll.instance]; 621 + 622 + return rzv2h_cpg_pll_set_rate(pll_clk, &dsi_info->pll_dsi_parameters.pll, true); 623 + } 197 624 198 625 static int rzv2h_cpg_pll_clk_is_enabled(struct clk_hw *hw) 199 626 { ··· 719 238 return DIV_ROUND_CLOSEST_ULL(rate, FIELD_GET(CPG_PLL_CLK1_PDIV, clk1)); 720 239 } 721 240 241 + static const struct clk_ops rzv2h_cpg_plldsi_ops = { 242 + .recalc_rate = rzv2h_cpg_pll_clk_recalc_rate, 243 + .determine_rate = rzv2h_cpg_plldsi_determine_rate, 244 + .set_rate = rzv2h_cpg_plldsi_set_rate, 245 + }; 246 + 722 247 static const struct clk_ops rzv2h_cpg_pll_ops = { 723 248 .is_enabled = rzv2h_cpg_pll_clk_is_enabled, 724 249 .enable = rzv2h_cpg_pll_clk_enable, ··· 750 263 pll_clk = devm_kzalloc(dev, sizeof(*pll_clk), GFP_KERNEL); 751 264 if (!pll_clk) 752 265 return ERR_PTR(-ENOMEM); 266 + 267 + if (core->type == CLK_TYPE_PLLDSI) 268 + priv->pll_dsi_info[core->cfg.pll.instance].pll_dsi_limits = 269 + core->cfg.pll.limits; 753 270 754 271 parent_name = __clk_get_name(parent); 755 272 init.name = core->name; ··· 1078 587 break; 1079 588 case CLK_TYPE_SMUX: 1080 589 clk = rzv2h_cpg_mux_clk_register(core, priv); 590 + break; 591 + case CLK_TYPE_PLLDSI: 592 + clk = rzv2h_cpg_pll_clk_register(core, priv, &rzv2h_cpg_plldsi_ops); 593 + break; 594 + case CLK_TYPE_PLLDSI_DIV: 595 + clk = rzv2h_cpg_plldsi_div_clk_register(core, priv); 1081 596 break; 1082 597 default: 1083 598 goto fail;
+17 -2
drivers/clk/renesas/rzv2h-cpg.h
··· 22 22 unsigned int offset:9; 23 23 unsigned int has_clkn:1; 24 24 unsigned int instance:2; 25 + const struct rzv2h_pll_limits *limits; 25 26 }; 26 27 27 - #define PLL_PACK(_offset, _has_clkn, _instance) \ 28 + #define PLL_PACK_LIMITS(_offset, _has_clkn, _instance, _limits) \ 28 29 ((struct pll){ \ 29 30 .offset = _offset, \ 30 31 .has_clkn = _has_clkn, \ 31 - .instance = _instance \ 32 + .instance = _instance, \ 33 + .limits = _limits \ 32 34 }) 35 + 36 + #define PLL_PACK(_offset, _has_clkn, _instance) \ 37 + PLL_PACK_LIMITS(_offset, _has_clkn, _instance, NULL) 33 38 34 39 #define PLLCA55 PLL_PACK(0x60, 1, 0) 35 40 #define PLLGPU PLL_PACK(0x120, 1, 0) ··· 196 191 CLK_TYPE_PLL, 197 192 CLK_TYPE_DDIV, /* Dynamic Switching Divider */ 198 193 CLK_TYPE_SMUX, /* Static Mux */ 194 + CLK_TYPE_PLLDSI, /* PLLDSI */ 195 + CLK_TYPE_PLLDSI_DIV, /* PLLDSI divider */ 199 196 }; 200 197 201 198 #define DEF_TYPE(_name, _id, _type...) \ ··· 228 221 .num_parents = ARRAY_SIZE(_parent_names), \ 229 222 .flag = CLK_SET_RATE_PARENT, \ 230 223 .mux_flags = CLK_MUX_HIWORD_MASK) 224 + #define DEF_PLLDSI(_name, _id, _parent, _pll_packed) \ 225 + DEF_TYPE(_name, _id, CLK_TYPE_PLLDSI, .parent = _parent, .cfg.pll = _pll_packed) 226 + #define DEF_PLLDSI_DIV(_name, _id, _parent, _ddiv_packed, _dtable) \ 227 + DEF_TYPE(_name, _id, CLK_TYPE_PLLDSI_DIV, \ 228 + .cfg.ddiv = _ddiv_packed, \ 229 + .dtable = _dtable, \ 230 + .parent = _parent, \ 231 + .flag = CLK_SET_RATE_PARENT) 231 232 232 233 /** 233 234 * struct rzv2h_mod_clk - Module Clocks definitions
+145
include/linux/clk/renesas.h
··· 10 10 #ifndef __LINUX_CLK_RENESAS_H_ 11 11 #define __LINUX_CLK_RENESAS_H_ 12 12 13 + #include <linux/clk-provider.h> 13 14 #include <linux/types.h> 15 + #include <linux/units.h> 14 16 15 17 struct device; 16 18 struct device_node; ··· 34 32 #define cpg_mssr_attach_dev NULL 35 33 #define cpg_mssr_detach_dev NULL 36 34 #endif 35 + 36 + /** 37 + * struct rzv2h_pll_limits - PLL parameter constraints 38 + * 39 + * This structure defines the minimum and maximum allowed values for 40 + * various parameters used to configure a PLL. These limits ensure 41 + * the PLL operates within valid and stable ranges. 42 + * 43 + * @fout: Output frequency range (in MHz) 44 + * @fout.min: Minimum allowed output frequency 45 + * @fout.max: Maximum allowed output frequency 46 + * 47 + * @fvco: PLL oscillation frequency range (in MHz) 48 + * @fvco.min: Minimum allowed VCO frequency 49 + * @fvco.max: Maximum allowed VCO frequency 50 + * 51 + * @m: Main-divider range 52 + * @m.min: Minimum main-divider value 53 + * @m.max: Maximum main-divider value 54 + * 55 + * @p: Pre-divider range 56 + * @p.min: Minimum pre-divider value 57 + * @p.max: Maximum pre-divider value 58 + * 59 + * @s: Divider range 60 + * @s.min: Minimum divider value 61 + * @s.max: Maximum divider value 62 + * 63 + * @k: Delta-sigma modulator range (signed) 64 + * @k.min: Minimum delta-sigma value 65 + * @k.max: Maximum delta-sigma value 66 + */ 67 + struct rzv2h_pll_limits { 68 + struct { 69 + u32 min; 70 + u32 max; 71 + } fout; 72 + 73 + struct { 74 + u32 min; 75 + u32 max; 76 + } fvco; 77 + 78 + struct { 79 + u16 min; 80 + u16 max; 81 + } m; 82 + 83 + struct { 84 + u8 min; 85 + u8 max; 86 + } p; 87 + 88 + struct { 89 + u8 min; 90 + u8 max; 91 + } s; 92 + 93 + struct { 94 + s16 min; 95 + s16 max; 96 + } k; 97 + }; 98 + 99 + /** 100 + * struct rzv2h_pll_pars - PLL configuration parameters 101 + * 102 + * This structure contains the configuration parameters for the 103 + * Phase-Locked Loop (PLL), used to achieve a specific output frequency. 104 + * 105 + * @m: Main divider value 106 + * @p: Pre-divider value 107 + * @s: Output divider value 108 + * @k: Delta-sigma modulation value 109 + * @freq_millihz: Calculated PLL output frequency in millihertz 110 + * @error_millihz: Frequency error from target in millihertz (signed) 111 + */ 112 + struct rzv2h_pll_pars { 113 + u16 m; 114 + u8 p; 115 + u8 s; 116 + s16 k; 117 + u64 freq_millihz; 118 + s64 error_millihz; 119 + }; 120 + 121 + /** 122 + * struct rzv2h_pll_div_pars - PLL parameters with post-divider 123 + * 124 + * This structure is used for PLLs that include an additional post-divider 125 + * stage after the main PLL block. It contains both the PLL configuration 126 + * parameters and the resulting frequency/error values after the divider. 127 + * 128 + * @pll: Main PLL configuration parameters (see struct rzv2h_pll_pars) 129 + * 130 + * @div: Post-divider configuration and result 131 + * @div.divider_value: Divider applied to the PLL output 132 + * @div.freq_millihz: Output frequency after divider in millihertz 133 + * @div.error_millihz: Frequency error from target in millihertz (signed) 134 + */ 135 + struct rzv2h_pll_div_pars { 136 + struct rzv2h_pll_pars pll; 137 + struct { 138 + u8 divider_value; 139 + u64 freq_millihz; 140 + s64 error_millihz; 141 + } div; 142 + }; 143 + 144 + #define RZV2H_CPG_PLL_DSI_LIMITS(name) \ 145 + static const struct rzv2h_pll_limits (name) = { \ 146 + .fout = { .min = 25 * MEGA, .max = 375 * MEGA }, \ 147 + .fvco = { .min = 1600 * MEGA, .max = 3200 * MEGA }, \ 148 + .m = { .min = 64, .max = 533 }, \ 149 + .p = { .min = 1, .max = 4 }, \ 150 + .s = { .min = 0, .max = 6 }, \ 151 + .k = { .min = -32768, .max = 32767 }, \ 152 + } \ 153 + 154 + #ifdef CONFIG_CLK_RZV2H 155 + bool rzv2h_get_pll_pars(const struct rzv2h_pll_limits *limits, 156 + struct rzv2h_pll_pars *pars, u64 freq_millihz); 157 + 158 + bool rzv2h_get_pll_divs_pars(const struct rzv2h_pll_limits *limits, 159 + struct rzv2h_pll_div_pars *pars, 160 + const u8 *table, u8 table_size, u64 freq_millihz); 161 + #else 162 + static inline bool rzv2h_get_pll_pars(const struct rzv2h_pll_limits *limits, 163 + struct rzv2h_pll_pars *pars, 164 + u64 freq_millihz) 165 + { 166 + return false; 167 + } 168 + 169 + static inline bool rzv2h_get_pll_divs_pars(const struct rzv2h_pll_limits *limits, 170 + struct rzv2h_pll_div_pars *pars, 171 + const u8 *table, u8 table_size, 172 + u64 freq_millihz) 173 + { 174 + return false; 175 + } 176 + #endif 177 + 37 178 #endif