linux/drivers/clk/microchip/clk-mpfs.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Daire McNamara,<daire.mcnamara@microchip.com>
   4 * Copyright (C) 2020 Microchip Technology Inc.  All rights reserved.
   5 */
   6#include <linux/clk-provider.h>
   7#include <linux/io.h>
   8#include <linux/module.h>
   9#include <linux/platform_device.h>
  10#include <linux/slab.h>
  11#include <dt-bindings/clock/microchip,mpfs-clock.h>
  12
  13/* address offset of control registers */
  14#define REG_MSSPLL_REF_CR       0x08u
  15#define REG_MSSPLL_POSTDIV_CR   0x10u
  16#define REG_MSSPLL_SSCG_2_CR    0x2Cu
  17#define REG_CLOCK_CONFIG_CR     0x08u
  18#define REG_RTC_CLOCK_CR        0x0Cu
  19#define REG_SUBBLK_CLOCK_CR     0x84u
  20#define REG_SUBBLK_RESET_CR     0x88u
  21
  22#define MSSPLL_FBDIV_SHIFT      0x00u
  23#define MSSPLL_FBDIV_WIDTH      0x0Cu
  24#define MSSPLL_REFDIV_SHIFT     0x08u
  25#define MSSPLL_REFDIV_WIDTH     0x06u
  26#define MSSPLL_POSTDIV_SHIFT    0x08u
  27#define MSSPLL_POSTDIV_WIDTH    0x07u
  28#define MSSPLL_FIXED_DIV        4u
  29
  30struct mpfs_clock_data {
  31        void __iomem *base;
  32        void __iomem *msspll_base;
  33        struct clk_hw_onecell_data hw_data;
  34};
  35
  36struct mpfs_msspll_hw_clock {
  37        void __iomem *base;
  38        unsigned int id;
  39        u32 reg_offset;
  40        u32 shift;
  41        u32 width;
  42        u32 flags;
  43        struct clk_hw hw;
  44        struct clk_init_data init;
  45};
  46
  47#define to_mpfs_msspll_clk(_hw) container_of(_hw, struct mpfs_msspll_hw_clock, hw)
  48
  49struct mpfs_cfg_clock {
  50        const struct clk_div_table *table;
  51        unsigned int id;
  52        u32 reg_offset;
  53        u8 shift;
  54        u8 width;
  55        u8 flags;
  56};
  57
  58struct mpfs_cfg_hw_clock {
  59        struct mpfs_cfg_clock cfg;
  60        void __iomem *sys_base;
  61        struct clk_hw hw;
  62        struct clk_init_data init;
  63};
  64
  65#define to_mpfs_cfg_clk(_hw) container_of(_hw, struct mpfs_cfg_hw_clock, hw)
  66
  67struct mpfs_periph_clock {
  68        unsigned int id;
  69        u8 shift;
  70};
  71
  72struct mpfs_periph_hw_clock {
  73        struct mpfs_periph_clock periph;
  74        void __iomem *sys_base;
  75        struct clk_hw hw;
  76};
  77
  78#define to_mpfs_periph_clk(_hw) container_of(_hw, struct mpfs_periph_hw_clock, hw)
  79
  80/*
  81 * mpfs_clk_lock prevents anything else from writing to the
  82 * mpfs clk block while a software locked register is being written.
  83 */
  84static DEFINE_SPINLOCK(mpfs_clk_lock);
  85
  86static const struct clk_parent_data mpfs_ext_ref[] = {
  87        { .index = 0 },
  88};
  89
  90static const struct clk_div_table mpfs_div_cpu_axi_table[] = {
  91        { 0, 1 }, { 1, 2 }, { 2, 4 }, { 3, 8 },
  92        { 0, 0 }
  93};
  94
  95static const struct clk_div_table mpfs_div_ahb_table[] = {
  96        { 1, 2 }, { 2, 4}, { 3, 8 },
  97        { 0, 0 }
  98};
  99
 100/*
 101 * The only two supported reference clock frequencies for the PolarFire SoC are
 102 * 100 and 125 MHz, as the rtc reference is required to be 1 MHz.
 103 * It therefore only needs to have divider table entries corresponding to
 104 * divide by 100 and 125.
 105 */
 106static const struct clk_div_table mpfs_div_rtcref_table[] = {
 107        { 100, 100 }, { 125, 125 },
 108        { 0, 0 }
 109};
 110
 111static unsigned long mpfs_clk_msspll_recalc_rate(struct clk_hw *hw, unsigned long prate)
 112{
 113        struct mpfs_msspll_hw_clock *msspll_hw = to_mpfs_msspll_clk(hw);
 114        void __iomem *mult_addr = msspll_hw->base + msspll_hw->reg_offset;
 115        void __iomem *ref_div_addr = msspll_hw->base + REG_MSSPLL_REF_CR;
 116        void __iomem *postdiv_addr = msspll_hw->base + REG_MSSPLL_POSTDIV_CR;
 117        u32 mult, ref_div, postdiv;
 118
 119        mult = readl_relaxed(mult_addr) >> MSSPLL_FBDIV_SHIFT;
 120        mult &= clk_div_mask(MSSPLL_FBDIV_WIDTH);
 121        ref_div = readl_relaxed(ref_div_addr) >> MSSPLL_REFDIV_SHIFT;
 122        ref_div &= clk_div_mask(MSSPLL_REFDIV_WIDTH);
 123        postdiv = readl_relaxed(postdiv_addr) >> MSSPLL_POSTDIV_SHIFT;
 124        postdiv &= clk_div_mask(MSSPLL_POSTDIV_WIDTH);
 125
 126        return prate * mult / (ref_div * MSSPLL_FIXED_DIV * postdiv);
 127}
 128
 129static const struct clk_ops mpfs_clk_msspll_ops = {
 130        .recalc_rate = mpfs_clk_msspll_recalc_rate,
 131};
 132
 133#define CLK_PLL(_id, _name, _parent, _shift, _width, _flags, _offset) {                 \
 134        .id = _id,                                                                      \
 135        .shift = _shift,                                                                \
 136        .width = _width,                                                                \
 137        .reg_offset = _offset,                                                          \
 138        .flags = _flags,                                                                \
 139        .hw.init = CLK_HW_INIT_PARENTS_DATA(_name, _parent, &mpfs_clk_msspll_ops, 0),   \
 140}
 141
 142static struct mpfs_msspll_hw_clock mpfs_msspll_clks[] = {
 143        CLK_PLL(CLK_MSSPLL, "clk_msspll", mpfs_ext_ref, MSSPLL_FBDIV_SHIFT,
 144                MSSPLL_FBDIV_WIDTH, 0, REG_MSSPLL_SSCG_2_CR),
 145};
 146
 147static int mpfs_clk_register_msspll(struct device *dev, struct mpfs_msspll_hw_clock *msspll_hw,
 148                                    void __iomem *base)
 149{
 150        msspll_hw->base = base;
 151
 152        return devm_clk_hw_register(dev, &msspll_hw->hw);
 153}
 154
 155static int mpfs_clk_register_mssplls(struct device *dev, struct mpfs_msspll_hw_clock *msspll_hws,
 156                                     unsigned int num_clks, struct mpfs_clock_data *data)
 157{
 158        void __iomem *base = data->msspll_base;
 159        unsigned int i;
 160        int ret;
 161
 162        for (i = 0; i < num_clks; i++) {
 163                struct mpfs_msspll_hw_clock *msspll_hw = &msspll_hws[i];
 164
 165                ret = mpfs_clk_register_msspll(dev, msspll_hw, base);
 166                if (ret)
 167                        return dev_err_probe(dev, ret, "failed to register msspll id: %d\n",
 168                                             CLK_MSSPLL);
 169
 170                data->hw_data.hws[msspll_hw->id] = &msspll_hw->hw;
 171        }
 172
 173        return 0;
 174}
 175
 176/*
 177 * "CFG" clocks
 178 */
 179
 180static unsigned long mpfs_cfg_clk_recalc_rate(struct clk_hw *hw, unsigned long prate)
 181{
 182        struct mpfs_cfg_hw_clock *cfg_hw = to_mpfs_cfg_clk(hw);
 183        struct mpfs_cfg_clock *cfg = &cfg_hw->cfg;
 184        void __iomem *base_addr = cfg_hw->sys_base;
 185        u32 val;
 186
 187        val = readl_relaxed(base_addr + cfg->reg_offset) >> cfg->shift;
 188        val &= clk_div_mask(cfg->width);
 189
 190        return divider_recalc_rate(hw, prate, val, cfg->table, cfg->flags, cfg->width);
 191}
 192
 193static long mpfs_cfg_clk_round_rate(struct clk_hw *hw, unsigned long rate, unsigned long *prate)
 194{
 195        struct mpfs_cfg_hw_clock *cfg_hw = to_mpfs_cfg_clk(hw);
 196        struct mpfs_cfg_clock *cfg = &cfg_hw->cfg;
 197
 198        return divider_round_rate(hw, rate, prate, cfg->table, cfg->width, 0);
 199}
 200
 201static int mpfs_cfg_clk_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long prate)
 202{
 203        struct mpfs_cfg_hw_clock *cfg_hw = to_mpfs_cfg_clk(hw);
 204        struct mpfs_cfg_clock *cfg = &cfg_hw->cfg;
 205        void __iomem *base_addr = cfg_hw->sys_base;
 206        unsigned long flags;
 207        u32 val;
 208        int divider_setting;
 209
 210        divider_setting = divider_get_val(rate, prate, cfg->table, cfg->width, 0);
 211
 212        if (divider_setting < 0)
 213                return divider_setting;
 214
 215        spin_lock_irqsave(&mpfs_clk_lock, flags);
 216        val = readl_relaxed(base_addr + cfg->reg_offset);
 217        val &= ~(clk_div_mask(cfg->width) << cfg_hw->cfg.shift);
 218        val |= divider_setting << cfg->shift;
 219        writel_relaxed(val, base_addr + cfg->reg_offset);
 220
 221        spin_unlock_irqrestore(&mpfs_clk_lock, flags);
 222
 223        return 0;
 224}
 225
 226static const struct clk_ops mpfs_clk_cfg_ops = {
 227        .recalc_rate = mpfs_cfg_clk_recalc_rate,
 228        .round_rate = mpfs_cfg_clk_round_rate,
 229        .set_rate = mpfs_cfg_clk_set_rate,
 230};
 231
 232#define CLK_CFG(_id, _name, _parent, _shift, _width, _table, _flags, _offset) {         \
 233        .cfg.id = _id,                                                                  \
 234        .cfg.shift = _shift,                                                            \
 235        .cfg.width = _width,                                                            \
 236        .cfg.table = _table,                                                            \
 237        .cfg.reg_offset = _offset,                                                      \
 238        .cfg.flags = _flags,                                                            \
 239        .hw.init = CLK_HW_INIT(_name, _parent, &mpfs_clk_cfg_ops, 0),                   \
 240}
 241
 242static struct mpfs_cfg_hw_clock mpfs_cfg_clks[] = {
 243        CLK_CFG(CLK_CPU, "clk_cpu", "clk_msspll", 0, 2, mpfs_div_cpu_axi_table, 0,
 244                REG_CLOCK_CONFIG_CR),
 245        CLK_CFG(CLK_AXI, "clk_axi", "clk_msspll", 2, 2, mpfs_div_cpu_axi_table, 0,
 246                REG_CLOCK_CONFIG_CR),
 247        CLK_CFG(CLK_AHB, "clk_ahb", "clk_msspll", 4, 2, mpfs_div_ahb_table, 0,
 248                REG_CLOCK_CONFIG_CR),
 249        {
 250                .cfg.id = CLK_RTCREF,
 251                .cfg.shift = 0,
 252                .cfg.width = 12,
 253                .cfg.table = mpfs_div_rtcref_table,
 254                .cfg.reg_offset = REG_RTC_CLOCK_CR,
 255                .cfg.flags = CLK_DIVIDER_ONE_BASED,
 256                .hw.init =
 257                        CLK_HW_INIT_PARENTS_DATA("clk_rtcref", mpfs_ext_ref, &mpfs_clk_cfg_ops, 0),
 258        }
 259};
 260
 261static int mpfs_clk_register_cfg(struct device *dev, struct mpfs_cfg_hw_clock *cfg_hw,
 262                                 void __iomem *sys_base)
 263{
 264        cfg_hw->sys_base = sys_base;
 265
 266        return devm_clk_hw_register(dev, &cfg_hw->hw);
 267}
 268
 269static int mpfs_clk_register_cfgs(struct device *dev, struct mpfs_cfg_hw_clock *cfg_hws,
 270                                  unsigned int num_clks, struct mpfs_clock_data *data)
 271{
 272        void __iomem *sys_base = data->base;
 273        unsigned int i, id;
 274        int ret;
 275
 276        for (i = 0; i < num_clks; i++) {
 277                struct mpfs_cfg_hw_clock *cfg_hw = &cfg_hws[i];
 278
 279                ret = mpfs_clk_register_cfg(dev, cfg_hw, sys_base);
 280                if (ret)
 281                        return dev_err_probe(dev, ret, "failed to register clock id: %d\n",
 282                                             cfg_hw->cfg.id);
 283
 284                id = cfg_hw->cfg.id;
 285                data->hw_data.hws[id] = &cfg_hw->hw;
 286        }
 287
 288        return 0;
 289}
 290
 291/*
 292 * peripheral clocks - devices connected to axi or ahb buses.
 293 */
 294
 295static int mpfs_periph_clk_enable(struct clk_hw *hw)
 296{
 297        struct mpfs_periph_hw_clock *periph_hw = to_mpfs_periph_clk(hw);
 298        struct mpfs_periph_clock *periph = &periph_hw->periph;
 299        void __iomem *base_addr = periph_hw->sys_base;
 300        u32 reg, val;
 301        unsigned long flags;
 302
 303        spin_lock_irqsave(&mpfs_clk_lock, flags);
 304
 305        reg = readl_relaxed(base_addr + REG_SUBBLK_RESET_CR);
 306        val = reg & ~(1u << periph->shift);
 307        writel_relaxed(val, base_addr + REG_SUBBLK_RESET_CR);
 308
 309        reg = readl_relaxed(base_addr + REG_SUBBLK_CLOCK_CR);
 310        val = reg | (1u << periph->shift);
 311        writel_relaxed(val, base_addr + REG_SUBBLK_CLOCK_CR);
 312
 313        spin_unlock_irqrestore(&mpfs_clk_lock, flags);
 314
 315        return 0;
 316}
 317
 318static void mpfs_periph_clk_disable(struct clk_hw *hw)
 319{
 320        struct mpfs_periph_hw_clock *periph_hw = to_mpfs_periph_clk(hw);
 321        struct mpfs_periph_clock *periph = &periph_hw->periph;
 322        void __iomem *base_addr = periph_hw->sys_base;
 323        u32 reg, val;
 324        unsigned long flags;
 325
 326        spin_lock_irqsave(&mpfs_clk_lock, flags);
 327
 328        reg = readl_relaxed(base_addr + REG_SUBBLK_CLOCK_CR);
 329        val = reg & ~(1u << periph->shift);
 330        writel_relaxed(val, base_addr + REG_SUBBLK_CLOCK_CR);
 331
 332        spin_unlock_irqrestore(&mpfs_clk_lock, flags);
 333}
 334
 335static int mpfs_periph_clk_is_enabled(struct clk_hw *hw)
 336{
 337        struct mpfs_periph_hw_clock *periph_hw = to_mpfs_periph_clk(hw);
 338        struct mpfs_periph_clock *periph = &periph_hw->periph;
 339        void __iomem *base_addr = periph_hw->sys_base;
 340        u32 reg;
 341
 342        reg = readl_relaxed(base_addr + REG_SUBBLK_RESET_CR);
 343        if ((reg & (1u << periph->shift)) == 0u) {
 344                reg = readl_relaxed(base_addr + REG_SUBBLK_CLOCK_CR);
 345                if (reg & (1u << periph->shift))
 346                        return 1;
 347        }
 348
 349        return 0;
 350}
 351
 352static const struct clk_ops mpfs_periph_clk_ops = {
 353        .enable = mpfs_periph_clk_enable,
 354        .disable = mpfs_periph_clk_disable,
 355        .is_enabled = mpfs_periph_clk_is_enabled,
 356};
 357
 358#define CLK_PERIPH(_id, _name, _parent, _shift, _flags) {                       \
 359        .periph.id = _id,                                                       \
 360        .periph.shift = _shift,                                                 \
 361        .hw.init = CLK_HW_INIT_HW(_name, _parent, &mpfs_periph_clk_ops,         \
 362                                  _flags),                                      \
 363}
 364
 365#define PARENT_CLK(PARENT) (&mpfs_cfg_clks[CLK_##PARENT].hw)
 366
 367/*
 368 * Critical clocks:
 369 * - CLK_ENVM: reserved by hart software services (hss) superloop monitor/m mode interrupt
 370 *   trap handler
 371 * - CLK_MMUART0: reserved by the hss
 372 * - CLK_DDRC: provides clock to the ddr subsystem
 373 * - CLK_FICx: these provide the processor side clocks to the "FIC" (Fabric InterConnect)
 374 *   clock domain crossers which provide the interface to the FPGA fabric. Disabling them
 375 *   causes the FPGA fabric to go into reset.
 376 * - CLK_ATHENA: The athena clock is FIC4, which is reserved for the Athena TeraFire.
 377 */
 378
 379static struct mpfs_periph_hw_clock mpfs_periph_clks[] = {
 380        CLK_PERIPH(CLK_ENVM, "clk_periph_envm", PARENT_CLK(AHB), 0, CLK_IS_CRITICAL),
 381        CLK_PERIPH(CLK_MAC0, "clk_periph_mac0", PARENT_CLK(AHB), 1, 0),
 382        CLK_PERIPH(CLK_MAC1, "clk_periph_mac1", PARENT_CLK(AHB), 2, 0),
 383        CLK_PERIPH(CLK_MMC, "clk_periph_mmc", PARENT_CLK(AHB), 3, 0),
 384        CLK_PERIPH(CLK_TIMER, "clk_periph_timer", PARENT_CLK(RTCREF), 4, 0),
 385        CLK_PERIPH(CLK_MMUART0, "clk_periph_mmuart0", PARENT_CLK(AHB), 5, CLK_IS_CRITICAL),
 386        CLK_PERIPH(CLK_MMUART1, "clk_periph_mmuart1", PARENT_CLK(AHB), 6, 0),
 387        CLK_PERIPH(CLK_MMUART2, "clk_periph_mmuart2", PARENT_CLK(AHB), 7, 0),
 388        CLK_PERIPH(CLK_MMUART3, "clk_periph_mmuart3", PARENT_CLK(AHB), 8, 0),
 389        CLK_PERIPH(CLK_MMUART4, "clk_periph_mmuart4", PARENT_CLK(AHB), 9, 0),
 390        CLK_PERIPH(CLK_SPI0, "clk_periph_spi0", PARENT_CLK(AHB), 10, 0),
 391        CLK_PERIPH(CLK_SPI1, "clk_periph_spi1", PARENT_CLK(AHB), 11, 0),
 392        CLK_PERIPH(CLK_I2C0, "clk_periph_i2c0", PARENT_CLK(AHB), 12, 0),
 393        CLK_PERIPH(CLK_I2C1, "clk_periph_i2c1", PARENT_CLK(AHB), 13, 0),
 394        CLK_PERIPH(CLK_CAN0, "clk_periph_can0", PARENT_CLK(AHB), 14, 0),
 395        CLK_PERIPH(CLK_CAN1, "clk_periph_can1", PARENT_CLK(AHB), 15, 0),
 396        CLK_PERIPH(CLK_USB, "clk_periph_usb", PARENT_CLK(AHB), 16, 0),
 397        CLK_PERIPH(CLK_RTC, "clk_periph_rtc", PARENT_CLK(AHB), 18, 0),
 398        CLK_PERIPH(CLK_QSPI, "clk_periph_qspi", PARENT_CLK(AHB), 19, 0),
 399        CLK_PERIPH(CLK_GPIO0, "clk_periph_gpio0", PARENT_CLK(AHB), 20, 0),
 400        CLK_PERIPH(CLK_GPIO1, "clk_periph_gpio1", PARENT_CLK(AHB), 21, 0),
 401        CLK_PERIPH(CLK_GPIO2, "clk_periph_gpio2", PARENT_CLK(AHB), 22, 0),
 402        CLK_PERIPH(CLK_DDRC, "clk_periph_ddrc", PARENT_CLK(AHB), 23, CLK_IS_CRITICAL),
 403        CLK_PERIPH(CLK_FIC0, "clk_periph_fic0", PARENT_CLK(AXI), 24, CLK_IS_CRITICAL),
 404        CLK_PERIPH(CLK_FIC1, "clk_periph_fic1", PARENT_CLK(AXI), 25, CLK_IS_CRITICAL),
 405        CLK_PERIPH(CLK_FIC2, "clk_periph_fic2", PARENT_CLK(AXI), 26, CLK_IS_CRITICAL),
 406        CLK_PERIPH(CLK_FIC3, "clk_periph_fic3", PARENT_CLK(AXI), 27, CLK_IS_CRITICAL),
 407        CLK_PERIPH(CLK_ATHENA, "clk_periph_athena", PARENT_CLK(AXI), 28, CLK_IS_CRITICAL),
 408        CLK_PERIPH(CLK_CFM, "clk_periph_cfm", PARENT_CLK(AHB), 29, 0),
 409};
 410
 411static int mpfs_clk_register_periph(struct device *dev, struct mpfs_periph_hw_clock *periph_hw,
 412                                    void __iomem *sys_base)
 413{
 414        periph_hw->sys_base = sys_base;
 415
 416        return devm_clk_hw_register(dev, &periph_hw->hw);
 417}
 418
 419static int mpfs_clk_register_periphs(struct device *dev, struct mpfs_periph_hw_clock *periph_hws,
 420                                     int num_clks, struct mpfs_clock_data *data)
 421{
 422        void __iomem *sys_base = data->base;
 423        unsigned int i, id;
 424        int ret;
 425
 426        for (i = 0; i < num_clks; i++) {
 427                struct mpfs_periph_hw_clock *periph_hw = &periph_hws[i];
 428
 429                ret = mpfs_clk_register_periph(dev, periph_hw, sys_base);
 430                if (ret)
 431                        return dev_err_probe(dev, ret, "failed to register clock id: %d\n",
 432                                             periph_hw->periph.id);
 433
 434                id = periph_hws[i].periph.id;
 435                data->hw_data.hws[id] = &periph_hw->hw;
 436        }
 437
 438        return 0;
 439}
 440
 441static int mpfs_clk_probe(struct platform_device *pdev)
 442{
 443        struct device *dev = &pdev->dev;
 444        struct mpfs_clock_data *clk_data;
 445        unsigned int num_clks;
 446        int ret;
 447
 448        /* CLK_RESERVED is not part of clock arrays, so add 1 */
 449        num_clks = ARRAY_SIZE(mpfs_msspll_clks) + ARRAY_SIZE(mpfs_cfg_clks)
 450                   + ARRAY_SIZE(mpfs_periph_clks) + 1;
 451
 452        clk_data = devm_kzalloc(dev, struct_size(clk_data, hw_data.hws, num_clks), GFP_KERNEL);
 453        if (!clk_data)
 454                return -ENOMEM;
 455
 456        clk_data->base = devm_platform_ioremap_resource(pdev, 0);
 457        if (IS_ERR(clk_data->base))
 458                return PTR_ERR(clk_data->base);
 459
 460        clk_data->msspll_base = devm_platform_ioremap_resource(pdev, 1);
 461        if (IS_ERR(clk_data->msspll_base))
 462                return PTR_ERR(clk_data->msspll_base);
 463
 464        clk_data->hw_data.num = num_clks;
 465
 466        ret = mpfs_clk_register_mssplls(dev, mpfs_msspll_clks, ARRAY_SIZE(mpfs_msspll_clks),
 467                                        clk_data);
 468        if (ret)
 469                return ret;
 470
 471        ret = mpfs_clk_register_cfgs(dev, mpfs_cfg_clks, ARRAY_SIZE(mpfs_cfg_clks), clk_data);
 472        if (ret)
 473                return ret;
 474
 475        ret = mpfs_clk_register_periphs(dev, mpfs_periph_clks, ARRAY_SIZE(mpfs_periph_clks),
 476                                        clk_data);
 477        if (ret)
 478                return ret;
 479
 480        ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, &clk_data->hw_data);
 481        if (ret)
 482                return ret;
 483
 484        return ret;
 485}
 486
 487static const struct of_device_id mpfs_clk_of_match_table[] = {
 488        { .compatible = "microchip,mpfs-clkcfg", },
 489        {}
 490};
 491MODULE_DEVICE_TABLE(of, mpfs_clk_match_table);
 492
 493static struct platform_driver mpfs_clk_driver = {
 494        .probe = mpfs_clk_probe,
 495        .driver = {
 496                .name = "microchip-mpfs-clkcfg",
 497                .of_match_table = mpfs_clk_of_match_table,
 498        },
 499};
 500
 501static int __init clk_mpfs_init(void)
 502{
 503        return platform_driver_register(&mpfs_clk_driver);
 504}
 505core_initcall(clk_mpfs_init);
 506
 507static void __exit clk_mpfs_exit(void)
 508{
 509        platform_driver_unregister(&mpfs_clk_driver);
 510}
 511module_exit(clk_mpfs_exit);
 512
 513MODULE_DESCRIPTION("Microchip PolarFire SoC Clock Driver");
 514MODULE_LICENSE("GPL v2");
 515