1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Copyright (c) 2019 Western Digital Corporation or its affiliates. 4 * 5 * Copyright (C) 2018 SiFive, Inc. 6 * Wesley Terpstra 7 * Paul Walmsley 8 * 9 * This program is free software; you can redistribute it and/or modify 10 * it under the terms of the GNU General Public License version 2 as 11 * published by the Free Software Foundation. 12 * 13 * This program is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 * GNU General Public License for more details. 17 * 18 * The FU540 PRCI implements clock and reset control for the SiFive 19 * FU540-C000 chip. This driver assumes that it has sole control 20 * over all PRCI resources. 21 * 22 * This driver is based on the PRCI driver written by Wesley Terpstra. 23 * 24 * Refer, commit 999529edf517ed75b56659d456d221b2ee56bb60 of: 25 * https://github.com/riscv/riscv-linux 26 * 27 * References: 28 * - SiFive FU540-C000 manual v1p0, Chapter 7 "Clocking and Reset" 29 */ 30 31 #include <asm/io.h> 32 #include <clk-uclass.h> 33 #include <clk.h> 34 #include <common.h> 35 #include <div64.h> 36 #include <dm.h> 37 #include <errno.h> 38 39 #include <linux/math64.h> 40 #include <dt-bindings/clk/sifive-fu540-prci.h> 41 42 #include "analogbits-wrpll-cln28hpc.h" 43 44 /* 45 * EXPECTED_CLK_PARENT_COUNT: how many parent clocks this driver expects: 46 * hfclk and rtcclk 47 */ 48 #define EXPECTED_CLK_PARENT_COUNT 2 49 50 /* 51 * Register offsets and bitmasks 52 */ 53 54 /* COREPLLCFG0 */ 55 #define PRCI_COREPLLCFG0_OFFSET 0x4 56 #define PRCI_COREPLLCFG0_DIVR_SHIFT 0 57 #define PRCI_COREPLLCFG0_DIVR_MASK (0x3f << PRCI_COREPLLCFG0_DIVR_SHIFT) 58 #define PRCI_COREPLLCFG0_DIVF_SHIFT 6 59 #define PRCI_COREPLLCFG0_DIVF_MASK (0x1ff << PRCI_COREPLLCFG0_DIVF_SHIFT) 60 #define PRCI_COREPLLCFG0_DIVQ_SHIFT 15 61 #define PRCI_COREPLLCFG0_DIVQ_MASK (0x7 << PRCI_COREPLLCFG0_DIVQ_SHIFT) 62 #define PRCI_COREPLLCFG0_RANGE_SHIFT 18 63 #define PRCI_COREPLLCFG0_RANGE_MASK (0x7 << PRCI_COREPLLCFG0_RANGE_SHIFT) 64 #define PRCI_COREPLLCFG0_BYPASS_SHIFT 24 65 #define PRCI_COREPLLCFG0_BYPASS_MASK (0x1 << PRCI_COREPLLCFG0_BYPASS_SHIFT) 66 #define PRCI_COREPLLCFG0_FSE_SHIFT 25 67 #define PRCI_COREPLLCFG0_FSE_MASK (0x1 << PRCI_COREPLLCFG0_FSE_SHIFT) 68 #define PRCI_COREPLLCFG0_LOCK_SHIFT 31 69 #define PRCI_COREPLLCFG0_LOCK_MASK (0x1 << PRCI_COREPLLCFG0_LOCK_SHIFT) 70 71 /* DDRPLLCFG0 */ 72 #define PRCI_DDRPLLCFG0_OFFSET 0xc 73 #define PRCI_DDRPLLCFG0_DIVR_SHIFT 0 74 #define PRCI_DDRPLLCFG0_DIVR_MASK (0x3f << PRCI_DDRPLLCFG0_DIVR_SHIFT) 75 #define PRCI_DDRPLLCFG0_DIVF_SHIFT 6 76 #define PRCI_DDRPLLCFG0_DIVF_MASK (0x1ff << PRCI_DDRPLLCFG0_DIVF_SHIFT) 77 #define PRCI_DDRPLLCFG0_DIVQ_SHIFT 15 78 #define PRCI_DDRPLLCFG0_DIVQ_MASK (0x7 << PRCI_DDRPLLCFG0_DIVQ_SHIFT) 79 #define PRCI_DDRPLLCFG0_RANGE_SHIFT 18 80 #define PRCI_DDRPLLCFG0_RANGE_MASK (0x7 << PRCI_DDRPLLCFG0_RANGE_SHIFT) 81 #define PRCI_DDRPLLCFG0_BYPASS_SHIFT 24 82 #define PRCI_DDRPLLCFG0_BYPASS_MASK (0x1 << PRCI_DDRPLLCFG0_BYPASS_SHIFT) 83 #define PRCI_DDRPLLCFG0_FSE_SHIFT 25 84 #define PRCI_DDRPLLCFG0_FSE_MASK (0x1 << PRCI_DDRPLLCFG0_FSE_SHIFT) 85 #define PRCI_DDRPLLCFG0_LOCK_SHIFT 31 86 #define PRCI_DDRPLLCFG0_LOCK_MASK (0x1 << PRCI_DDRPLLCFG0_LOCK_SHIFT) 87 88 /* DDRPLLCFG1 */ 89 #define PRCI_DDRPLLCFG1_OFFSET 0x10 90 #define PRCI_DDRPLLCFG1_CKE_SHIFT 24 91 #define PRCI_DDRPLLCFG1_CKE_MASK (0x1 << PRCI_DDRPLLCFG1_CKE_SHIFT) 92 93 /* GEMGXLPLLCFG0 */ 94 #define PRCI_GEMGXLPLLCFG0_OFFSET 0x1c 95 #define PRCI_GEMGXLPLLCFG0_DIVR_SHIFT 0 96 #define PRCI_GEMGXLPLLCFG0_DIVR_MASK \ 97 (0x3f << PRCI_GEMGXLPLLCFG0_DIVR_SHIFT) 98 #define PRCI_GEMGXLPLLCFG0_DIVF_SHIFT 6 99 #define PRCI_GEMGXLPLLCFG0_DIVF_MASK \ 100 (0x1ff << PRCI_GEMGXLPLLCFG0_DIVF_SHIFT) 101 #define PRCI_GEMGXLPLLCFG0_DIVQ_SHIFT 15 102 #define PRCI_GEMGXLPLLCFG0_DIVQ_MASK (0x7 << PRCI_GEMGXLPLLCFG0_DIVQ_SHIFT) 103 #define PRCI_GEMGXLPLLCFG0_RANGE_SHIFT 18 104 #define PRCI_GEMGXLPLLCFG0_RANGE_MASK \ 105 (0x7 << PRCI_GEMGXLPLLCFG0_RANGE_SHIFT) 106 #define PRCI_GEMGXLPLLCFG0_BYPASS_SHIFT 24 107 #define PRCI_GEMGXLPLLCFG0_BYPASS_MASK \ 108 (0x1 << PRCI_GEMGXLPLLCFG0_BYPASS_SHIFT) 109 #define PRCI_GEMGXLPLLCFG0_FSE_SHIFT 25 110 #define PRCI_GEMGXLPLLCFG0_FSE_MASK \ 111 (0x1 << PRCI_GEMGXLPLLCFG0_FSE_SHIFT) 112 #define PRCI_GEMGXLPLLCFG0_LOCK_SHIFT 31 113 #define PRCI_GEMGXLPLLCFG0_LOCK_MASK (0x1 << PRCI_GEMGXLPLLCFG0_LOCK_SHIFT) 114 115 /* GEMGXLPLLCFG1 */ 116 #define PRCI_GEMGXLPLLCFG1_OFFSET 0x20 117 #define PRCI_GEMGXLPLLCFG1_CKE_SHIFT 24 118 #define PRCI_GEMGXLPLLCFG1_CKE_MASK (0x1 << PRCI_GEMGXLPLLCFG1_CKE_SHIFT) 119 120 /* CORECLKSEL */ 121 #define PRCI_CORECLKSEL_OFFSET 0x24 122 #define PRCI_CORECLKSEL_CORECLKSEL_SHIFT 0 123 #define PRCI_CORECLKSEL_CORECLKSEL_MASK \ 124 (0x1 << PRCI_CORECLKSEL_CORECLKSEL_SHIFT) 125 126 /* DEVICESRESETREG */ 127 #define PRCI_DEVICESRESETREG_OFFSET 0x28 128 #define PRCI_DEVICESRESETREG_DDR_CTRL_RST_N_SHIFT 0 129 #define PRCI_DEVICESRESETREG_DDR_CTRL_RST_N_MASK \ 130 (0x1 << PRCI_DEVICESRESETREG_DDR_CTRL_RST_N_SHIFT) 131 #define PRCI_DEVICESRESETREG_DDR_AXI_RST_N_SHIFT 1 132 #define PRCI_DEVICESRESETREG_DDR_AXI_RST_N_MASK \ 133 (0x1 << PRCI_DEVICESRESETREG_DDR_AXI_RST_N_SHIFT) 134 #define PRCI_DEVICESRESETREG_DDR_AHB_RST_N_SHIFT 2 135 #define PRCI_DEVICESRESETREG_DDR_AHB_RST_N_MASK \ 136 (0x1 << PRCI_DEVICESRESETREG_DDR_AHB_RST_N_SHIFT) 137 #define PRCI_DEVICESRESETREG_DDR_PHY_RST_N_SHIFT 3 138 #define PRCI_DEVICESRESETREG_DDR_PHY_RST_N_MASK \ 139 (0x1 << PRCI_DEVICESRESETREG_DDR_PHY_RST_N_SHIFT) 140 #define PRCI_DEVICESRESETREG_GEMGXL_RST_N_SHIFT 5 141 #define PRCI_DEVICESRESETREG_GEMGXL_RST_N_MASK \ 142 (0x1 << PRCI_DEVICESRESETREG_GEMGXL_RST_N_SHIFT) 143 144 /* CLKMUXSTATUSREG */ 145 #define PRCI_CLKMUXSTATUSREG_OFFSET 0x2c 146 #define PRCI_CLKMUXSTATUSREG_TLCLKSEL_STATUS_SHIFT 1 147 #define PRCI_CLKMUXSTATUSREG_TLCLKSEL_STATUS_MASK \ 148 (0x1 << PRCI_CLKMUXSTATUSREG_TLCLKSEL_STATUS_SHIFT) 149 150 /* 151 * Private structures 152 */ 153 154 /** 155 * struct __prci_data - per-device-instance data 156 * @va: base virtual address of the PRCI IP block 157 * @parent: parent clk instance 158 * 159 * PRCI per-device instance data 160 */ 161 struct __prci_data { 162 void *base; 163 struct clk parent; 164 }; 165 166 /** 167 * struct __prci_wrpll_data - WRPLL configuration and integration data 168 * @c: WRPLL current configuration record 169 * @bypass: fn ptr to code to bypass the WRPLL (if applicable; else NULL) 170 * @no_bypass: fn ptr to code to not bypass the WRPLL (if applicable; else NULL) 171 * @cfg0_offs: WRPLL CFG0 register offset (in bytes) from the PRCI base address 172 * 173 * @bypass and @no_bypass are used for WRPLL instances that contain a separate 174 * external glitchless clock mux downstream from the PLL. The WRPLL internal 175 * bypass mux is not glitchless. 176 */ 177 struct __prci_wrpll_data { 178 struct analogbits_wrpll_cfg c; 179 void (*bypass)(struct __prci_data *pd); 180 void (*no_bypass)(struct __prci_data *pd); 181 u8 cfg0_offs; 182 }; 183 184 struct __prci_clock; 185 186 struct __prci_clock_ops { 187 int (*set_rate)(struct __prci_clock *pc, 188 unsigned long rate, 189 unsigned long parent_rate); 190 unsigned long (*round_rate)(struct __prci_clock *pc, 191 unsigned long rate, 192 unsigned long *parent_rate); 193 unsigned long (*recalc_rate)(struct __prci_clock *pc, 194 unsigned long parent_rate); 195 }; 196 197 /** 198 * struct __prci_clock - describes a clock device managed by PRCI 199 * @name: user-readable clock name string - should match the manual 200 * @parent_name: parent name for this clock 201 * @ops: struct clk_ops for the Linux clock framework to use for control 202 * @hw: Linux-private clock data 203 * @pwd: WRPLL-specific data, associated with this clock (if not NULL) 204 * @pd: PRCI-specific data associated with this clock (if not NULL) 205 * 206 * PRCI clock data. Used by the PRCI driver to register PRCI-provided 207 * clocks to the Linux clock infrastructure. 208 */ 209 struct __prci_clock { 210 const char *name; 211 const char *parent_name; 212 const struct __prci_clock_ops *ops; 213 struct __prci_wrpll_data *pwd; 214 struct __prci_data *pd; 215 }; 216 217 /* 218 * Private functions 219 */ 220 221 /** 222 * __prci_readl() - read from a PRCI register 223 * @pd: PRCI context 224 * @offs: register offset to read from (in bytes, from PRCI base address) 225 * 226 * Read the register located at offset @offs from the base virtual 227 * address of the PRCI register target described by @pd, and return 228 * the value to the caller. 229 * 230 * Context: Any context. 231 * 232 * Return: the contents of the register described by @pd and @offs. 233 */ 234 static u32 __prci_readl(struct __prci_data *pd, u32 offs) 235 { 236 return readl(pd->base + offs); 237 } 238 239 static void __prci_writel(u32 v, u32 offs, struct __prci_data *pd) 240 { 241 return writel(v, pd->base + offs); 242 } 243 244 /* WRPLL-related private functions */ 245 246 /** 247 * __prci_wrpll_unpack() - unpack WRPLL configuration registers into parameters 248 * @c: ptr to a struct analogbits_wrpll_cfg record to write config into 249 * @r: value read from the PRCI PLL configuration register 250 * 251 * Given a value @r read from an FU540 PRCI PLL configuration register, 252 * split it into fields and populate it into the WRPLL configuration record 253 * pointed to by @c. 254 * 255 * The COREPLLCFG0 macros are used below, but the other *PLLCFG0 macros 256 * have the same register layout. 257 * 258 * Context: Any context. 259 */ 260 static void __prci_wrpll_unpack(struct analogbits_wrpll_cfg *c, u32 r) 261 { 262 u32 v; 263 264 v = r & PRCI_COREPLLCFG0_DIVR_MASK; 265 v >>= PRCI_COREPLLCFG0_DIVR_SHIFT; 266 c->divr = v; 267 268 v = r & PRCI_COREPLLCFG0_DIVF_MASK; 269 v >>= PRCI_COREPLLCFG0_DIVF_SHIFT; 270 c->divf = v; 271 272 v = r & PRCI_COREPLLCFG0_DIVQ_MASK; 273 v >>= PRCI_COREPLLCFG0_DIVQ_SHIFT; 274 c->divq = v; 275 276 v = r & PRCI_COREPLLCFG0_RANGE_MASK; 277 v >>= PRCI_COREPLLCFG0_RANGE_SHIFT; 278 c->range = v; 279 280 c->flags &= (WRPLL_FLAGS_INT_FEEDBACK_MASK | 281 WRPLL_FLAGS_EXT_FEEDBACK_MASK); 282 283 if (r & PRCI_COREPLLCFG0_FSE_MASK) 284 c->flags |= WRPLL_FLAGS_INT_FEEDBACK_MASK; 285 else 286 c->flags |= WRPLL_FLAGS_EXT_FEEDBACK_MASK; 287 } 288 289 /** 290 * __prci_wrpll_pack() - pack PLL configuration parameters into a register value 291 * @c: pointer to a struct analogbits_wrpll_cfg record containing the PLL's cfg 292 * 293 * Using a set of WRPLL configuration values pointed to by @c, 294 * assemble a PRCI PLL configuration register value, and return it to 295 * the caller. 296 * 297 * Context: Any context. Caller must ensure that the contents of the 298 * record pointed to by @c do not change during the execution 299 * of this function. 300 * 301 * Returns: a value suitable for writing into a PRCI PLL configuration 302 * register 303 */ 304 static u32 __prci_wrpll_pack(struct analogbits_wrpll_cfg *c) 305 { 306 u32 r = 0; 307 308 r |= c->divr << PRCI_COREPLLCFG0_DIVR_SHIFT; 309 r |= c->divf << PRCI_COREPLLCFG0_DIVF_SHIFT; 310 r |= c->divq << PRCI_COREPLLCFG0_DIVQ_SHIFT; 311 r |= c->range << PRCI_COREPLLCFG0_RANGE_SHIFT; 312 if (c->flags & WRPLL_FLAGS_INT_FEEDBACK_MASK) 313 r |= PRCI_COREPLLCFG0_FSE_MASK; 314 315 return r; 316 } 317 318 /** 319 * __prci_wrpll_read_cfg() - read the WRPLL configuration from the PRCI 320 * @pd: PRCI context 321 * @pwd: PRCI WRPLL metadata 322 * 323 * Read the current configuration of the PLL identified by @pwd from 324 * the PRCI identified by @pd, and store it into the local configuration 325 * cache in @pwd. 326 * 327 * Context: Any context. Caller must prevent the records pointed to by 328 * @pd and @pwd from changing during execution. 329 */ 330 static void __prci_wrpll_read_cfg(struct __prci_data *pd, 331 struct __prci_wrpll_data *pwd) 332 { 333 __prci_wrpll_unpack(&pwd->c, __prci_readl(pd, pwd->cfg0_offs)); 334 } 335 336 /** 337 * __prci_wrpll_write_cfg() - write WRPLL configuration into the PRCI 338 * @pd: PRCI context 339 * @pwd: PRCI WRPLL metadata 340 * @c: WRPLL configuration record to write 341 * 342 * Write the WRPLL configuration described by @c into the WRPLL 343 * configuration register identified by @pwd in the PRCI instance 344 * described by @c. Make a cached copy of the WRPLL's current 345 * configuration so it can be used by other code. 346 * 347 * Context: Any context. Caller must prevent the records pointed to by 348 * @pd and @pwd from changing during execution. 349 */ 350 static void __prci_wrpll_write_cfg(struct __prci_data *pd, 351 struct __prci_wrpll_data *pwd, 352 struct analogbits_wrpll_cfg *c) 353 { 354 __prci_writel(__prci_wrpll_pack(c), pwd->cfg0_offs, pd); 355 356 memcpy(&pwd->c, c, sizeof(struct analogbits_wrpll_cfg)); 357 } 358 359 /* Core clock mux control */ 360 361 /** 362 * __prci_coreclksel_use_hfclk() - switch the CORECLK mux to output HFCLK 363 * @pd: struct __prci_data * for the PRCI containing the CORECLK mux reg 364 * 365 * Switch the CORECLK mux to the HFCLK input source; return once complete. 366 * 367 * Context: Any context. Caller must prevent concurrent changes to the 368 * PRCI_CORECLKSEL_OFFSET register. 369 */ 370 static void __prci_coreclksel_use_hfclk(struct __prci_data *pd) 371 { 372 u32 r; 373 374 r = __prci_readl(pd, PRCI_CORECLKSEL_OFFSET); 375 r |= PRCI_CORECLKSEL_CORECLKSEL_MASK; 376 __prci_writel(r, PRCI_CORECLKSEL_OFFSET, pd); 377 378 r = __prci_readl(pd, PRCI_CORECLKSEL_OFFSET); /* barrier */ 379 } 380 381 /** 382 * __prci_coreclksel_use_corepll() - switch the CORECLK mux to output COREPLL 383 * @pd: struct __prci_data * for the PRCI containing the CORECLK mux reg 384 * 385 * Switch the CORECLK mux to the PLL output clock; return once complete. 386 * 387 * Context: Any context. Caller must prevent concurrent changes to the 388 * PRCI_CORECLKSEL_OFFSET register. 389 */ 390 static void __prci_coreclksel_use_corepll(struct __prci_data *pd) 391 { 392 u32 r; 393 394 r = __prci_readl(pd, PRCI_CORECLKSEL_OFFSET); 395 r &= ~PRCI_CORECLKSEL_CORECLKSEL_MASK; 396 __prci_writel(r, PRCI_CORECLKSEL_OFFSET, pd); 397 398 r = __prci_readl(pd, PRCI_CORECLKSEL_OFFSET); /* barrier */ 399 } 400 401 static unsigned long sifive_fu540_prci_wrpll_recalc_rate( 402 struct __prci_clock *pc, 403 unsigned long parent_rate) 404 { 405 struct __prci_wrpll_data *pwd = pc->pwd; 406 407 return analogbits_wrpll_calc_output_rate(&pwd->c, parent_rate); 408 } 409 410 static unsigned long sifive_fu540_prci_wrpll_round_rate( 411 struct __prci_clock *pc, 412 unsigned long rate, 413 unsigned long *parent_rate) 414 { 415 struct __prci_wrpll_data *pwd = pc->pwd; 416 struct analogbits_wrpll_cfg c; 417 418 memcpy(&c, &pwd->c, sizeof(c)); 419 420 analogbits_wrpll_configure_for_rate(&c, rate, *parent_rate); 421 422 return analogbits_wrpll_calc_output_rate(&c, *parent_rate); 423 } 424 425 static int sifive_fu540_prci_wrpll_set_rate(struct __prci_clock *pc, 426 unsigned long rate, 427 unsigned long parent_rate) 428 { 429 struct __prci_wrpll_data *pwd = pc->pwd; 430 struct __prci_data *pd = pc->pd; 431 int r; 432 433 r = analogbits_wrpll_configure_for_rate(&pwd->c, rate, parent_rate); 434 if (r) 435 return -ERANGE; 436 437 if (pwd->bypass) 438 pwd->bypass(pd); 439 440 __prci_wrpll_write_cfg(pd, pwd, &pwd->c); 441 442 udelay(analogbits_wrpll_calc_max_lock_us(&pwd->c)); 443 444 if (pwd->no_bypass) 445 pwd->no_bypass(pd); 446 447 return 0; 448 } 449 450 static const struct __prci_clock_ops sifive_fu540_prci_wrpll_clk_ops = { 451 .set_rate = sifive_fu540_prci_wrpll_set_rate, 452 .round_rate = sifive_fu540_prci_wrpll_round_rate, 453 .recalc_rate = sifive_fu540_prci_wrpll_recalc_rate, 454 }; 455 456 static const struct __prci_clock_ops sifive_fu540_prci_wrpll_ro_clk_ops = { 457 .recalc_rate = sifive_fu540_prci_wrpll_recalc_rate, 458 }; 459 460 /* TLCLKSEL clock integration */ 461 462 static unsigned long sifive_fu540_prci_tlclksel_recalc_rate( 463 struct __prci_clock *pc, 464 unsigned long parent_rate) 465 { 466 struct __prci_data *pd = pc->pd; 467 u32 v; 468 u8 div; 469 470 v = __prci_readl(pd, PRCI_CLKMUXSTATUSREG_OFFSET); 471 v &= PRCI_CLKMUXSTATUSREG_TLCLKSEL_STATUS_MASK; 472 div = v ? 1 : 2; 473 474 return div_u64(parent_rate, div); 475 } 476 477 static const struct __prci_clock_ops sifive_fu540_prci_tlclksel_clk_ops = { 478 .recalc_rate = sifive_fu540_prci_tlclksel_recalc_rate, 479 }; 480 481 /* 482 * PRCI integration data for each WRPLL instance 483 */ 484 485 static struct __prci_wrpll_data __prci_corepll_data = { 486 .cfg0_offs = PRCI_COREPLLCFG0_OFFSET, 487 .bypass = __prci_coreclksel_use_hfclk, 488 .no_bypass = __prci_coreclksel_use_corepll, 489 }; 490 491 static struct __prci_wrpll_data __prci_ddrpll_data = { 492 .cfg0_offs = PRCI_DDRPLLCFG0_OFFSET, 493 }; 494 495 static struct __prci_wrpll_data __prci_gemgxlpll_data = { 496 .cfg0_offs = PRCI_GEMGXLPLLCFG0_OFFSET, 497 }; 498 499 /* 500 * List of clock controls provided by the PRCI 501 */ 502 503 static struct __prci_clock __prci_init_clocks[] = { 504 [PRCI_CLK_COREPLL] = { 505 .name = "corepll", 506 .parent_name = "hfclk", 507 .ops = &sifive_fu540_prci_wrpll_clk_ops, 508 .pwd = &__prci_corepll_data, 509 }, 510 [PRCI_CLK_DDRPLL] = { 511 .name = "ddrpll", 512 .parent_name = "hfclk", 513 .ops = &sifive_fu540_prci_wrpll_ro_clk_ops, 514 .pwd = &__prci_ddrpll_data, 515 }, 516 [PRCI_CLK_GEMGXLPLL] = { 517 .name = "gemgxlpll", 518 .parent_name = "hfclk", 519 .ops = &sifive_fu540_prci_wrpll_clk_ops, 520 .pwd = &__prci_gemgxlpll_data, 521 }, 522 [PRCI_CLK_TLCLK] = { 523 .name = "tlclk", 524 .parent_name = "corepll", 525 .ops = &sifive_fu540_prci_tlclksel_clk_ops, 526 }, 527 }; 528 529 static ulong sifive_fu540_prci_get_rate(struct clk *clk) 530 { 531 struct __prci_clock *pc; 532 533 if (ARRAY_SIZE(__prci_init_clocks) <= clk->id) 534 return -ENXIO; 535 536 pc = &__prci_init_clocks[clk->id]; 537 if (!pc->pd || !pc->ops->recalc_rate) 538 return -ENXIO; 539 540 return pc->ops->recalc_rate(pc, clk_get_rate(&pc->pd->parent)); 541 } 542 543 static ulong sifive_fu540_prci_set_rate(struct clk *clk, ulong rate) 544 { 545 int err; 546 struct __prci_clock *pc; 547 548 if (ARRAY_SIZE(__prci_init_clocks) <= clk->id) 549 return -ENXIO; 550 551 pc = &__prci_init_clocks[clk->id]; 552 if (!pc->pd || !pc->ops->set_rate) 553 return -ENXIO; 554 555 err = pc->ops->set_rate(pc, rate, clk_get_rate(&pc->pd->parent)); 556 if (err) 557 return err; 558 559 return rate; 560 } 561 562 static int sifive_fu540_prci_probe(struct udevice *dev) 563 { 564 int i, err; 565 struct __prci_clock *pc; 566 struct __prci_data *pd = dev_get_priv(dev); 567 568 pd->base = (void *)dev_read_addr(dev); 569 if (IS_ERR(pd->base)) 570 return PTR_ERR(pd->base); 571 572 err = clk_get_by_index(dev, 0, &pd->parent); 573 if (err) 574 return err; 575 576 for (i = 0; i < ARRAY_SIZE(__prci_init_clocks); ++i) { 577 pc = &__prci_init_clocks[i]; 578 pc->pd = pd; 579 if (pc->pwd) 580 __prci_wrpll_read_cfg(pd, pc->pwd); 581 } 582 583 return 0; 584 } 585 586 static struct clk_ops sifive_fu540_prci_ops = { 587 .set_rate = sifive_fu540_prci_set_rate, 588 .get_rate = sifive_fu540_prci_get_rate, 589 }; 590 591 static const struct udevice_id sifive_fu540_prci_ids[] = { 592 { .compatible = "sifive,fu540-c000-prci0" }, 593 { .compatible = "sifive,aloeprci0" }, 594 { } 595 }; 596 597 U_BOOT_DRIVER(sifive_fu540_prci) = { 598 .name = "sifive-fu540-prci", 599 .id = UCLASS_CLK, 600 .of_match = sifive_fu540_prci_ids, 601 .probe = sifive_fu540_prci_probe, 602 .ops = &sifive_fu540_prci_ops, 603 .priv_auto_alloc_size = sizeof(struct __prci_data), 604 }; 605