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