Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * Daire McNamara,<daire.mcnamara@microchip.com>
0004  * Copyright (C) 2020 Microchip Technology Inc.  All rights reserved.
0005  */
0006 #include <linux/clk-provider.h>
0007 #include <linux/io.h>
0008 #include <linux/module.h>
0009 #include <linux/platform_device.h>
0010 #include <linux/slab.h>
0011 #include <dt-bindings/clock/microchip,mpfs-clock.h>
0012 
0013 /* address offset of control registers */
0014 #define REG_MSSPLL_REF_CR   0x08u
0015 #define REG_MSSPLL_POSTDIV_CR   0x10u
0016 #define REG_MSSPLL_SSCG_2_CR    0x2Cu
0017 #define REG_CLOCK_CONFIG_CR 0x08u
0018 #define REG_RTC_CLOCK_CR    0x0Cu
0019 #define REG_SUBBLK_CLOCK_CR 0x84u
0020 #define REG_SUBBLK_RESET_CR 0x88u
0021 
0022 #define MSSPLL_FBDIV_SHIFT  0x00u
0023 #define MSSPLL_FBDIV_WIDTH  0x0Cu
0024 #define MSSPLL_REFDIV_SHIFT 0x08u
0025 #define MSSPLL_REFDIV_WIDTH 0x06u
0026 #define MSSPLL_POSTDIV_SHIFT    0x08u
0027 #define MSSPLL_POSTDIV_WIDTH    0x07u
0028 #define MSSPLL_FIXED_DIV    4u
0029 
0030 struct mpfs_clock_data {
0031     void __iomem *base;
0032     void __iomem *msspll_base;
0033     struct clk_hw_onecell_data hw_data;
0034 };
0035 
0036 struct mpfs_msspll_hw_clock {
0037     void __iomem *base;
0038     unsigned int id;
0039     u32 reg_offset;
0040     u32 shift;
0041     u32 width;
0042     u32 flags;
0043     struct clk_hw hw;
0044     struct clk_init_data init;
0045 };
0046 
0047 #define to_mpfs_msspll_clk(_hw) container_of(_hw, struct mpfs_msspll_hw_clock, hw)
0048 
0049 struct mpfs_cfg_clock {
0050     const struct clk_div_table *table;
0051     unsigned int id;
0052     u32 reg_offset;
0053     u8 shift;
0054     u8 width;
0055     u8 flags;
0056 };
0057 
0058 struct mpfs_cfg_hw_clock {
0059     struct mpfs_cfg_clock cfg;
0060     void __iomem *sys_base;
0061     struct clk_hw hw;
0062     struct clk_init_data init;
0063 };
0064 
0065 #define to_mpfs_cfg_clk(_hw) container_of(_hw, struct mpfs_cfg_hw_clock, hw)
0066 
0067 struct mpfs_periph_clock {
0068     unsigned int id;
0069     u8 shift;
0070 };
0071 
0072 struct mpfs_periph_hw_clock {
0073     struct mpfs_periph_clock periph;
0074     void __iomem *sys_base;
0075     struct clk_hw hw;
0076 };
0077 
0078 #define to_mpfs_periph_clk(_hw) container_of(_hw, struct mpfs_periph_hw_clock, hw)
0079 
0080 /*
0081  * mpfs_clk_lock prevents anything else from writing to the
0082  * mpfs clk block while a software locked register is being written.
0083  */
0084 static DEFINE_SPINLOCK(mpfs_clk_lock);
0085 
0086 static const struct clk_parent_data mpfs_ext_ref[] = {
0087     { .index = 0 },
0088 };
0089 
0090 static const struct clk_div_table mpfs_div_cpu_axi_table[] = {
0091     { 0, 1 }, { 1, 2 }, { 2, 4 }, { 3, 8 },
0092     { 0, 0 }
0093 };
0094 
0095 static const struct clk_div_table mpfs_div_ahb_table[] = {
0096     { 1, 2 }, { 2, 4}, { 3, 8 },
0097     { 0, 0 }
0098 };
0099 
0100 /*
0101  * The only two supported reference clock frequencies for the PolarFire SoC are
0102  * 100 and 125 MHz, as the rtc reference is required to be 1 MHz.
0103  * It therefore only needs to have divider table entries corresponding to
0104  * divide by 100 and 125.
0105  */
0106 static const struct clk_div_table mpfs_div_rtcref_table[] = {
0107     { 100, 100 }, { 125, 125 },
0108     { 0, 0 }
0109 };
0110 
0111 static unsigned long mpfs_clk_msspll_recalc_rate(struct clk_hw *hw, unsigned long prate)
0112 {
0113     struct mpfs_msspll_hw_clock *msspll_hw = to_mpfs_msspll_clk(hw);
0114     void __iomem *mult_addr = msspll_hw->base + msspll_hw->reg_offset;
0115     void __iomem *ref_div_addr = msspll_hw->base + REG_MSSPLL_REF_CR;
0116     void __iomem *postdiv_addr = msspll_hw->base + REG_MSSPLL_POSTDIV_CR;
0117     u32 mult, ref_div, postdiv;
0118 
0119     mult = readl_relaxed(mult_addr) >> MSSPLL_FBDIV_SHIFT;
0120     mult &= clk_div_mask(MSSPLL_FBDIV_WIDTH);
0121     ref_div = readl_relaxed(ref_div_addr) >> MSSPLL_REFDIV_SHIFT;
0122     ref_div &= clk_div_mask(MSSPLL_REFDIV_WIDTH);
0123     postdiv = readl_relaxed(postdiv_addr) >> MSSPLL_POSTDIV_SHIFT;
0124     postdiv &= clk_div_mask(MSSPLL_POSTDIV_WIDTH);
0125 
0126     return prate * mult / (ref_div * MSSPLL_FIXED_DIV * postdiv);
0127 }
0128 
0129 static const struct clk_ops mpfs_clk_msspll_ops = {
0130     .recalc_rate = mpfs_clk_msspll_recalc_rate,
0131 };
0132 
0133 #define CLK_PLL(_id, _name, _parent, _shift, _width, _flags, _offset) {         \
0134     .id = _id,                                  \
0135     .shift = _shift,                                \
0136     .width = _width,                                \
0137     .reg_offset = _offset,                              \
0138     .flags = _flags,                                \
0139     .hw.init = CLK_HW_INIT_PARENTS_DATA(_name, _parent, &mpfs_clk_msspll_ops, 0),   \
0140 }
0141 
0142 static struct mpfs_msspll_hw_clock mpfs_msspll_clks[] = {
0143     CLK_PLL(CLK_MSSPLL, "clk_msspll", mpfs_ext_ref, MSSPLL_FBDIV_SHIFT,
0144         MSSPLL_FBDIV_WIDTH, 0, REG_MSSPLL_SSCG_2_CR),
0145 };
0146 
0147 static int mpfs_clk_register_msspll(struct device *dev, struct mpfs_msspll_hw_clock *msspll_hw,
0148                     void __iomem *base)
0149 {
0150     msspll_hw->base = base;
0151 
0152     return devm_clk_hw_register(dev, &msspll_hw->hw);
0153 }
0154 
0155 static int mpfs_clk_register_mssplls(struct device *dev, struct mpfs_msspll_hw_clock *msspll_hws,
0156                      unsigned int num_clks, struct mpfs_clock_data *data)
0157 {
0158     void __iomem *base = data->msspll_base;
0159     unsigned int i;
0160     int ret;
0161 
0162     for (i = 0; i < num_clks; i++) {
0163         struct mpfs_msspll_hw_clock *msspll_hw = &msspll_hws[i];
0164 
0165         ret = mpfs_clk_register_msspll(dev, msspll_hw, base);
0166         if (ret)
0167             return dev_err_probe(dev, ret, "failed to register msspll id: %d\n",
0168                          CLK_MSSPLL);
0169 
0170         data->hw_data.hws[msspll_hw->id] = &msspll_hw->hw;
0171     }
0172 
0173     return 0;
0174 }
0175 
0176 /*
0177  * "CFG" clocks
0178  */
0179 
0180 static unsigned long mpfs_cfg_clk_recalc_rate(struct clk_hw *hw, unsigned long prate)
0181 {
0182     struct mpfs_cfg_hw_clock *cfg_hw = to_mpfs_cfg_clk(hw);
0183     struct mpfs_cfg_clock *cfg = &cfg_hw->cfg;
0184     void __iomem *base_addr = cfg_hw->sys_base;
0185     u32 val;
0186 
0187     val = readl_relaxed(base_addr + cfg->reg_offset) >> cfg->shift;
0188     val &= clk_div_mask(cfg->width);
0189 
0190     return divider_recalc_rate(hw, prate, val, cfg->table, cfg->flags, cfg->width);
0191 }
0192 
0193 static long mpfs_cfg_clk_round_rate(struct clk_hw *hw, unsigned long rate, unsigned long *prate)
0194 {
0195     struct mpfs_cfg_hw_clock *cfg_hw = to_mpfs_cfg_clk(hw);
0196     struct mpfs_cfg_clock *cfg = &cfg_hw->cfg;
0197 
0198     return divider_round_rate(hw, rate, prate, cfg->table, cfg->width, 0);
0199 }
0200 
0201 static int mpfs_cfg_clk_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long prate)
0202 {
0203     struct mpfs_cfg_hw_clock *cfg_hw = to_mpfs_cfg_clk(hw);
0204     struct mpfs_cfg_clock *cfg = &cfg_hw->cfg;
0205     void __iomem *base_addr = cfg_hw->sys_base;
0206     unsigned long flags;
0207     u32 val;
0208     int divider_setting;
0209 
0210     divider_setting = divider_get_val(rate, prate, cfg->table, cfg->width, 0);
0211 
0212     if (divider_setting < 0)
0213         return divider_setting;
0214 
0215     spin_lock_irqsave(&mpfs_clk_lock, flags);
0216     val = readl_relaxed(base_addr + cfg->reg_offset);
0217     val &= ~(clk_div_mask(cfg->width) << cfg_hw->cfg.shift);
0218     val |= divider_setting << cfg->shift;
0219     writel_relaxed(val, base_addr + cfg->reg_offset);
0220 
0221     spin_unlock_irqrestore(&mpfs_clk_lock, flags);
0222 
0223     return 0;
0224 }
0225 
0226 static const struct clk_ops mpfs_clk_cfg_ops = {
0227     .recalc_rate = mpfs_cfg_clk_recalc_rate,
0228     .round_rate = mpfs_cfg_clk_round_rate,
0229     .set_rate = mpfs_cfg_clk_set_rate,
0230 };
0231 
0232 #define CLK_CFG(_id, _name, _parent, _shift, _width, _table, _flags, _offset) {     \
0233     .cfg.id = _id,                                  \
0234     .cfg.shift = _shift,                                \
0235     .cfg.width = _width,                                \
0236     .cfg.table = _table,                                \
0237     .cfg.reg_offset = _offset,                          \
0238     .cfg.flags = _flags,                                \
0239     .hw.init = CLK_HW_INIT(_name, _parent, &mpfs_clk_cfg_ops, 0),           \
0240 }
0241 
0242 #define CLK_CPU_OFFSET      0u
0243 #define CLK_AXI_OFFSET      1u
0244 #define CLK_AHB_OFFSET      2u
0245 #define CLK_RTCREF_OFFSET   3u
0246 
0247 static struct mpfs_cfg_hw_clock mpfs_cfg_clks[] = {
0248     CLK_CFG(CLK_CPU, "clk_cpu", "clk_msspll", 0, 2, mpfs_div_cpu_axi_table, 0,
0249         REG_CLOCK_CONFIG_CR),
0250     CLK_CFG(CLK_AXI, "clk_axi", "clk_msspll", 2, 2, mpfs_div_cpu_axi_table, 0,
0251         REG_CLOCK_CONFIG_CR),
0252     CLK_CFG(CLK_AHB, "clk_ahb", "clk_msspll", 4, 2, mpfs_div_ahb_table, 0,
0253         REG_CLOCK_CONFIG_CR),
0254     {
0255         .cfg.id = CLK_RTCREF,
0256         .cfg.shift = 0,
0257         .cfg.width = 12,
0258         .cfg.table = mpfs_div_rtcref_table,
0259         .cfg.reg_offset = REG_RTC_CLOCK_CR,
0260         .cfg.flags = CLK_DIVIDER_ONE_BASED,
0261         .hw.init =
0262             CLK_HW_INIT_PARENTS_DATA("clk_rtcref", mpfs_ext_ref, &mpfs_clk_cfg_ops, 0),
0263     }
0264 };
0265 
0266 static int mpfs_clk_register_cfg(struct device *dev, struct mpfs_cfg_hw_clock *cfg_hw,
0267                  void __iomem *sys_base)
0268 {
0269     cfg_hw->sys_base = sys_base;
0270 
0271     return devm_clk_hw_register(dev, &cfg_hw->hw);
0272 }
0273 
0274 static int mpfs_clk_register_cfgs(struct device *dev, struct mpfs_cfg_hw_clock *cfg_hws,
0275                   unsigned int num_clks, struct mpfs_clock_data *data)
0276 {
0277     void __iomem *sys_base = data->base;
0278     unsigned int i, id;
0279     int ret;
0280 
0281     for (i = 0; i < num_clks; i++) {
0282         struct mpfs_cfg_hw_clock *cfg_hw = &cfg_hws[i];
0283 
0284         ret = mpfs_clk_register_cfg(dev, cfg_hw, sys_base);
0285         if (ret)
0286             return dev_err_probe(dev, ret, "failed to register clock id: %d\n",
0287                          cfg_hw->cfg.id);
0288 
0289         id = cfg_hw->cfg.id;
0290         data->hw_data.hws[id] = &cfg_hw->hw;
0291     }
0292 
0293     return 0;
0294 }
0295 
0296 /*
0297  * peripheral clocks - devices connected to axi or ahb buses.
0298  */
0299 
0300 static int mpfs_periph_clk_enable(struct clk_hw *hw)
0301 {
0302     struct mpfs_periph_hw_clock *periph_hw = to_mpfs_periph_clk(hw);
0303     struct mpfs_periph_clock *periph = &periph_hw->periph;
0304     void __iomem *base_addr = periph_hw->sys_base;
0305     u32 reg, val;
0306     unsigned long flags;
0307 
0308     spin_lock_irqsave(&mpfs_clk_lock, flags);
0309 
0310     reg = readl_relaxed(base_addr + REG_SUBBLK_RESET_CR);
0311     val = reg & ~(1u << periph->shift);
0312     writel_relaxed(val, base_addr + REG_SUBBLK_RESET_CR);
0313 
0314     reg = readl_relaxed(base_addr + REG_SUBBLK_CLOCK_CR);
0315     val = reg | (1u << periph->shift);
0316     writel_relaxed(val, base_addr + REG_SUBBLK_CLOCK_CR);
0317 
0318     spin_unlock_irqrestore(&mpfs_clk_lock, flags);
0319 
0320     return 0;
0321 }
0322 
0323 static void mpfs_periph_clk_disable(struct clk_hw *hw)
0324 {
0325     struct mpfs_periph_hw_clock *periph_hw = to_mpfs_periph_clk(hw);
0326     struct mpfs_periph_clock *periph = &periph_hw->periph;
0327     void __iomem *base_addr = periph_hw->sys_base;
0328     u32 reg, val;
0329     unsigned long flags;
0330 
0331     spin_lock_irqsave(&mpfs_clk_lock, flags);
0332 
0333     reg = readl_relaxed(base_addr + REG_SUBBLK_CLOCK_CR);
0334     val = reg & ~(1u << periph->shift);
0335     writel_relaxed(val, base_addr + REG_SUBBLK_CLOCK_CR);
0336 
0337     spin_unlock_irqrestore(&mpfs_clk_lock, flags);
0338 }
0339 
0340 static int mpfs_periph_clk_is_enabled(struct clk_hw *hw)
0341 {
0342     struct mpfs_periph_hw_clock *periph_hw = to_mpfs_periph_clk(hw);
0343     struct mpfs_periph_clock *periph = &periph_hw->periph;
0344     void __iomem *base_addr = periph_hw->sys_base;
0345     u32 reg;
0346 
0347     reg = readl_relaxed(base_addr + REG_SUBBLK_RESET_CR);
0348     if ((reg & (1u << periph->shift)) == 0u) {
0349         reg = readl_relaxed(base_addr + REG_SUBBLK_CLOCK_CR);
0350         if (reg & (1u << periph->shift))
0351             return 1;
0352     }
0353 
0354     return 0;
0355 }
0356 
0357 static const struct clk_ops mpfs_periph_clk_ops = {
0358     .enable = mpfs_periph_clk_enable,
0359     .disable = mpfs_periph_clk_disable,
0360     .is_enabled = mpfs_periph_clk_is_enabled,
0361 };
0362 
0363 #define CLK_PERIPH(_id, _name, _parent, _shift, _flags) {           \
0364     .periph.id = _id,                           \
0365     .periph.shift = _shift,                         \
0366     .hw.init = CLK_HW_INIT_HW(_name, _parent, &mpfs_periph_clk_ops,     \
0367                   _flags),                  \
0368 }
0369 
0370 #define PARENT_CLK(PARENT) (&mpfs_cfg_clks[CLK_##PARENT##_OFFSET].hw)
0371 
0372 /*
0373  * Critical clocks:
0374  * - CLK_ENVM: reserved by hart software services (hss) superloop monitor/m mode interrupt
0375  *   trap handler
0376  * - CLK_MMUART0: reserved by the hss
0377  * - CLK_DDRC: provides clock to the ddr subsystem
0378  * - CLK_RTC: the onboard RTC's AHB bus clock must be kept running as the rtc will stop
0379  *   if the AHB interface clock is disabled
0380  * - CLK_FICx: these provide the processor side clocks to the "FIC" (Fabric InterConnect)
0381  *   clock domain crossers which provide the interface to the FPGA fabric. Disabling them
0382  *   causes the FPGA fabric to go into reset.
0383  * - CLK_ATHENA: The athena clock is FIC4, which is reserved for the Athena TeraFire.
0384  */
0385 
0386 static struct mpfs_periph_hw_clock mpfs_periph_clks[] = {
0387     CLK_PERIPH(CLK_ENVM, "clk_periph_envm", PARENT_CLK(AHB), 0, CLK_IS_CRITICAL),
0388     CLK_PERIPH(CLK_MAC0, "clk_periph_mac0", PARENT_CLK(AHB), 1, 0),
0389     CLK_PERIPH(CLK_MAC1, "clk_periph_mac1", PARENT_CLK(AHB), 2, 0),
0390     CLK_PERIPH(CLK_MMC, "clk_periph_mmc", PARENT_CLK(AHB), 3, 0),
0391     CLK_PERIPH(CLK_TIMER, "clk_periph_timer", PARENT_CLK(RTCREF), 4, 0),
0392     CLK_PERIPH(CLK_MMUART0, "clk_periph_mmuart0", PARENT_CLK(AHB), 5, CLK_IS_CRITICAL),
0393     CLK_PERIPH(CLK_MMUART1, "clk_periph_mmuart1", PARENT_CLK(AHB), 6, 0),
0394     CLK_PERIPH(CLK_MMUART2, "clk_periph_mmuart2", PARENT_CLK(AHB), 7, 0),
0395     CLK_PERIPH(CLK_MMUART3, "clk_periph_mmuart3", PARENT_CLK(AHB), 8, 0),
0396     CLK_PERIPH(CLK_MMUART4, "clk_periph_mmuart4", PARENT_CLK(AHB), 9, 0),
0397     CLK_PERIPH(CLK_SPI0, "clk_periph_spi0", PARENT_CLK(AHB), 10, 0),
0398     CLK_PERIPH(CLK_SPI1, "clk_periph_spi1", PARENT_CLK(AHB), 11, 0),
0399     CLK_PERIPH(CLK_I2C0, "clk_periph_i2c0", PARENT_CLK(AHB), 12, 0),
0400     CLK_PERIPH(CLK_I2C1, "clk_periph_i2c1", PARENT_CLK(AHB), 13, 0),
0401     CLK_PERIPH(CLK_CAN0, "clk_periph_can0", PARENT_CLK(AHB), 14, 0),
0402     CLK_PERIPH(CLK_CAN1, "clk_periph_can1", PARENT_CLK(AHB), 15, 0),
0403     CLK_PERIPH(CLK_USB, "clk_periph_usb", PARENT_CLK(AHB), 16, 0),
0404     CLK_PERIPH(CLK_RTC, "clk_periph_rtc", PARENT_CLK(AHB), 18, CLK_IS_CRITICAL),
0405     CLK_PERIPH(CLK_QSPI, "clk_periph_qspi", PARENT_CLK(AHB), 19, 0),
0406     CLK_PERIPH(CLK_GPIO0, "clk_periph_gpio0", PARENT_CLK(AHB), 20, 0),
0407     CLK_PERIPH(CLK_GPIO1, "clk_periph_gpio1", PARENT_CLK(AHB), 21, 0),
0408     CLK_PERIPH(CLK_GPIO2, "clk_periph_gpio2", PARENT_CLK(AHB), 22, 0),
0409     CLK_PERIPH(CLK_DDRC, "clk_periph_ddrc", PARENT_CLK(AHB), 23, CLK_IS_CRITICAL),
0410     CLK_PERIPH(CLK_FIC0, "clk_periph_fic0", PARENT_CLK(AXI), 24, CLK_IS_CRITICAL),
0411     CLK_PERIPH(CLK_FIC1, "clk_periph_fic1", PARENT_CLK(AXI), 25, CLK_IS_CRITICAL),
0412     CLK_PERIPH(CLK_FIC2, "clk_periph_fic2", PARENT_CLK(AXI), 26, CLK_IS_CRITICAL),
0413     CLK_PERIPH(CLK_FIC3, "clk_periph_fic3", PARENT_CLK(AXI), 27, CLK_IS_CRITICAL),
0414     CLK_PERIPH(CLK_ATHENA, "clk_periph_athena", PARENT_CLK(AXI), 28, CLK_IS_CRITICAL),
0415     CLK_PERIPH(CLK_CFM, "clk_periph_cfm", PARENT_CLK(AHB), 29, 0),
0416 };
0417 
0418 static int mpfs_clk_register_periph(struct device *dev, struct mpfs_periph_hw_clock *periph_hw,
0419                     void __iomem *sys_base)
0420 {
0421     periph_hw->sys_base = sys_base;
0422 
0423     return devm_clk_hw_register(dev, &periph_hw->hw);
0424 }
0425 
0426 static int mpfs_clk_register_periphs(struct device *dev, struct mpfs_periph_hw_clock *periph_hws,
0427                      int num_clks, struct mpfs_clock_data *data)
0428 {
0429     void __iomem *sys_base = data->base;
0430     unsigned int i, id;
0431     int ret;
0432 
0433     for (i = 0; i < num_clks; i++) {
0434         struct mpfs_periph_hw_clock *periph_hw = &periph_hws[i];
0435 
0436         ret = mpfs_clk_register_periph(dev, periph_hw, sys_base);
0437         if (ret)
0438             return dev_err_probe(dev, ret, "failed to register clock id: %d\n",
0439                          periph_hw->periph.id);
0440 
0441         id = periph_hws[i].periph.id;
0442         data->hw_data.hws[id] = &periph_hw->hw;
0443     }
0444 
0445     return 0;
0446 }
0447 
0448 static int mpfs_clk_probe(struct platform_device *pdev)
0449 {
0450     struct device *dev = &pdev->dev;
0451     struct mpfs_clock_data *clk_data;
0452     unsigned int num_clks;
0453     int ret;
0454 
0455     /* CLK_RESERVED is not part of clock arrays, so add 1 */
0456     num_clks = ARRAY_SIZE(mpfs_msspll_clks) + ARRAY_SIZE(mpfs_cfg_clks)
0457            + ARRAY_SIZE(mpfs_periph_clks) + 1;
0458 
0459     clk_data = devm_kzalloc(dev, struct_size(clk_data, hw_data.hws, num_clks), GFP_KERNEL);
0460     if (!clk_data)
0461         return -ENOMEM;
0462 
0463     clk_data->base = devm_platform_ioremap_resource(pdev, 0);
0464     if (IS_ERR(clk_data->base))
0465         return PTR_ERR(clk_data->base);
0466 
0467     clk_data->msspll_base = devm_platform_ioremap_resource(pdev, 1);
0468     if (IS_ERR(clk_data->msspll_base))
0469         return PTR_ERR(clk_data->msspll_base);
0470 
0471     clk_data->hw_data.num = num_clks;
0472 
0473     ret = mpfs_clk_register_mssplls(dev, mpfs_msspll_clks, ARRAY_SIZE(mpfs_msspll_clks),
0474                     clk_data);
0475     if (ret)
0476         return ret;
0477 
0478     ret = mpfs_clk_register_cfgs(dev, mpfs_cfg_clks, ARRAY_SIZE(mpfs_cfg_clks), clk_data);
0479     if (ret)
0480         return ret;
0481 
0482     ret = mpfs_clk_register_periphs(dev, mpfs_periph_clks, ARRAY_SIZE(mpfs_periph_clks),
0483                     clk_data);
0484     if (ret)
0485         return ret;
0486 
0487     ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, &clk_data->hw_data);
0488     if (ret)
0489         return ret;
0490 
0491     return ret;
0492 }
0493 
0494 static const struct of_device_id mpfs_clk_of_match_table[] = {
0495     { .compatible = "microchip,mpfs-clkcfg", },
0496     {}
0497 };
0498 MODULE_DEVICE_TABLE(of, mpfs_clk_match_table);
0499 
0500 static struct platform_driver mpfs_clk_driver = {
0501     .probe = mpfs_clk_probe,
0502     .driver = {
0503         .name = "microchip-mpfs-clkcfg",
0504         .of_match_table = mpfs_clk_of_match_table,
0505     },
0506 };
0507 
0508 static int __init clk_mpfs_init(void)
0509 {
0510     return platform_driver_register(&mpfs_clk_driver);
0511 }
0512 core_initcall(clk_mpfs_init);
0513 
0514 static void __exit clk_mpfs_exit(void)
0515 {
0516     platform_driver_unregister(&mpfs_clk_driver);
0517 }
0518 module_exit(clk_mpfs_exit);
0519 
0520 MODULE_DESCRIPTION("Microchip PolarFire SoC Clock Driver");
0521 MODULE_LICENSE("GPL v2");