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