FILE
sifive-prci.c
File preview with syntax highlighting.
linux-6.19.14/drivers/clk/sifive/sifive-prci.c
1// SPDX-License-Identifier: GPL-2.0 2/* 3 * Copyright (C) 2020 SiFive, Inc. 4 * Copyright (C) 2020 Zong Li 5 */ 6 7#include <linux/delay.h> 8#include <linux/io.h> 9#include <linux/module.h> 10#include <linux/of.h> 11#include "sifive-prci.h" 12#include "fu540-prci.h" 13#include "fu740-prci.h" 14 15/* 16 * Private functions 17 */ 18 19/** 20 * __prci_readl() - read from a PRCI register 21 * @pd: PRCI context 22 * @offs: register offset to read from (in bytes, from PRCI base address) 23 * 24 * Read the register located at offset @offs from the base virtual 25 * address of the PRCI register target described by @pd, and return 26 * the value to the caller. 27 * 28 * Context: Any context. 29 * 30 * Return: the contents of the register described by @pd and @offs. 31 */ 32static u32 __prci_readl(struct __prci_data *pd, u32 offs) 33{ 34 return readl_relaxed(pd->va + offs); 35} 36 37static void __prci_writel(u32 v, u32 offs, struct __prci_data *pd) 38{ 39 writel_relaxed(v, pd->va + offs); 40} 41 42/* WRPLL-related private functions */ 43 44/** 45 * __prci_wrpll_unpack() - unpack WRPLL configuration registers into parameters 46 * @c: ptr to a struct wrpll_cfg record to write config into 47 * @r: value read from the PRCI PLL configuration register 48 * 49 * Given a value @r read from an FU740 PRCI PLL configuration register, 50 * split it into fields and populate it into the WRPLL configuration record 51 * pointed to by @c. 52 * 53 * The COREPLLCFG0 macros are used below, but the other *PLLCFG0 macros 54 * have the same register layout. 55 * 56 * Context: Any context. 57 */ 58static void __prci_wrpll_unpack(struct wrpll_cfg *c, u32 r) 59{ 60 u32 v; 61 62 v = r & PRCI_COREPLLCFG0_DIVR_MASK; 63 v >>= PRCI_COREPLLCFG0_DIVR_SHIFT; 64 c->divr = v; 65 66 v = r & PRCI_COREPLLCFG0_DIVF_MASK; 67 v >>= PRCI_COREPLLCFG0_DIVF_SHIFT; 68 c->divf = v; 69 70 v = r & PRCI_COREPLLCFG0_DIVQ_MASK; 71 v >>= PRCI_COREPLLCFG0_DIVQ_SHIFT; 72 c->divq = v; 73 74 v = r & PRCI_COREPLLCFG0_RANGE_MASK; 75 v >>= PRCI_COREPLLCFG0_RANGE_SHIFT; 76 c->range = v; 77 78 c->flags &= 79 (WRPLL_FLAGS_INT_FEEDBACK_MASK | WRPLL_FLAGS_EXT_FEEDBACK_MASK); 80 81 /* external feedback mode not supported */ 82 c->flags |= WRPLL_FLAGS_INT_FEEDBACK_MASK; 83} 84 85/** 86 * __prci_wrpll_pack() - pack PLL configuration parameters into a register value 87 * @c: pointer to a struct wrpll_cfg record containing the PLL's cfg 88 * 89 * Using a set of WRPLL configuration values pointed to by @c, 90 * assemble a PRCI PLL configuration register value, and return it to 91 * the caller. 92 * 93 * Context: Any context. Caller must ensure that the contents of the 94 * record pointed to by @c do not change during the execution 95 * of this function. 96 * 97 * Returns: a value suitable for writing into a PRCI PLL configuration 98 * register 99 */ 100static u32 __prci_wrpll_pack(const struct wrpll_cfg *c) 101{ 102 u32 r = 0; 103 104 r |= c->divr << PRCI_COREPLLCFG0_DIVR_SHIFT; 105 r |= c->divf << PRCI_COREPLLCFG0_DIVF_SHIFT; 106 r |= c->divq << PRCI_COREPLLCFG0_DIVQ_SHIFT; 107 r |= c->range << PRCI_COREPLLCFG0_RANGE_SHIFT; 108 109 /* external feedback mode not supported */ 110 r |= PRCI_COREPLLCFG0_FSE_MASK; 111 112 return r; 113} 114 115/** 116 * __prci_wrpll_read_cfg0() - read the WRPLL configuration from the PRCI 117 * @pd: PRCI context 118 * @pwd: PRCI WRPLL metadata 119 * 120 * Read the current configuration of the PLL identified by @pwd from 121 * the PRCI identified by @pd, and store it into the local configuration 122 * cache in @pwd. 123 * 124 * Context: Any context. Caller must prevent the records pointed to by 125 * @pd and @pwd from changing during execution. 126 */ 127static void __prci_wrpll_read_cfg0(struct __prci_data *pd, 128 struct __prci_wrpll_data *pwd) 129{ 130 __prci_wrpll_unpack(&pwd->c, __prci_readl(pd, pwd->cfg0_offs)); 131} 132 133/** 134 * __prci_wrpll_write_cfg0() - write WRPLL configuration into the PRCI 135 * @pd: PRCI context 136 * @pwd: PRCI WRPLL metadata 137 * @c: WRPLL configuration record to write 138 * 139 * Write the WRPLL configuration described by @c into the WRPLL 140 * configuration register identified by @pwd in the PRCI instance 141 * described by @c. Make a cached copy of the WRPLL's current 142 * configuration so it can be used by other code. 143 * 144 * Context: Any context. Caller must prevent the records pointed to by 145 * @pd and @pwd from changing during execution. 146 */ 147static void __prci_wrpll_write_cfg0(struct __prci_data *pd, 148 struct __prci_wrpll_data *pwd, 149 struct wrpll_cfg *c) 150{ 151 __prci_writel(__prci_wrpll_pack(c), pwd->cfg0_offs, pd); 152 153 memcpy(&pwd->c, c, sizeof(*c)); 154} 155 156/** 157 * __prci_wrpll_write_cfg1() - write Clock enable/disable configuration 158 * into the PRCI 159 * @pd: PRCI context 160 * @pwd: PRCI WRPLL metadata 161 * @enable: Clock enable or disable value 162 */ 163static void __prci_wrpll_write_cfg1(struct __prci_data *pd, 164 struct __prci_wrpll_data *pwd, 165 u32 enable) 166{ 167 __prci_writel(enable, pwd->cfg1_offs, pd); 168} 169 170/* 171 * Linux clock framework integration 172 * 173 * See the Linux clock framework documentation for more information on 174 * these functions. 175 */ 176 177unsigned long sifive_prci_wrpll_recalc_rate(struct clk_hw *hw, 178 unsigned long parent_rate) 179{ 180 struct __prci_clock *pc = clk_hw_to_prci_clock(hw); 181 struct __prci_wrpll_data *pwd = pc->pwd; 182 183 return wrpll_calc_output_rate(&pwd->c, parent_rate); 184} 185 186int sifive_prci_wrpll_determine_rate(struct clk_hw *hw, 187 struct clk_rate_request *req) 188{ 189 struct __prci_clock *pc = clk_hw_to_prci_clock(hw); 190 struct __prci_wrpll_data *pwd = pc->pwd; 191 struct wrpll_cfg c; 192 193 memcpy(&c, &pwd->c, sizeof(c)); 194 195 wrpll_configure_for_rate(&c, req->rate, req->best_parent_rate); 196 197 req->rate = wrpll_calc_output_rate(&c, req->best_parent_rate); 198 199 return 0; 200} 201 202int sifive_prci_wrpll_set_rate(struct clk_hw *hw, 203 unsigned long rate, unsigned long parent_rate) 204{ 205 struct __prci_clock *pc = clk_hw_to_prci_clock(hw); 206 struct __prci_wrpll_data *pwd = pc->pwd; 207 struct __prci_data *pd = pc->pd; 208 int r; 209 210 r = wrpll_configure_for_rate(&pwd->c, rate, parent_rate); 211 if (r) 212 return r; 213 214 if (pwd->enable_bypass) 215 pwd->enable_bypass(pd); 216 217 __prci_wrpll_write_cfg0(pd, pwd, &pwd->c); 218 219 udelay(wrpll_calc_max_lock_us(&pwd->c)); 220 221 return 0; 222} 223 224int sifive_clk_is_enabled(struct clk_hw *hw) 225{ 226 struct __prci_clock *pc = clk_hw_to_prci_clock(hw); 227 struct __prci_wrpll_data *pwd = pc->pwd; 228 struct __prci_data *pd = pc->pd; 229 u32 r; 230 231 r = __prci_readl(pd, pwd->cfg1_offs); 232 233 if (r & PRCI_COREPLLCFG1_CKE_MASK) 234 return 1; 235 else 236 return 0; 237} 238 239int sifive_prci_clock_enable(struct clk_hw *hw) 240{ 241 struct __prci_clock *pc = clk_hw_to_prci_clock(hw); 242 struct __prci_wrpll_data *pwd = pc->pwd; 243 struct __prci_data *pd = pc->pd; 244 245 if (sifive_clk_is_enabled(hw)) 246 return 0; 247 248 __prci_wrpll_write_cfg1(pd, pwd, PRCI_COREPLLCFG1_CKE_MASK); 249 250 if (pwd->disable_bypass) 251 pwd->disable_bypass(pd); 252 253 return 0; 254} 255 256void sifive_prci_clock_disable(struct clk_hw *hw) 257{ 258 struct __prci_clock *pc = clk_hw_to_prci_clock(hw); 259 struct __prci_wrpll_data *pwd = pc->pwd; 260 struct __prci_data *pd = pc->pd; 261 u32 r; 262 263 if (pwd->enable_bypass) 264 pwd->enable_bypass(pd); 265 266 r = __prci_readl(pd, pwd->cfg1_offs); 267 r &= ~PRCI_COREPLLCFG1_CKE_MASK; 268 269 __prci_wrpll_write_cfg1(pd, pwd, r); 270} 271 272/* TLCLKSEL clock integration */ 273 274unsigned long sifive_prci_tlclksel_recalc_rate(struct clk_hw *hw, 275 unsigned long parent_rate) 276{ 277 struct __prci_clock *pc = clk_hw_to_prci_clock(hw); 278 struct __prci_data *pd = pc->pd; 279 u32 v; 280 u8 div; 281 282 v = __prci_readl(pd, PRCI_CLKMUXSTATUSREG_OFFSET); 283 v &= PRCI_CLKMUXSTATUSREG_TLCLKSEL_STATUS_MASK; 284 div = v ? 1 : 2; 285 286 return div_u64(parent_rate, div); 287} 288 289/* HFPCLK clock integration */ 290 291unsigned long sifive_prci_hfpclkplldiv_recalc_rate(struct clk_hw *hw, 292 unsigned long parent_rate) 293{ 294 struct __prci_clock *pc = clk_hw_to_prci_clock(hw); 295 struct __prci_data *pd = pc->pd; 296 u32 div = __prci_readl(pd, PRCI_HFPCLKPLLDIV_OFFSET); 297 298 return div_u64(parent_rate, div + 2); 299} 300 301/* 302 * Core clock mux control 303 */ 304 305/** 306 * sifive_prci_coreclksel_use_hfclk() - switch the CORECLK mux to output HFCLK 307 * @pd: struct __prci_data * for the PRCI containing the CORECLK mux reg 308 * 309 * Switch the CORECLK mux to the HFCLK input source; return once complete. 310 * 311 * Context: Any context. Caller must prevent concurrent changes to the 312 * PRCI_CORECLKSEL_OFFSET register. 313 */ 314void sifive_prci_coreclksel_use_hfclk(struct __prci_data *pd) 315{ 316 u32 r; 317 318 r = __prci_readl(pd, PRCI_CORECLKSEL_OFFSET); 319 r |= PRCI_CORECLKSEL_CORECLKSEL_MASK; 320 __prci_writel(r, PRCI_CORECLKSEL_OFFSET, pd); 321 322 r = __prci_readl(pd, PRCI_CORECLKSEL_OFFSET); /* barrier */ 323} 324 325/** 326 * sifive_prci_coreclksel_use_corepll() - switch the CORECLK mux to output 327 * COREPLL 328 * @pd: struct __prci_data * for the PRCI containing the CORECLK mux reg 329 * 330 * Switch the CORECLK mux to the COREPLL output clock; return once complete. 331 * 332 * Context: Any context. Caller must prevent concurrent changes to the 333 * PRCI_CORECLKSEL_OFFSET register. 334 */ 335void sifive_prci_coreclksel_use_corepll(struct __prci_data *pd) 336{ 337 u32 r; 338 339 r = __prci_readl(pd, PRCI_CORECLKSEL_OFFSET); 340 r &= ~PRCI_CORECLKSEL_CORECLKSEL_MASK; 341 __prci_writel(r, PRCI_CORECLKSEL_OFFSET, pd); 342 343 r = __prci_readl(pd, PRCI_CORECLKSEL_OFFSET); /* barrier */ 344} 345 346/** 347 * sifive_prci_coreclksel_use_final_corepll() - switch the CORECLK mux to output 348 * FINAL_COREPLL 349 * @pd: struct __prci_data * for the PRCI containing the CORECLK mux reg 350 * 351 * Switch the CORECLK mux to the final COREPLL output clock; return once 352 * complete. 353 * 354 * Context: Any context. Caller must prevent concurrent changes to the 355 * PRCI_CORECLKSEL_OFFSET register. 356 */ 357void sifive_prci_coreclksel_use_final_corepll(struct __prci_data *pd) 358{ 359 u32 r; 360 361 r = __prci_readl(pd, PRCI_CORECLKSEL_OFFSET); 362 r &= ~PRCI_CORECLKSEL_CORECLKSEL_MASK; 363 __prci_writel(r, PRCI_CORECLKSEL_OFFSET, pd); 364 365 r = __prci_readl(pd, PRCI_CORECLKSEL_OFFSET); /* barrier */ 366} 367 368/** 369 * sifive_prci_corepllsel_use_dvfscorepll() - switch the COREPLL mux to 370 * output DVFS_COREPLL 371 * @pd: struct __prci_data * for the PRCI containing the COREPLL mux reg 372 * 373 * Switch the COREPLL mux to the DVFSCOREPLL output clock; return once complete. 374 * 375 * Context: Any context. Caller must prevent concurrent changes to the 376 * PRCI_COREPLLSEL_OFFSET register. 377 */ 378void sifive_prci_corepllsel_use_dvfscorepll(struct __prci_data *pd) 379{ 380 u32 r; 381 382 r = __prci_readl(pd, PRCI_COREPLLSEL_OFFSET); 383 r |= PRCI_COREPLLSEL_COREPLLSEL_MASK; 384 __prci_writel(r, PRCI_COREPLLSEL_OFFSET, pd); 385 386 r = __prci_readl(pd, PRCI_COREPLLSEL_OFFSET); /* barrier */ 387} 388 389/** 390 * sifive_prci_corepllsel_use_corepll() - switch the COREPLL mux to 391 * output COREPLL 392 * @pd: struct __prci_data * for the PRCI containing the COREPLL mux reg 393 * 394 * Switch the COREPLL mux to the COREPLL output clock; return once complete. 395 * 396 * Context: Any context. Caller must prevent concurrent changes to the 397 * PRCI_COREPLLSEL_OFFSET register. 398 */ 399void sifive_prci_corepllsel_use_corepll(struct __prci_data *pd) 400{ 401 u32 r; 402 403 r = __prci_readl(pd, PRCI_COREPLLSEL_OFFSET); 404 r &= ~PRCI_COREPLLSEL_COREPLLSEL_MASK; 405 __prci_writel(r, PRCI_COREPLLSEL_OFFSET, pd); 406 407 r = __prci_readl(pd, PRCI_COREPLLSEL_OFFSET); /* barrier */ 408} 409 410/** 411 * sifive_prci_hfpclkpllsel_use_hfclk() - switch the HFPCLKPLL mux to 412 * output HFCLK 413 * @pd: struct __prci_data * for the PRCI containing the HFPCLKPLL mux reg 414 * 415 * Switch the HFPCLKPLL mux to the HFCLK input source; return once complete. 416 * 417 * Context: Any context. Caller must prevent concurrent changes to the 418 * PRCI_HFPCLKPLLSEL_OFFSET register. 419 */ 420void sifive_prci_hfpclkpllsel_use_hfclk(struct __prci_data *pd) 421{ 422 u32 r; 423 424 r = __prci_readl(pd, PRCI_HFPCLKPLLSEL_OFFSET); 425 r |= PRCI_HFPCLKPLLSEL_HFPCLKPLLSEL_MASK; 426 __prci_writel(r, PRCI_HFPCLKPLLSEL_OFFSET, pd); 427 428 r = __prci_readl(pd, PRCI_HFPCLKPLLSEL_OFFSET); /* barrier */ 429} 430 431/** 432 * sifive_prci_hfpclkpllsel_use_hfpclkpll() - switch the HFPCLKPLL mux to 433 * output HFPCLKPLL 434 * @pd: struct __prci_data * for the PRCI containing the HFPCLKPLL mux reg 435 * 436 * Switch the HFPCLKPLL mux to the HFPCLKPLL output clock; return once complete. 437 * 438 * Context: Any context. Caller must prevent concurrent changes to the 439 * PRCI_HFPCLKPLLSEL_OFFSET register. 440 */ 441void sifive_prci_hfpclkpllsel_use_hfpclkpll(struct __prci_data *pd) 442{ 443 u32 r; 444 445 r = __prci_readl(pd, PRCI_HFPCLKPLLSEL_OFFSET); 446 r &= ~PRCI_HFPCLKPLLSEL_HFPCLKPLLSEL_MASK; 447 __prci_writel(r, PRCI_HFPCLKPLLSEL_OFFSET, pd); 448 449 r = __prci_readl(pd, PRCI_HFPCLKPLLSEL_OFFSET); /* barrier */ 450} 451 452/* PCIE AUX clock APIs for enable, disable. */ 453int sifive_prci_pcie_aux_clock_is_enabled(struct clk_hw *hw) 454{ 455 struct __prci_clock *pc = clk_hw_to_prci_clock(hw); 456 struct __prci_data *pd = pc->pd; 457 u32 r; 458 459 r = __prci_readl(pd, PRCI_PCIE_AUX_OFFSET); 460 461 if (r & PRCI_PCIE_AUX_EN_MASK) 462 return 1; 463 else 464 return 0; 465} 466 467int sifive_prci_pcie_aux_clock_enable(struct clk_hw *hw) 468{ 469 struct __prci_clock *pc = clk_hw_to_prci_clock(hw); 470 struct __prci_data *pd = pc->pd; 471 u32 r __maybe_unused; 472 473 if (sifive_prci_pcie_aux_clock_is_enabled(hw)) 474 return 0; 475 476 __prci_writel(1, PRCI_PCIE_AUX_OFFSET, pd); 477 r = __prci_readl(pd, PRCI_PCIE_AUX_OFFSET); /* barrier */ 478 479 return 0; 480} 481 482void sifive_prci_pcie_aux_clock_disable(struct clk_hw *hw) 483{ 484 struct __prci_clock *pc = clk_hw_to_prci_clock(hw); 485 struct __prci_data *pd = pc->pd; 486 u32 r __maybe_unused; 487 488 __prci_writel(0, PRCI_PCIE_AUX_OFFSET, pd); 489 r = __prci_readl(pd, PRCI_PCIE_AUX_OFFSET); /* barrier */ 490 491} 492 493/** 494 * __prci_register_clocks() - register clock controls in the PRCI 495 * @dev: Linux struct device 496 * @pd: The pointer for PRCI per-device instance data 497 * @desc: The pointer for the information of clocks of each SoCs 498 * 499 * Register the list of clock controls described in __prci_init_clocks[] with 500 * the Linux clock framework. 501 * 502 * Return: 0 upon success or a negative error code upon failure. 503 */ 504static int __prci_register_clocks(struct device *dev, struct __prci_data *pd, 505 const struct prci_clk_desc *desc) 506{ 507 struct clk_init_data init = { }; 508 struct __prci_clock *pic; 509 int parent_count, i, r; 510 511 parent_count = of_clk_get_parent_count(dev->of_node); 512 if (parent_count != EXPECTED_CLK_PARENT_COUNT) { 513 dev_err(dev, "expected only two parent clocks, found %d\n", 514 parent_count); 515 return -EINVAL; 516 } 517 518 /* Register PLLs */ 519 for (i = 0; i < desc->num_clks; ++i) { 520 pic = &(desc->clks[i]); 521 522 init.name = pic->name; 523 init.parent_names = &pic->parent_name; 524 init.num_parents = 1; 525 init.ops = pic->ops; 526 pic->hw.init = &init; 527 528 pic->pd = pd; 529 530 if (pic->pwd) 531 __prci_wrpll_read_cfg0(pd, pic->pwd); 532 533 r = devm_clk_hw_register(dev, &pic->hw); 534 if (r) { 535 dev_warn(dev, "Failed to register clock %s: %d\n", 536 init.name, r); 537 return r; 538 } 539 540 pd->hw_clks.hws[i] = &pic->hw; 541 } 542 543 pd->hw_clks.num = i; 544 545 r = devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, 546 &pd->hw_clks); 547 if (r) { 548 dev_err(dev, "could not add hw_provider: %d\n", r); 549 return r; 550 } 551 552 return 0; 553} 554 555/** 556 * sifive_prci_probe() - initialize prci data and check parent count 557 * @pdev: platform device pointer for the prci 558 * 559 * Return: 0 upon success or a negative error code upon failure. 560 */ 561static int sifive_prci_probe(struct platform_device *pdev) 562{ 563 struct device *dev = &pdev->dev; 564 struct __prci_data *pd; 565 const struct prci_clk_desc *desc; 566 int r; 567 568 desc = of_device_get_match_data(&pdev->dev); 569 570 pd = devm_kzalloc(dev, struct_size(pd, hw_clks.hws, desc->num_clks), GFP_KERNEL); 571 if (!pd) 572 return -ENOMEM; 573 574 pd->va = devm_platform_ioremap_resource(pdev, 0); 575 if (IS_ERR(pd->va)) 576 return PTR_ERR(pd->va); 577 578 pd->reset.rcdev.owner = THIS_MODULE; 579 pd->reset.rcdev.nr_resets = PRCI_RST_NR; 580 pd->reset.rcdev.ops = &reset_simple_ops; 581 pd->reset.rcdev.of_node = pdev->dev.of_node; 582 pd->reset.active_low = true; 583 pd->reset.membase = pd->va + PRCI_DEVICESRESETREG_OFFSET; 584 spin_lock_init(&pd->reset.lock); 585 586 r = devm_reset_controller_register(&pdev->dev, &pd->reset.rcdev); 587 if (r) { 588 dev_err(dev, "could not register reset controller: %d\n", r); 589 return r; 590 } 591 r = __prci_register_clocks(dev, pd, desc); 592 if (r) { 593 dev_err(dev, "could not register clocks: %d\n", r); 594 return r; 595 } 596 597 dev_dbg(dev, "SiFive PRCI probed\n"); 598 599 return 0; 600} 601 602static const struct of_device_id sifive_prci_of_match[] = { 603 {.compatible = "sifive,fu540-c000-prci", .data = &prci_clk_fu540}, 604 {.compatible = "sifive,fu740-c000-prci", .data = &prci_clk_fu740}, 605 {} 606}; 607MODULE_DEVICE_TABLE(of, sifive_prci_of_match); 608 609static struct platform_driver sifive_prci_driver = { 610 .driver = { 611 .name = "sifive-clk-prci", 612 .of_match_table = sifive_prci_of_match, 613 }, 614 .probe = sifive_prci_probe, 615}; 616module_platform_driver(sifive_prci_driver); 617 618MODULE_AUTHOR("Paul Walmsley <paul.walmsley@sifive.com>"); 619MODULE_DESCRIPTION("SiFive Power Reset Clock Interface (PRCI) driver"); 620MODULE_LICENSE("GPL");
~/
Read-only file preview.