Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * Copyright (C) 2020 SiFive, Inc.
0004  * Copyright (C) 2020 Zong Li
0005  */
0006 
0007 #include <linux/clkdev.h>
0008 #include <linux/delay.h>
0009 #include <linux/io.h>
0010 #include <linux/of_device.h>
0011 #include "sifive-prci.h"
0012 #include "fu540-prci.h"
0013 #include "fu740-prci.h"
0014 
0015 /*
0016  * Private functions
0017  */
0018 
0019 /**
0020  * __prci_readl() - read from a PRCI register
0021  * @pd: PRCI context
0022  * @offs: register offset to read from (in bytes, from PRCI base address)
0023  *
0024  * Read the register located at offset @offs from the base virtual
0025  * address of the PRCI register target described by @pd, and return
0026  * the value to the caller.
0027  *
0028  * Context: Any context.
0029  *
0030  * Return: the contents of the register described by @pd and @offs.
0031  */
0032 static u32 __prci_readl(struct __prci_data *pd, u32 offs)
0033 {
0034     return readl_relaxed(pd->va + offs);
0035 }
0036 
0037 static void __prci_writel(u32 v, u32 offs, struct __prci_data *pd)
0038 {
0039     writel_relaxed(v, pd->va + offs);
0040 }
0041 
0042 /* WRPLL-related private functions */
0043 
0044 /**
0045  * __prci_wrpll_unpack() - unpack WRPLL configuration registers into parameters
0046  * @c: ptr to a struct wrpll_cfg record to write config into
0047  * @r: value read from the PRCI PLL configuration register
0048  *
0049  * Given a value @r read from an FU740 PRCI PLL configuration register,
0050  * split it into fields and populate it into the WRPLL configuration record
0051  * pointed to by @c.
0052  *
0053  * The COREPLLCFG0 macros are used below, but the other *PLLCFG0 macros
0054  * have the same register layout.
0055  *
0056  * Context: Any context.
0057  */
0058 static void __prci_wrpll_unpack(struct wrpll_cfg *c, u32 r)
0059 {
0060     u32 v;
0061 
0062     v = r & PRCI_COREPLLCFG0_DIVR_MASK;
0063     v >>= PRCI_COREPLLCFG0_DIVR_SHIFT;
0064     c->divr = v;
0065 
0066     v = r & PRCI_COREPLLCFG0_DIVF_MASK;
0067     v >>= PRCI_COREPLLCFG0_DIVF_SHIFT;
0068     c->divf = v;
0069 
0070     v = r & PRCI_COREPLLCFG0_DIVQ_MASK;
0071     v >>= PRCI_COREPLLCFG0_DIVQ_SHIFT;
0072     c->divq = v;
0073 
0074     v = r & PRCI_COREPLLCFG0_RANGE_MASK;
0075     v >>= PRCI_COREPLLCFG0_RANGE_SHIFT;
0076     c->range = v;
0077 
0078     c->flags &=
0079         (WRPLL_FLAGS_INT_FEEDBACK_MASK | WRPLL_FLAGS_EXT_FEEDBACK_MASK);
0080 
0081     /* external feedback mode not supported */
0082     c->flags |= WRPLL_FLAGS_INT_FEEDBACK_MASK;
0083 }
0084 
0085 /**
0086  * __prci_wrpll_pack() - pack PLL configuration parameters into a register value
0087  * @c: pointer to a struct wrpll_cfg record containing the PLL's cfg
0088  *
0089  * Using a set of WRPLL configuration values pointed to by @c,
0090  * assemble a PRCI PLL configuration register value, and return it to
0091  * the caller.
0092  *
0093  * Context: Any context.  Caller must ensure that the contents of the
0094  *          record pointed to by @c do not change during the execution
0095  *          of this function.
0096  *
0097  * Returns: a value suitable for writing into a PRCI PLL configuration
0098  *          register
0099  */
0100 static u32 __prci_wrpll_pack(const struct wrpll_cfg *c)
0101 {
0102     u32 r = 0;
0103 
0104     r |= c->divr << PRCI_COREPLLCFG0_DIVR_SHIFT;
0105     r |= c->divf << PRCI_COREPLLCFG0_DIVF_SHIFT;
0106     r |= c->divq << PRCI_COREPLLCFG0_DIVQ_SHIFT;
0107     r |= c->range << PRCI_COREPLLCFG0_RANGE_SHIFT;
0108 
0109     /* external feedback mode not supported */
0110     r |= PRCI_COREPLLCFG0_FSE_MASK;
0111 
0112     return r;
0113 }
0114 
0115 /**
0116  * __prci_wrpll_read_cfg0() - read the WRPLL configuration from the PRCI
0117  * @pd: PRCI context
0118  * @pwd: PRCI WRPLL metadata
0119  *
0120  * Read the current configuration of the PLL identified by @pwd from
0121  * the PRCI identified by @pd, and store it into the local configuration
0122  * cache in @pwd.
0123  *
0124  * Context: Any context.  Caller must prevent the records pointed to by
0125  *          @pd and @pwd from changing during execution.
0126  */
0127 static void __prci_wrpll_read_cfg0(struct __prci_data *pd,
0128                    struct __prci_wrpll_data *pwd)
0129 {
0130     __prci_wrpll_unpack(&pwd->c, __prci_readl(pd, pwd->cfg0_offs));
0131 }
0132 
0133 /**
0134  * __prci_wrpll_write_cfg0() - write WRPLL configuration into the PRCI
0135  * @pd: PRCI context
0136  * @pwd: PRCI WRPLL metadata
0137  * @c: WRPLL configuration record to write
0138  *
0139  * Write the WRPLL configuration described by @c into the WRPLL
0140  * configuration register identified by @pwd in the PRCI instance
0141  * described by @c.  Make a cached copy of the WRPLL's current
0142  * configuration so it can be used by other code.
0143  *
0144  * Context: Any context.  Caller must prevent the records pointed to by
0145  *          @pd and @pwd from changing during execution.
0146  */
0147 static void __prci_wrpll_write_cfg0(struct __prci_data *pd,
0148                     struct __prci_wrpll_data *pwd,
0149                     struct wrpll_cfg *c)
0150 {
0151     __prci_writel(__prci_wrpll_pack(c), pwd->cfg0_offs, pd);
0152 
0153     memcpy(&pwd->c, c, sizeof(*c));
0154 }
0155 
0156 /**
0157  * __prci_wrpll_write_cfg1() - write Clock enable/disable configuration
0158  * into the PRCI
0159  * @pd: PRCI context
0160  * @pwd: PRCI WRPLL metadata
0161  * @enable: Clock enable or disable value
0162  */
0163 static void __prci_wrpll_write_cfg1(struct __prci_data *pd,
0164                     struct __prci_wrpll_data *pwd,
0165                     u32 enable)
0166 {
0167     __prci_writel(enable, pwd->cfg1_offs, pd);
0168 }
0169 
0170 /*
0171  * Linux clock framework integration
0172  *
0173  * See the Linux clock framework documentation for more information on
0174  * these functions.
0175  */
0176 
0177 unsigned long sifive_prci_wrpll_recalc_rate(struct clk_hw *hw,
0178                         unsigned long parent_rate)
0179 {
0180     struct __prci_clock *pc = clk_hw_to_prci_clock(hw);
0181     struct __prci_wrpll_data *pwd = pc->pwd;
0182 
0183     return wrpll_calc_output_rate(&pwd->c, parent_rate);
0184 }
0185 
0186 long sifive_prci_wrpll_round_rate(struct clk_hw *hw,
0187                   unsigned long rate,
0188                   unsigned long *parent_rate)
0189 {
0190     struct __prci_clock *pc = clk_hw_to_prci_clock(hw);
0191     struct __prci_wrpll_data *pwd = pc->pwd;
0192     struct wrpll_cfg c;
0193 
0194     memcpy(&c, &pwd->c, sizeof(c));
0195 
0196     wrpll_configure_for_rate(&c, rate, *parent_rate);
0197 
0198     return wrpll_calc_output_rate(&c, *parent_rate);
0199 }
0200 
0201 int sifive_prci_wrpll_set_rate(struct clk_hw *hw,
0202                    unsigned long rate, unsigned long parent_rate)
0203 {
0204     struct __prci_clock *pc = clk_hw_to_prci_clock(hw);
0205     struct __prci_wrpll_data *pwd = pc->pwd;
0206     struct __prci_data *pd = pc->pd;
0207     int r;
0208 
0209     r = wrpll_configure_for_rate(&pwd->c, rate, parent_rate);
0210     if (r)
0211         return r;
0212 
0213     if (pwd->enable_bypass)
0214         pwd->enable_bypass(pd);
0215 
0216     __prci_wrpll_write_cfg0(pd, pwd, &pwd->c);
0217 
0218     udelay(wrpll_calc_max_lock_us(&pwd->c));
0219 
0220     return 0;
0221 }
0222 
0223 int sifive_clk_is_enabled(struct clk_hw *hw)
0224 {
0225     struct __prci_clock *pc = clk_hw_to_prci_clock(hw);
0226     struct __prci_wrpll_data *pwd = pc->pwd;
0227     struct __prci_data *pd = pc->pd;
0228     u32 r;
0229 
0230     r = __prci_readl(pd, pwd->cfg1_offs);
0231 
0232     if (r & PRCI_COREPLLCFG1_CKE_MASK)
0233         return 1;
0234     else
0235         return 0;
0236 }
0237 
0238 int sifive_prci_clock_enable(struct clk_hw *hw)
0239 {
0240     struct __prci_clock *pc = clk_hw_to_prci_clock(hw);
0241     struct __prci_wrpll_data *pwd = pc->pwd;
0242     struct __prci_data *pd = pc->pd;
0243 
0244     if (sifive_clk_is_enabled(hw))
0245         return 0;
0246 
0247     __prci_wrpll_write_cfg1(pd, pwd, PRCI_COREPLLCFG1_CKE_MASK);
0248 
0249     if (pwd->disable_bypass)
0250         pwd->disable_bypass(pd);
0251 
0252     return 0;
0253 }
0254 
0255 void sifive_prci_clock_disable(struct clk_hw *hw)
0256 {
0257     struct __prci_clock *pc = clk_hw_to_prci_clock(hw);
0258     struct __prci_wrpll_data *pwd = pc->pwd;
0259     struct __prci_data *pd = pc->pd;
0260     u32 r;
0261 
0262     if (pwd->enable_bypass)
0263         pwd->enable_bypass(pd);
0264 
0265     r = __prci_readl(pd, pwd->cfg1_offs);
0266     r &= ~PRCI_COREPLLCFG1_CKE_MASK;
0267 
0268     __prci_wrpll_write_cfg1(pd, pwd, r);
0269 }
0270 
0271 /* TLCLKSEL clock integration */
0272 
0273 unsigned long sifive_prci_tlclksel_recalc_rate(struct clk_hw *hw,
0274                            unsigned long parent_rate)
0275 {
0276     struct __prci_clock *pc = clk_hw_to_prci_clock(hw);
0277     struct __prci_data *pd = pc->pd;
0278     u32 v;
0279     u8 div;
0280 
0281     v = __prci_readl(pd, PRCI_CLKMUXSTATUSREG_OFFSET);
0282     v &= PRCI_CLKMUXSTATUSREG_TLCLKSEL_STATUS_MASK;
0283     div = v ? 1 : 2;
0284 
0285     return div_u64(parent_rate, div);
0286 }
0287 
0288 /* HFPCLK clock integration */
0289 
0290 unsigned long sifive_prci_hfpclkplldiv_recalc_rate(struct clk_hw *hw,
0291                            unsigned long parent_rate)
0292 {
0293     struct __prci_clock *pc = clk_hw_to_prci_clock(hw);
0294     struct __prci_data *pd = pc->pd;
0295     u32 div = __prci_readl(pd, PRCI_HFPCLKPLLDIV_OFFSET);
0296 
0297     return div_u64(parent_rate, div + 2);
0298 }
0299 
0300 /*
0301  * Core clock mux control
0302  */
0303 
0304 /**
0305  * sifive_prci_coreclksel_use_hfclk() - switch the CORECLK mux to output HFCLK
0306  * @pd: struct __prci_data * for the PRCI containing the CORECLK mux reg
0307  *
0308  * Switch the CORECLK mux to the HFCLK input source; return once complete.
0309  *
0310  * Context: Any context.  Caller must prevent concurrent changes to the
0311  *          PRCI_CORECLKSEL_OFFSET register.
0312  */
0313 void sifive_prci_coreclksel_use_hfclk(struct __prci_data *pd)
0314 {
0315     u32 r;
0316 
0317     r = __prci_readl(pd, PRCI_CORECLKSEL_OFFSET);
0318     r |= PRCI_CORECLKSEL_CORECLKSEL_MASK;
0319     __prci_writel(r, PRCI_CORECLKSEL_OFFSET, pd);
0320 
0321     r = __prci_readl(pd, PRCI_CORECLKSEL_OFFSET);   /* barrier */
0322 }
0323 
0324 /**
0325  * sifive_prci_coreclksel_use_corepll() - switch the CORECLK mux to output
0326  * COREPLL
0327  * @pd: struct __prci_data * for the PRCI containing the CORECLK mux reg
0328  *
0329  * Switch the CORECLK mux to the COREPLL output clock; return once complete.
0330  *
0331  * Context: Any context.  Caller must prevent concurrent changes to the
0332  *          PRCI_CORECLKSEL_OFFSET register.
0333  */
0334 void sifive_prci_coreclksel_use_corepll(struct __prci_data *pd)
0335 {
0336     u32 r;
0337 
0338     r = __prci_readl(pd, PRCI_CORECLKSEL_OFFSET);
0339     r &= ~PRCI_CORECLKSEL_CORECLKSEL_MASK;
0340     __prci_writel(r, PRCI_CORECLKSEL_OFFSET, pd);
0341 
0342     r = __prci_readl(pd, PRCI_CORECLKSEL_OFFSET);   /* barrier */
0343 }
0344 
0345 /**
0346  * sifive_prci_coreclksel_use_final_corepll() - switch the CORECLK mux to output
0347  * FINAL_COREPLL
0348  * @pd: struct __prci_data * for the PRCI containing the CORECLK mux reg
0349  *
0350  * Switch the CORECLK mux to the final COREPLL output clock; return once
0351  * complete.
0352  *
0353  * Context: Any context.  Caller must prevent concurrent changes to the
0354  *          PRCI_CORECLKSEL_OFFSET register.
0355  */
0356 void sifive_prci_coreclksel_use_final_corepll(struct __prci_data *pd)
0357 {
0358     u32 r;
0359 
0360     r = __prci_readl(pd, PRCI_CORECLKSEL_OFFSET);
0361     r &= ~PRCI_CORECLKSEL_CORECLKSEL_MASK;
0362     __prci_writel(r, PRCI_CORECLKSEL_OFFSET, pd);
0363 
0364     r = __prci_readl(pd, PRCI_CORECLKSEL_OFFSET);   /* barrier */
0365 }
0366 
0367 /**
0368  * sifive_prci_corepllsel_use_dvfscorepll() - switch the COREPLL mux to
0369  * output DVFS_COREPLL
0370  * @pd: struct __prci_data * for the PRCI containing the COREPLL mux reg
0371  *
0372  * Switch the COREPLL mux to the DVFSCOREPLL output clock; return once complete.
0373  *
0374  * Context: Any context.  Caller must prevent concurrent changes to the
0375  *          PRCI_COREPLLSEL_OFFSET register.
0376  */
0377 void sifive_prci_corepllsel_use_dvfscorepll(struct __prci_data *pd)
0378 {
0379     u32 r;
0380 
0381     r = __prci_readl(pd, PRCI_COREPLLSEL_OFFSET);
0382     r |= PRCI_COREPLLSEL_COREPLLSEL_MASK;
0383     __prci_writel(r, PRCI_COREPLLSEL_OFFSET, pd);
0384 
0385     r = __prci_readl(pd, PRCI_COREPLLSEL_OFFSET);   /* barrier */
0386 }
0387 
0388 /**
0389  * sifive_prci_corepllsel_use_corepll() - switch the COREPLL mux to
0390  * output COREPLL
0391  * @pd: struct __prci_data * for the PRCI containing the COREPLL mux reg
0392  *
0393  * Switch the COREPLL mux to the COREPLL output clock; return once complete.
0394  *
0395  * Context: Any context.  Caller must prevent concurrent changes to the
0396  *          PRCI_COREPLLSEL_OFFSET register.
0397  */
0398 void sifive_prci_corepllsel_use_corepll(struct __prci_data *pd)
0399 {
0400     u32 r;
0401 
0402     r = __prci_readl(pd, PRCI_COREPLLSEL_OFFSET);
0403     r &= ~PRCI_COREPLLSEL_COREPLLSEL_MASK;
0404     __prci_writel(r, PRCI_COREPLLSEL_OFFSET, pd);
0405 
0406     r = __prci_readl(pd, PRCI_COREPLLSEL_OFFSET);   /* barrier */
0407 }
0408 
0409 /**
0410  * sifive_prci_hfpclkpllsel_use_hfclk() - switch the HFPCLKPLL mux to
0411  * output HFCLK
0412  * @pd: struct __prci_data * for the PRCI containing the HFPCLKPLL mux reg
0413  *
0414  * Switch the HFPCLKPLL mux to the HFCLK input source; return once complete.
0415  *
0416  * Context: Any context.  Caller must prevent concurrent changes to the
0417  *          PRCI_HFPCLKPLLSEL_OFFSET register.
0418  */
0419 void sifive_prci_hfpclkpllsel_use_hfclk(struct __prci_data *pd)
0420 {
0421     u32 r;
0422 
0423     r = __prci_readl(pd, PRCI_HFPCLKPLLSEL_OFFSET);
0424     r |= PRCI_HFPCLKPLLSEL_HFPCLKPLLSEL_MASK;
0425     __prci_writel(r, PRCI_HFPCLKPLLSEL_OFFSET, pd);
0426 
0427     r = __prci_readl(pd, PRCI_HFPCLKPLLSEL_OFFSET); /* barrier */
0428 }
0429 
0430 /**
0431  * sifive_prci_hfpclkpllsel_use_hfpclkpll() - switch the HFPCLKPLL mux to
0432  * output HFPCLKPLL
0433  * @pd: struct __prci_data * for the PRCI containing the HFPCLKPLL mux reg
0434  *
0435  * Switch the HFPCLKPLL mux to the HFPCLKPLL output clock; return once complete.
0436  *
0437  * Context: Any context.  Caller must prevent concurrent changes to the
0438  *          PRCI_HFPCLKPLLSEL_OFFSET register.
0439  */
0440 void sifive_prci_hfpclkpllsel_use_hfpclkpll(struct __prci_data *pd)
0441 {
0442     u32 r;
0443 
0444     r = __prci_readl(pd, PRCI_HFPCLKPLLSEL_OFFSET);
0445     r &= ~PRCI_HFPCLKPLLSEL_HFPCLKPLLSEL_MASK;
0446     __prci_writel(r, PRCI_HFPCLKPLLSEL_OFFSET, pd);
0447 
0448     r = __prci_readl(pd, PRCI_HFPCLKPLLSEL_OFFSET); /* barrier */
0449 }
0450 
0451 /* PCIE AUX clock APIs for enable, disable. */
0452 int sifive_prci_pcie_aux_clock_is_enabled(struct clk_hw *hw)
0453 {
0454     struct __prci_clock *pc = clk_hw_to_prci_clock(hw);
0455     struct __prci_data *pd = pc->pd;
0456     u32 r;
0457 
0458     r = __prci_readl(pd, PRCI_PCIE_AUX_OFFSET);
0459 
0460     if (r & PRCI_PCIE_AUX_EN_MASK)
0461         return 1;
0462     else
0463         return 0;
0464 }
0465 
0466 int sifive_prci_pcie_aux_clock_enable(struct clk_hw *hw)
0467 {
0468     struct __prci_clock *pc = clk_hw_to_prci_clock(hw);
0469     struct __prci_data *pd = pc->pd;
0470     u32 r __maybe_unused;
0471 
0472     if (sifive_prci_pcie_aux_clock_is_enabled(hw))
0473         return 0;
0474 
0475     __prci_writel(1, PRCI_PCIE_AUX_OFFSET, pd);
0476     r = __prci_readl(pd, PRCI_PCIE_AUX_OFFSET); /* barrier */
0477 
0478     return 0;
0479 }
0480 
0481 void sifive_prci_pcie_aux_clock_disable(struct clk_hw *hw)
0482 {
0483     struct __prci_clock *pc = clk_hw_to_prci_clock(hw);
0484     struct __prci_data *pd = pc->pd;
0485     u32 r __maybe_unused;
0486 
0487     __prci_writel(0, PRCI_PCIE_AUX_OFFSET, pd);
0488     r = __prci_readl(pd, PRCI_PCIE_AUX_OFFSET); /* barrier */
0489 
0490 }
0491 
0492 /**
0493  * __prci_register_clocks() - register clock controls in the PRCI
0494  * @dev: Linux struct device
0495  * @pd: The pointer for PRCI per-device instance data
0496  * @desc: The pointer for the information of clocks of each SoCs
0497  *
0498  * Register the list of clock controls described in __prci_init_clocks[] with
0499  * the Linux clock framework.
0500  *
0501  * Return: 0 upon success or a negative error code upon failure.
0502  */
0503 static int __prci_register_clocks(struct device *dev, struct __prci_data *pd,
0504                   const struct prci_clk_desc *desc)
0505 {
0506     struct clk_init_data init = { };
0507     struct __prci_clock *pic;
0508     int parent_count, i, r;
0509 
0510     parent_count = of_clk_get_parent_count(dev->of_node);
0511     if (parent_count != EXPECTED_CLK_PARENT_COUNT) {
0512         dev_err(dev, "expected only two parent clocks, found %d\n",
0513             parent_count);
0514         return -EINVAL;
0515     }
0516 
0517     /* Register PLLs */
0518     for (i = 0; i < desc->num_clks; ++i) {
0519         pic = &(desc->clks[i]);
0520 
0521         init.name = pic->name;
0522         init.parent_names = &pic->parent_name;
0523         init.num_parents = 1;
0524         init.ops = pic->ops;
0525         pic->hw.init = &init;
0526 
0527         pic->pd = pd;
0528 
0529         if (pic->pwd)
0530             __prci_wrpll_read_cfg0(pd, pic->pwd);
0531 
0532         r = devm_clk_hw_register(dev, &pic->hw);
0533         if (r) {
0534             dev_warn(dev, "Failed to register clock %s: %d\n",
0535                  init.name, r);
0536             return r;
0537         }
0538 
0539         r = clk_hw_register_clkdev(&pic->hw, pic->name, dev_name(dev));
0540         if (r) {
0541             dev_warn(dev, "Failed to register clkdev for %s: %d\n",
0542                  init.name, r);
0543             return r;
0544         }
0545 
0546         pd->hw_clks.hws[i] = &pic->hw;
0547     }
0548 
0549     pd->hw_clks.num = i;
0550 
0551     r = devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get,
0552                     &pd->hw_clks);
0553     if (r) {
0554         dev_err(dev, "could not add hw_provider: %d\n", r);
0555         return r;
0556     }
0557 
0558     return 0;
0559 }
0560 
0561 /**
0562  * sifive_prci_probe() - initialize prci data and check parent count
0563  * @pdev: platform device pointer for the prci
0564  *
0565  * Return: 0 upon success or a negative error code upon failure.
0566  */
0567 static int sifive_prci_probe(struct platform_device *pdev)
0568 {
0569     struct device *dev = &pdev->dev;
0570     struct resource *res;
0571     struct __prci_data *pd;
0572     const struct prci_clk_desc *desc;
0573     int r;
0574 
0575     desc = of_device_get_match_data(&pdev->dev);
0576 
0577     pd = devm_kzalloc(dev, struct_size(pd, hw_clks.hws, desc->num_clks), GFP_KERNEL);
0578     if (!pd)
0579         return -ENOMEM;
0580 
0581     res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
0582     pd->va = devm_ioremap_resource(dev, res);
0583     if (IS_ERR(pd->va))
0584         return PTR_ERR(pd->va);
0585 
0586     pd->reset.rcdev.owner = THIS_MODULE;
0587     pd->reset.rcdev.nr_resets = PRCI_RST_NR;
0588     pd->reset.rcdev.ops = &reset_simple_ops;
0589     pd->reset.rcdev.of_node = pdev->dev.of_node;
0590     pd->reset.active_low = true;
0591     pd->reset.membase = pd->va + PRCI_DEVICESRESETREG_OFFSET;
0592     spin_lock_init(&pd->reset.lock);
0593 
0594     r = devm_reset_controller_register(&pdev->dev, &pd->reset.rcdev);
0595     if (r) {
0596         dev_err(dev, "could not register reset controller: %d\n", r);
0597         return r;
0598     }
0599     r = __prci_register_clocks(dev, pd, desc);
0600     if (r) {
0601         dev_err(dev, "could not register clocks: %d\n", r);
0602         return r;
0603     }
0604 
0605     dev_dbg(dev, "SiFive PRCI probed\n");
0606 
0607     return 0;
0608 }
0609 
0610 static const struct of_device_id sifive_prci_of_match[] = {
0611     {.compatible = "sifive,fu540-c000-prci", .data = &prci_clk_fu540},
0612     {.compatible = "sifive,fu740-c000-prci", .data = &prci_clk_fu740},
0613     {}
0614 };
0615 
0616 static struct platform_driver sifive_prci_driver = {
0617     .driver = {
0618         .name = "sifive-clk-prci",
0619         .of_match_table = sifive_prci_of_match,
0620     },
0621     .probe = sifive_prci_probe,
0622 };
0623 
0624 static int __init sifive_prci_init(void)
0625 {
0626     return platform_driver_register(&sifive_prci_driver);
0627 }
0628 core_initcall(sifive_prci_init);