1635e5e73SDaire McNamara // SPDX-License-Identifier: GPL-2.0-only 2635e5e73SDaire McNamara /* 3635e5e73SDaire McNamara * Daire McNamara,<daire.mcnamara@microchip.com> 4635e5e73SDaire McNamara * Copyright (C) 2020 Microchip Technology Inc. All rights reserved. 5635e5e73SDaire McNamara */ 6635e5e73SDaire McNamara #include <linux/clk-provider.h> 7635e5e73SDaire McNamara #include <linux/io.h> 8635e5e73SDaire McNamara #include <linux/module.h> 9635e5e73SDaire McNamara #include <linux/platform_device.h> 10635e5e73SDaire McNamara #include <linux/slab.h> 11635e5e73SDaire McNamara #include <dt-bindings/clock/microchip,mpfs-clock.h> 12635e5e73SDaire McNamara 13635e5e73SDaire McNamara /* address offset of control registers */ 14635e5e73SDaire McNamara #define REG_CLOCK_CONFIG_CR 0x08u 15635e5e73SDaire McNamara #define REG_SUBBLK_CLOCK_CR 0x84u 16635e5e73SDaire McNamara #define REG_SUBBLK_RESET_CR 0x88u 17635e5e73SDaire McNamara 18635e5e73SDaire McNamara struct mpfs_clock_data { 19635e5e73SDaire McNamara void __iomem *base; 20635e5e73SDaire McNamara struct clk_hw_onecell_data hw_data; 21635e5e73SDaire McNamara }; 22635e5e73SDaire McNamara 23635e5e73SDaire McNamara struct mpfs_cfg_clock { 24635e5e73SDaire McNamara const struct clk_div_table *table; 25635e5e73SDaire McNamara unsigned int id; 26635e5e73SDaire McNamara u8 shift; 27635e5e73SDaire McNamara u8 width; 28635e5e73SDaire McNamara }; 29635e5e73SDaire McNamara 30635e5e73SDaire McNamara struct mpfs_cfg_hw_clock { 31635e5e73SDaire McNamara struct mpfs_cfg_clock cfg; 32635e5e73SDaire McNamara void __iomem *sys_base; 33635e5e73SDaire McNamara struct clk_hw hw; 34635e5e73SDaire McNamara struct clk_init_data init; 35635e5e73SDaire McNamara }; 36635e5e73SDaire McNamara 37635e5e73SDaire McNamara #define to_mpfs_cfg_clk(_hw) container_of(_hw, struct mpfs_cfg_hw_clock, hw) 38635e5e73SDaire McNamara 39635e5e73SDaire McNamara struct mpfs_periph_clock { 40635e5e73SDaire McNamara unsigned int id; 41635e5e73SDaire McNamara u8 shift; 42635e5e73SDaire McNamara }; 43635e5e73SDaire McNamara 44635e5e73SDaire McNamara struct mpfs_periph_hw_clock { 45635e5e73SDaire McNamara struct mpfs_periph_clock periph; 46635e5e73SDaire McNamara void __iomem *sys_base; 47635e5e73SDaire McNamara struct clk_hw hw; 48635e5e73SDaire McNamara }; 49635e5e73SDaire McNamara 50635e5e73SDaire McNamara #define to_mpfs_periph_clk(_hw) container_of(_hw, struct mpfs_periph_hw_clock, hw) 51635e5e73SDaire McNamara 52635e5e73SDaire McNamara /* 53635e5e73SDaire McNamara * mpfs_clk_lock prevents anything else from writing to the 54635e5e73SDaire McNamara * mpfs clk block while a software locked register is being written. 55635e5e73SDaire McNamara */ 56635e5e73SDaire McNamara static DEFINE_SPINLOCK(mpfs_clk_lock); 57635e5e73SDaire McNamara 58635e5e73SDaire McNamara static const struct clk_parent_data mpfs_cfg_parent[] = { 59635e5e73SDaire McNamara { .index = 0 }, 60635e5e73SDaire McNamara }; 61635e5e73SDaire McNamara 62635e5e73SDaire McNamara static const struct clk_div_table mpfs_div_cpu_axi_table[] = { 63635e5e73SDaire McNamara { 0, 1 }, { 1, 2 }, { 2, 4 }, { 3, 8 }, 64635e5e73SDaire McNamara { 0, 0 } 65635e5e73SDaire McNamara }; 66635e5e73SDaire McNamara 67635e5e73SDaire McNamara static const struct clk_div_table mpfs_div_ahb_table[] = { 68635e5e73SDaire McNamara { 1, 2 }, { 2, 4}, { 3, 8 }, 69635e5e73SDaire McNamara { 0, 0 } 70635e5e73SDaire McNamara }; 71635e5e73SDaire McNamara 72635e5e73SDaire McNamara static unsigned long mpfs_cfg_clk_recalc_rate(struct clk_hw *hw, unsigned long prate) 73635e5e73SDaire McNamara { 74635e5e73SDaire McNamara struct mpfs_cfg_hw_clock *cfg_hw = to_mpfs_cfg_clk(hw); 75635e5e73SDaire McNamara struct mpfs_cfg_clock *cfg = &cfg_hw->cfg; 76635e5e73SDaire McNamara void __iomem *base_addr = cfg_hw->sys_base; 77635e5e73SDaire McNamara u32 val; 78635e5e73SDaire McNamara 79635e5e73SDaire McNamara val = readl_relaxed(base_addr + REG_CLOCK_CONFIG_CR) >> cfg->shift; 80635e5e73SDaire McNamara val &= clk_div_mask(cfg->width); 81635e5e73SDaire McNamara 82635e5e73SDaire McNamara return prate / (1u << val); 83635e5e73SDaire McNamara } 84635e5e73SDaire McNamara 85635e5e73SDaire McNamara static long mpfs_cfg_clk_round_rate(struct clk_hw *hw, unsigned long rate, unsigned long *prate) 86635e5e73SDaire McNamara { 87635e5e73SDaire McNamara struct mpfs_cfg_hw_clock *cfg_hw = to_mpfs_cfg_clk(hw); 88635e5e73SDaire McNamara struct mpfs_cfg_clock *cfg = &cfg_hw->cfg; 89635e5e73SDaire McNamara 90635e5e73SDaire McNamara return divider_round_rate(hw, rate, prate, cfg->table, cfg->width, 0); 91635e5e73SDaire McNamara } 92635e5e73SDaire McNamara 93635e5e73SDaire McNamara static int mpfs_cfg_clk_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long prate) 94635e5e73SDaire McNamara { 95635e5e73SDaire McNamara struct mpfs_cfg_hw_clock *cfg_hw = to_mpfs_cfg_clk(hw); 96635e5e73SDaire McNamara struct mpfs_cfg_clock *cfg = &cfg_hw->cfg; 97635e5e73SDaire McNamara void __iomem *base_addr = cfg_hw->sys_base; 98635e5e73SDaire McNamara unsigned long flags; 99635e5e73SDaire McNamara u32 val; 100635e5e73SDaire McNamara int divider_setting; 101635e5e73SDaire McNamara 102635e5e73SDaire McNamara divider_setting = divider_get_val(rate, prate, cfg->table, cfg->width, 0); 103635e5e73SDaire McNamara 104635e5e73SDaire McNamara if (divider_setting < 0) 105635e5e73SDaire McNamara return divider_setting; 106635e5e73SDaire McNamara 107635e5e73SDaire McNamara spin_lock_irqsave(&mpfs_clk_lock, flags); 108635e5e73SDaire McNamara 109635e5e73SDaire McNamara val = readl_relaxed(base_addr + REG_CLOCK_CONFIG_CR); 110635e5e73SDaire McNamara val &= ~(clk_div_mask(cfg->width) << cfg_hw->cfg.shift); 111635e5e73SDaire McNamara val |= divider_setting << cfg->shift; 112635e5e73SDaire McNamara writel_relaxed(val, base_addr + REG_CLOCK_CONFIG_CR); 113635e5e73SDaire McNamara 114635e5e73SDaire McNamara spin_unlock_irqrestore(&mpfs_clk_lock, flags); 115635e5e73SDaire McNamara 116635e5e73SDaire McNamara return 0; 117635e5e73SDaire McNamara } 118635e5e73SDaire McNamara 119635e5e73SDaire McNamara static const struct clk_ops mpfs_clk_cfg_ops = { 120635e5e73SDaire McNamara .recalc_rate = mpfs_cfg_clk_recalc_rate, 121635e5e73SDaire McNamara .round_rate = mpfs_cfg_clk_round_rate, 122635e5e73SDaire McNamara .set_rate = mpfs_cfg_clk_set_rate, 123635e5e73SDaire McNamara }; 124635e5e73SDaire McNamara 125635e5e73SDaire McNamara #define CLK_CFG(_id, _name, _parent, _shift, _width, _table, _flags) { \ 126635e5e73SDaire McNamara .cfg.id = _id, \ 127635e5e73SDaire McNamara .cfg.shift = _shift, \ 128635e5e73SDaire McNamara .cfg.width = _width, \ 129635e5e73SDaire McNamara .cfg.table = _table, \ 130635e5e73SDaire McNamara .hw.init = CLK_HW_INIT_PARENTS_DATA(_name, _parent, &mpfs_clk_cfg_ops, \ 131635e5e73SDaire McNamara _flags), \ 132635e5e73SDaire McNamara } 133635e5e73SDaire McNamara 134635e5e73SDaire McNamara static struct mpfs_cfg_hw_clock mpfs_cfg_clks[] = { 135635e5e73SDaire McNamara CLK_CFG(CLK_CPU, "clk_cpu", mpfs_cfg_parent, 0, 2, mpfs_div_cpu_axi_table, 0), 136635e5e73SDaire McNamara CLK_CFG(CLK_AXI, "clk_axi", mpfs_cfg_parent, 2, 2, mpfs_div_cpu_axi_table, 0), 137635e5e73SDaire McNamara CLK_CFG(CLK_AHB, "clk_ahb", mpfs_cfg_parent, 4, 2, mpfs_div_ahb_table, 0), 138635e5e73SDaire McNamara }; 139635e5e73SDaire McNamara 140635e5e73SDaire McNamara static int mpfs_clk_register_cfg(struct device *dev, struct mpfs_cfg_hw_clock *cfg_hw, 141635e5e73SDaire McNamara void __iomem *sys_base) 142635e5e73SDaire McNamara { 143635e5e73SDaire McNamara cfg_hw->sys_base = sys_base; 144635e5e73SDaire McNamara 145635e5e73SDaire McNamara return devm_clk_hw_register(dev, &cfg_hw->hw); 146635e5e73SDaire McNamara } 147635e5e73SDaire McNamara 148635e5e73SDaire McNamara static int mpfs_clk_register_cfgs(struct device *dev, struct mpfs_cfg_hw_clock *cfg_hws, 149635e5e73SDaire McNamara unsigned int num_clks, struct mpfs_clock_data *data) 150635e5e73SDaire McNamara { 151635e5e73SDaire McNamara void __iomem *sys_base = data->base; 152635e5e73SDaire McNamara unsigned int i, id; 153635e5e73SDaire McNamara int ret; 154635e5e73SDaire McNamara 155635e5e73SDaire McNamara for (i = 0; i < num_clks; i++) { 156635e5e73SDaire McNamara struct mpfs_cfg_hw_clock *cfg_hw = &cfg_hws[i]; 157635e5e73SDaire McNamara 158635e5e73SDaire McNamara ret = mpfs_clk_register_cfg(dev, cfg_hw, sys_base); 159635e5e73SDaire McNamara if (ret) 160635e5e73SDaire McNamara return dev_err_probe(dev, ret, "failed to register clock id: %d\n", 161635e5e73SDaire McNamara cfg_hw->cfg.id); 162635e5e73SDaire McNamara 163635e5e73SDaire McNamara id = cfg_hws[i].cfg.id; 164635e5e73SDaire McNamara data->hw_data.hws[id] = &cfg_hw->hw; 165635e5e73SDaire McNamara } 166635e5e73SDaire McNamara 167635e5e73SDaire McNamara return 0; 168635e5e73SDaire McNamara } 169635e5e73SDaire McNamara 170635e5e73SDaire McNamara static int mpfs_periph_clk_enable(struct clk_hw *hw) 171635e5e73SDaire McNamara { 172635e5e73SDaire McNamara struct mpfs_periph_hw_clock *periph_hw = to_mpfs_periph_clk(hw); 173635e5e73SDaire McNamara struct mpfs_periph_clock *periph = &periph_hw->periph; 174635e5e73SDaire McNamara void __iomem *base_addr = periph_hw->sys_base; 175635e5e73SDaire McNamara u32 reg, val; 176635e5e73SDaire McNamara unsigned long flags; 177635e5e73SDaire McNamara 178635e5e73SDaire McNamara spin_lock_irqsave(&mpfs_clk_lock, flags); 179635e5e73SDaire McNamara 180635e5e73SDaire McNamara reg = readl_relaxed(base_addr + REG_SUBBLK_RESET_CR); 181635e5e73SDaire McNamara val = reg & ~(1u << periph->shift); 182635e5e73SDaire McNamara writel_relaxed(val, base_addr + REG_SUBBLK_RESET_CR); 183635e5e73SDaire McNamara 184635e5e73SDaire McNamara reg = readl_relaxed(base_addr + REG_SUBBLK_CLOCK_CR); 185635e5e73SDaire McNamara val = reg | (1u << periph->shift); 186635e5e73SDaire McNamara writel_relaxed(val, base_addr + REG_SUBBLK_CLOCK_CR); 187635e5e73SDaire McNamara 188635e5e73SDaire McNamara spin_unlock_irqrestore(&mpfs_clk_lock, flags); 189635e5e73SDaire McNamara 190635e5e73SDaire McNamara return 0; 191635e5e73SDaire McNamara } 192635e5e73SDaire McNamara 193635e5e73SDaire McNamara static void mpfs_periph_clk_disable(struct clk_hw *hw) 194635e5e73SDaire McNamara { 195635e5e73SDaire McNamara struct mpfs_periph_hw_clock *periph_hw = to_mpfs_periph_clk(hw); 196635e5e73SDaire McNamara struct mpfs_periph_clock *periph = &periph_hw->periph; 197635e5e73SDaire McNamara void __iomem *base_addr = periph_hw->sys_base; 198635e5e73SDaire McNamara u32 reg, val; 199635e5e73SDaire McNamara unsigned long flags; 200635e5e73SDaire McNamara 201635e5e73SDaire McNamara spin_lock_irqsave(&mpfs_clk_lock, flags); 202635e5e73SDaire McNamara 203635e5e73SDaire McNamara reg = readl_relaxed(base_addr + REG_SUBBLK_CLOCK_CR); 204635e5e73SDaire McNamara val = reg & ~(1u << periph->shift); 205635e5e73SDaire McNamara writel_relaxed(val, base_addr + REG_SUBBLK_CLOCK_CR); 206635e5e73SDaire McNamara 207635e5e73SDaire McNamara spin_unlock_irqrestore(&mpfs_clk_lock, flags); 208635e5e73SDaire McNamara } 209635e5e73SDaire McNamara 210635e5e73SDaire McNamara static int mpfs_periph_clk_is_enabled(struct clk_hw *hw) 211635e5e73SDaire McNamara { 212635e5e73SDaire McNamara struct mpfs_periph_hw_clock *periph_hw = to_mpfs_periph_clk(hw); 213635e5e73SDaire McNamara struct mpfs_periph_clock *periph = &periph_hw->periph; 214635e5e73SDaire McNamara void __iomem *base_addr = periph_hw->sys_base; 215635e5e73SDaire McNamara u32 reg; 216635e5e73SDaire McNamara 217635e5e73SDaire McNamara reg = readl_relaxed(base_addr + REG_SUBBLK_RESET_CR); 218635e5e73SDaire McNamara if ((reg & (1u << periph->shift)) == 0u) { 219635e5e73SDaire McNamara reg = readl_relaxed(base_addr + REG_SUBBLK_CLOCK_CR); 220635e5e73SDaire McNamara if (reg & (1u << periph->shift)) 221635e5e73SDaire McNamara return 1; 222635e5e73SDaire McNamara } 223635e5e73SDaire McNamara 224635e5e73SDaire McNamara return 0; 225635e5e73SDaire McNamara } 226635e5e73SDaire McNamara 227635e5e73SDaire McNamara static const struct clk_ops mpfs_periph_clk_ops = { 228635e5e73SDaire McNamara .enable = mpfs_periph_clk_enable, 229635e5e73SDaire McNamara .disable = mpfs_periph_clk_disable, 230635e5e73SDaire McNamara .is_enabled = mpfs_periph_clk_is_enabled, 231635e5e73SDaire McNamara }; 232635e5e73SDaire McNamara 233635e5e73SDaire McNamara #define CLK_PERIPH(_id, _name, _parent, _shift, _flags) { \ 234635e5e73SDaire McNamara .periph.id = _id, \ 235635e5e73SDaire McNamara .periph.shift = _shift, \ 236635e5e73SDaire McNamara .hw.init = CLK_HW_INIT_HW(_name, _parent, &mpfs_periph_clk_ops, \ 237635e5e73SDaire McNamara _flags), \ 238635e5e73SDaire McNamara } 239635e5e73SDaire McNamara 240635e5e73SDaire McNamara #define PARENT_CLK(PARENT) (&mpfs_cfg_clks[CLK_##PARENT].hw) 241635e5e73SDaire McNamara 242635e5e73SDaire McNamara /* 243635e5e73SDaire McNamara * Critical clocks: 244635e5e73SDaire McNamara * - CLK_ENVM: reserved by hart software services (hss) superloop monitor/m mode interrupt 245635e5e73SDaire McNamara * trap handler 246635e5e73SDaire McNamara * - CLK_MMUART0: reserved by the hss 247635e5e73SDaire McNamara * - CLK_DDRC: provides clock to the ddr subsystem 248635e5e73SDaire McNamara * - CLK_FICx: these provide clocks for sections of the fpga fabric, disabling them would 249635e5e73SDaire McNamara * cause the fabric to go into reset 250635e5e73SDaire McNamara */ 251635e5e73SDaire McNamara 252635e5e73SDaire McNamara static struct mpfs_periph_hw_clock mpfs_periph_clks[] = { 253635e5e73SDaire McNamara CLK_PERIPH(CLK_ENVM, "clk_periph_envm", PARENT_CLK(AHB), 0, CLK_IS_CRITICAL), 254635e5e73SDaire McNamara CLK_PERIPH(CLK_MAC0, "clk_periph_mac0", PARENT_CLK(AHB), 1, 0), 255635e5e73SDaire McNamara CLK_PERIPH(CLK_MAC1, "clk_periph_mac1", PARENT_CLK(AHB), 2, 0), 256635e5e73SDaire McNamara CLK_PERIPH(CLK_MMC, "clk_periph_mmc", PARENT_CLK(AHB), 3, 0), 257635e5e73SDaire McNamara CLK_PERIPH(CLK_TIMER, "clk_periph_timer", PARENT_CLK(AHB), 4, 0), 258635e5e73SDaire McNamara CLK_PERIPH(CLK_MMUART0, "clk_periph_mmuart0", PARENT_CLK(AHB), 5, CLK_IS_CRITICAL), 259635e5e73SDaire McNamara CLK_PERIPH(CLK_MMUART1, "clk_periph_mmuart1", PARENT_CLK(AHB), 6, 0), 260635e5e73SDaire McNamara CLK_PERIPH(CLK_MMUART2, "clk_periph_mmuart2", PARENT_CLK(AHB), 7, 0), 261635e5e73SDaire McNamara CLK_PERIPH(CLK_MMUART3, "clk_periph_mmuart3", PARENT_CLK(AHB), 8, 0), 262635e5e73SDaire McNamara CLK_PERIPH(CLK_MMUART4, "clk_periph_mmuart4", PARENT_CLK(AHB), 9, 0), 263635e5e73SDaire McNamara CLK_PERIPH(CLK_SPI0, "clk_periph_spi0", PARENT_CLK(AHB), 10, 0), 264635e5e73SDaire McNamara CLK_PERIPH(CLK_SPI1, "clk_periph_spi1", PARENT_CLK(AHB), 11, 0), 265635e5e73SDaire McNamara CLK_PERIPH(CLK_I2C0, "clk_periph_i2c0", PARENT_CLK(AHB), 12, 0), 266635e5e73SDaire McNamara CLK_PERIPH(CLK_I2C1, "clk_periph_i2c1", PARENT_CLK(AHB), 13, 0), 267635e5e73SDaire McNamara CLK_PERIPH(CLK_CAN0, "clk_periph_can0", PARENT_CLK(AHB), 14, 0), 268635e5e73SDaire McNamara CLK_PERIPH(CLK_CAN1, "clk_periph_can1", PARENT_CLK(AHB), 15, 0), 269635e5e73SDaire McNamara CLK_PERIPH(CLK_USB, "clk_periph_usb", PARENT_CLK(AHB), 16, 0), 270635e5e73SDaire McNamara CLK_PERIPH(CLK_RTC, "clk_periph_rtc", PARENT_CLK(AHB), 18, 0), 271635e5e73SDaire McNamara CLK_PERIPH(CLK_QSPI, "clk_periph_qspi", PARENT_CLK(AHB), 19, 0), 272635e5e73SDaire McNamara CLK_PERIPH(CLK_GPIO0, "clk_periph_gpio0", PARENT_CLK(AHB), 20, 0), 273635e5e73SDaire McNamara CLK_PERIPH(CLK_GPIO1, "clk_periph_gpio1", PARENT_CLK(AHB), 21, 0), 274635e5e73SDaire McNamara CLK_PERIPH(CLK_GPIO2, "clk_periph_gpio2", PARENT_CLK(AHB), 22, 0), 275635e5e73SDaire McNamara CLK_PERIPH(CLK_DDRC, "clk_periph_ddrc", PARENT_CLK(AHB), 23, CLK_IS_CRITICAL), 276*8f9fb2abSConor Dooley CLK_PERIPH(CLK_FIC0, "clk_periph_fic0", PARENT_CLK(AXI), 24, CLK_IS_CRITICAL), 277*8f9fb2abSConor Dooley CLK_PERIPH(CLK_FIC1, "clk_periph_fic1", PARENT_CLK(AXI), 25, CLK_IS_CRITICAL), 278*8f9fb2abSConor Dooley CLK_PERIPH(CLK_FIC2, "clk_periph_fic2", PARENT_CLK(AXI), 26, CLK_IS_CRITICAL), 279*8f9fb2abSConor Dooley CLK_PERIPH(CLK_FIC3, "clk_periph_fic3", PARENT_CLK(AXI), 27, CLK_IS_CRITICAL), 280*8f9fb2abSConor Dooley CLK_PERIPH(CLK_ATHENA, "clk_periph_athena", PARENT_CLK(AXI), 28, 0), 281635e5e73SDaire McNamara CLK_PERIPH(CLK_CFM, "clk_periph_cfm", PARENT_CLK(AHB), 29, 0), 282635e5e73SDaire McNamara }; 283635e5e73SDaire McNamara 284635e5e73SDaire McNamara static int mpfs_clk_register_periph(struct device *dev, struct mpfs_periph_hw_clock *periph_hw, 285635e5e73SDaire McNamara void __iomem *sys_base) 286635e5e73SDaire McNamara { 287635e5e73SDaire McNamara periph_hw->sys_base = sys_base; 288635e5e73SDaire McNamara 289635e5e73SDaire McNamara return devm_clk_hw_register(dev, &periph_hw->hw); 290635e5e73SDaire McNamara } 291635e5e73SDaire McNamara 292635e5e73SDaire McNamara static int mpfs_clk_register_periphs(struct device *dev, struct mpfs_periph_hw_clock *periph_hws, 293635e5e73SDaire McNamara int num_clks, struct mpfs_clock_data *data) 294635e5e73SDaire McNamara { 295635e5e73SDaire McNamara void __iomem *sys_base = data->base; 296635e5e73SDaire McNamara unsigned int i, id; 297635e5e73SDaire McNamara int ret; 298635e5e73SDaire McNamara 299635e5e73SDaire McNamara for (i = 0; i < num_clks; i++) { 300635e5e73SDaire McNamara struct mpfs_periph_hw_clock *periph_hw = &periph_hws[i]; 301635e5e73SDaire McNamara 302635e5e73SDaire McNamara ret = mpfs_clk_register_periph(dev, periph_hw, sys_base); 303635e5e73SDaire McNamara if (ret) 304635e5e73SDaire McNamara return dev_err_probe(dev, ret, "failed to register clock id: %d\n", 305635e5e73SDaire McNamara periph_hw->periph.id); 306635e5e73SDaire McNamara 307635e5e73SDaire McNamara id = periph_hws[i].periph.id; 308635e5e73SDaire McNamara data->hw_data.hws[id] = &periph_hw->hw; 309635e5e73SDaire McNamara } 310635e5e73SDaire McNamara 311635e5e73SDaire McNamara return 0; 312635e5e73SDaire McNamara } 313635e5e73SDaire McNamara 314635e5e73SDaire McNamara static int mpfs_clk_probe(struct platform_device *pdev) 315635e5e73SDaire McNamara { 316635e5e73SDaire McNamara struct device *dev = &pdev->dev; 317635e5e73SDaire McNamara struct mpfs_clock_data *clk_data; 318635e5e73SDaire McNamara unsigned int num_clks; 319635e5e73SDaire McNamara int ret; 320635e5e73SDaire McNamara 321635e5e73SDaire McNamara /* CLK_RESERVED is not part of cfg_clks nor periph_clks, so add 1 */ 322635e5e73SDaire McNamara num_clks = ARRAY_SIZE(mpfs_cfg_clks) + ARRAY_SIZE(mpfs_periph_clks) + 1; 323635e5e73SDaire McNamara 324635e5e73SDaire McNamara clk_data = devm_kzalloc(dev, struct_size(clk_data, hw_data.hws, num_clks), GFP_KERNEL); 325635e5e73SDaire McNamara if (!clk_data) 326635e5e73SDaire McNamara return -ENOMEM; 327635e5e73SDaire McNamara 328635e5e73SDaire McNamara clk_data->base = devm_platform_ioremap_resource(pdev, 0); 329635e5e73SDaire McNamara if (IS_ERR(clk_data->base)) 330635e5e73SDaire McNamara return PTR_ERR(clk_data->base); 331635e5e73SDaire McNamara 332635e5e73SDaire McNamara clk_data->hw_data.num = num_clks; 333635e5e73SDaire McNamara 334635e5e73SDaire McNamara ret = mpfs_clk_register_cfgs(dev, mpfs_cfg_clks, ARRAY_SIZE(mpfs_cfg_clks), clk_data); 335635e5e73SDaire McNamara if (ret) 336635e5e73SDaire McNamara return ret; 337635e5e73SDaire McNamara 338635e5e73SDaire McNamara ret = mpfs_clk_register_periphs(dev, mpfs_periph_clks, ARRAY_SIZE(mpfs_periph_clks), 339635e5e73SDaire McNamara clk_data); 340635e5e73SDaire McNamara if (ret) 341635e5e73SDaire McNamara return ret; 342635e5e73SDaire McNamara 343635e5e73SDaire McNamara ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, &clk_data->hw_data); 344635e5e73SDaire McNamara if (ret) 345635e5e73SDaire McNamara return ret; 346635e5e73SDaire McNamara 347635e5e73SDaire McNamara return ret; 348635e5e73SDaire McNamara } 349635e5e73SDaire McNamara 350635e5e73SDaire McNamara static const struct of_device_id mpfs_clk_of_match_table[] = { 351635e5e73SDaire McNamara { .compatible = "microchip,mpfs-clkcfg", }, 352635e5e73SDaire McNamara {} 353635e5e73SDaire McNamara }; 354635e5e73SDaire McNamara MODULE_DEVICE_TABLE(of, mpfs_clk_match_table); 355635e5e73SDaire McNamara 356635e5e73SDaire McNamara static struct platform_driver mpfs_clk_driver = { 357635e5e73SDaire McNamara .probe = mpfs_clk_probe, 358635e5e73SDaire McNamara .driver = { 359635e5e73SDaire McNamara .name = "microchip-mpfs-clkcfg", 360635e5e73SDaire McNamara .of_match_table = mpfs_clk_of_match_table, 361635e5e73SDaire McNamara }, 362635e5e73SDaire McNamara }; 363635e5e73SDaire McNamara 364635e5e73SDaire McNamara static int __init clk_mpfs_init(void) 365635e5e73SDaire McNamara { 366635e5e73SDaire McNamara return platform_driver_register(&mpfs_clk_driver); 367635e5e73SDaire McNamara } 368635e5e73SDaire McNamara core_initcall(clk_mpfs_init); 369635e5e73SDaire McNamara 370635e5e73SDaire McNamara static void __exit clk_mpfs_exit(void) 371635e5e73SDaire McNamara { 372635e5e73SDaire McNamara platform_driver_unregister(&mpfs_clk_driver); 373635e5e73SDaire McNamara } 374635e5e73SDaire McNamara module_exit(clk_mpfs_exit); 375635e5e73SDaire McNamara 376635e5e73SDaire McNamara MODULE_DESCRIPTION("Microchip PolarFire SoC Clock Driver"); 377635e5e73SDaire McNamara MODULE_LICENSE("GPL v2"); 378