447 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * PolarFire SoC MSS/core complex clock control4 *5 * Copyright (C) 2020-2022 Microchip Technology Inc. All rights reserved.6 */7#include <linux/clk-provider.h>8#include <linux/io.h>9#include <linux/module.h>10#include <linux/platform_device.h>11#include <dt-bindings/clock/microchip,mpfs-clock.h>12#include <soc/microchip/mpfs.h>13 14/* address offset of control registers */15#define REG_MSSPLL_REF_CR 0x08u16#define REG_MSSPLL_POSTDIV01_CR 0x10u17#define REG_MSSPLL_POSTDIV23_CR 0x14u18#define REG_MSSPLL_SSCG_2_CR 0x2Cu19#define REG_CLOCK_CONFIG_CR 0x08u20#define REG_RTC_CLOCK_CR 0x0Cu21#define REG_SUBBLK_CLOCK_CR 0x84u22#define REG_SUBBLK_RESET_CR 0x88u23 24#define MSSPLL_FBDIV_SHIFT 0x00u25#define MSSPLL_FBDIV_WIDTH 0x0Cu26#define MSSPLL_REFDIV_SHIFT 0x08u27#define MSSPLL_REFDIV_WIDTH 0x06u28#define MSSPLL_POSTDIV02_SHIFT 0x08u29#define MSSPLL_POSTDIV13_SHIFT 0x18u30#define MSSPLL_POSTDIV_WIDTH 0x07u31#define MSSPLL_FIXED_DIV 4u32 33/*34 * This clock ID is defined here, rather than the binding headers, as it is an35 * internal clock only, and therefore has no consumers in other peripheral36 * blocks.37 */38#define CLK_MSSPLL_INTERNAL 38u39 40struct mpfs_clock_data {41 struct device *dev;42 void __iomem *base;43 void __iomem *msspll_base;44 struct clk_hw_onecell_data hw_data;45};46 47struct mpfs_msspll_hw_clock {48 void __iomem *base;49 struct clk_hw hw;50 struct clk_init_data init;51 unsigned int id;52 u32 reg_offset;53 u32 shift;54 u32 width;55 u32 flags;56};57 58#define to_mpfs_msspll_clk(_hw) container_of(_hw, struct mpfs_msspll_hw_clock, hw)59 60struct mpfs_msspll_out_hw_clock {61 void __iomem *base;62 struct clk_divider output;63 struct clk_init_data init;64 unsigned int id;65 u32 reg_offset;66};67 68#define to_mpfs_msspll_out_clk(_hw) container_of(_hw, struct mpfs_msspll_out_hw_clock, hw)69 70struct mpfs_cfg_hw_clock {71 struct clk_divider cfg;72 struct clk_init_data init;73 unsigned int id;74 u32 reg_offset;75};76 77struct mpfs_periph_hw_clock {78 struct clk_gate periph;79 unsigned int id;80};81 82/*83 * mpfs_clk_lock prevents anything else from writing to the84 * mpfs clk block while a software locked register is being written.85 */86static DEFINE_SPINLOCK(mpfs_clk_lock);87 88static const struct clk_parent_data mpfs_ext_ref[] = {89 { .index = 0 },90};91 92static const struct clk_div_table mpfs_div_cpu_axi_table[] = {93 { 0, 1 }, { 1, 2 }, { 2, 4 }, { 3, 8 },94 { 0, 0 }95};96 97static const struct clk_div_table mpfs_div_ahb_table[] = {98 { 1, 2 }, { 2, 4}, { 3, 8 },99 { 0, 0 }100};101 102/*103 * The only two supported reference clock frequencies for the PolarFire SoC are104 * 100 and 125 MHz, as the rtc reference is required to be 1 MHz.105 * It therefore only needs to have divider table entries corresponding to106 * divide by 100 and 125.107 */108static const struct clk_div_table mpfs_div_rtcref_table[] = {109 { 100, 100 }, { 125, 125 },110 { 0, 0 }111};112 113/*114 * MSS PLL internal clock115 */116 117static unsigned long mpfs_clk_msspll_recalc_rate(struct clk_hw *hw, unsigned long prate)118{119 struct mpfs_msspll_hw_clock *msspll_hw = to_mpfs_msspll_clk(hw);120 void __iomem *mult_addr = msspll_hw->base + msspll_hw->reg_offset;121 void __iomem *ref_div_addr = msspll_hw->base + REG_MSSPLL_REF_CR;122 u32 mult, ref_div;123 124 mult = readl_relaxed(mult_addr) >> MSSPLL_FBDIV_SHIFT;125 mult &= clk_div_mask(MSSPLL_FBDIV_WIDTH);126 ref_div = readl_relaxed(ref_div_addr) >> MSSPLL_REFDIV_SHIFT;127 ref_div &= clk_div_mask(MSSPLL_REFDIV_WIDTH);128 129 return prate * mult / (ref_div * MSSPLL_FIXED_DIV);130}131 132static const struct clk_ops mpfs_clk_msspll_ops = {133 .recalc_rate = mpfs_clk_msspll_recalc_rate,134};135 136#define CLK_PLL(_id, _name, _parent, _shift, _width, _flags, _offset) { \137 .id = _id, \138 .flags = _flags, \139 .shift = _shift, \140 .width = _width, \141 .reg_offset = _offset, \142 .hw.init = CLK_HW_INIT_PARENTS_DATA(_name, _parent, &mpfs_clk_msspll_ops, 0), \143}144 145static struct mpfs_msspll_hw_clock mpfs_msspll_clks[] = {146 CLK_PLL(CLK_MSSPLL_INTERNAL, "clk_msspll_internal", mpfs_ext_ref, MSSPLL_FBDIV_SHIFT,147 MSSPLL_FBDIV_WIDTH, 0, REG_MSSPLL_SSCG_2_CR),148};149 150static int mpfs_clk_register_mssplls(struct device *dev, struct mpfs_msspll_hw_clock *msspll_hws,151 unsigned int num_clks, struct mpfs_clock_data *data)152{153 unsigned int i;154 int ret;155 156 for (i = 0; i < num_clks; i++) {157 struct mpfs_msspll_hw_clock *msspll_hw = &msspll_hws[i];158 159 msspll_hw->base = data->msspll_base;160 ret = devm_clk_hw_register(dev, &msspll_hw->hw);161 if (ret)162 return dev_err_probe(dev, ret, "failed to register msspll id: %d\n",163 CLK_MSSPLL_INTERNAL);164 165 data->hw_data.hws[msspll_hw->id] = &msspll_hw->hw;166 }167 168 return 0;169}170 171/*172 * MSS PLL output clocks173 */174 175#define CLK_PLL_OUT(_id, _name, _parent, _flags, _shift, _width, _offset) { \176 .id = _id, \177 .output.shift = _shift, \178 .output.width = _width, \179 .output.table = NULL, \180 .reg_offset = _offset, \181 .output.flags = _flags, \182 .output.hw.init = CLK_HW_INIT(_name, _parent, &clk_divider_ops, 0), \183 .output.lock = &mpfs_clk_lock, \184}185 186static struct mpfs_msspll_out_hw_clock mpfs_msspll_out_clks[] = {187 CLK_PLL_OUT(CLK_MSSPLL0, "clk_msspll", "clk_msspll_internal", CLK_DIVIDER_ONE_BASED,188 MSSPLL_POSTDIV02_SHIFT, MSSPLL_POSTDIV_WIDTH, REG_MSSPLL_POSTDIV01_CR),189 CLK_PLL_OUT(CLK_MSSPLL1, "clk_msspll1", "clk_msspll_internal", CLK_DIVIDER_ONE_BASED,190 MSSPLL_POSTDIV13_SHIFT, MSSPLL_POSTDIV_WIDTH, REG_MSSPLL_POSTDIV01_CR),191 CLK_PLL_OUT(CLK_MSSPLL2, "clk_msspll2", "clk_msspll_internal", CLK_DIVIDER_ONE_BASED,192 MSSPLL_POSTDIV02_SHIFT, MSSPLL_POSTDIV_WIDTH, REG_MSSPLL_POSTDIV23_CR),193 CLK_PLL_OUT(CLK_MSSPLL3, "clk_msspll3", "clk_msspll_internal", CLK_DIVIDER_ONE_BASED,194 MSSPLL_POSTDIV13_SHIFT, MSSPLL_POSTDIV_WIDTH, REG_MSSPLL_POSTDIV23_CR),195};196 197static int mpfs_clk_register_msspll_outs(struct device *dev,198 struct mpfs_msspll_out_hw_clock *msspll_out_hws,199 unsigned int num_clks, struct mpfs_clock_data *data)200{201 unsigned int i;202 int ret;203 204 for (i = 0; i < num_clks; i++) {205 struct mpfs_msspll_out_hw_clock *msspll_out_hw = &msspll_out_hws[i];206 207 msspll_out_hw->output.reg = data->msspll_base + msspll_out_hw->reg_offset;208 ret = devm_clk_hw_register(dev, &msspll_out_hw->output.hw);209 if (ret)210 return dev_err_probe(dev, ret, "failed to register msspll out id: %d\n",211 msspll_out_hw->id);212 213 data->hw_data.hws[msspll_out_hw->id] = &msspll_out_hw->output.hw;214 }215 216 return 0;217}218 219/*220 * "CFG" clocks221 */222 223#define CLK_CFG(_id, _name, _parent, _shift, _width, _table, _flags, _offset) { \224 .id = _id, \225 .cfg.shift = _shift, \226 .cfg.width = _width, \227 .cfg.table = _table, \228 .reg_offset = _offset, \229 .cfg.flags = _flags, \230 .cfg.hw.init = CLK_HW_INIT(_name, _parent, &clk_divider_ops, 0), \231 .cfg.lock = &mpfs_clk_lock, \232}233 234#define CLK_CPU_OFFSET 0u235#define CLK_AXI_OFFSET 1u236#define CLK_AHB_OFFSET 2u237#define CLK_RTCREF_OFFSET 3u238 239static struct mpfs_cfg_hw_clock mpfs_cfg_clks[] = {240 CLK_CFG(CLK_CPU, "clk_cpu", "clk_msspll", 0, 2, mpfs_div_cpu_axi_table, 0,241 REG_CLOCK_CONFIG_CR),242 CLK_CFG(CLK_AXI, "clk_axi", "clk_msspll", 2, 2, mpfs_div_cpu_axi_table, 0,243 REG_CLOCK_CONFIG_CR),244 CLK_CFG(CLK_AHB, "clk_ahb", "clk_msspll", 4, 2, mpfs_div_ahb_table, 0,245 REG_CLOCK_CONFIG_CR),246 {247 .id = CLK_RTCREF,248 .cfg.shift = 0,249 .cfg.width = 12,250 .cfg.table = mpfs_div_rtcref_table,251 .reg_offset = REG_RTC_CLOCK_CR,252 .cfg.flags = CLK_DIVIDER_ONE_BASED,253 .cfg.hw.init =254 CLK_HW_INIT_PARENTS_DATA("clk_rtcref", mpfs_ext_ref, &clk_divider_ops, 0),255 }256};257 258static int mpfs_clk_register_cfgs(struct device *dev, struct mpfs_cfg_hw_clock *cfg_hws,259 unsigned int num_clks, struct mpfs_clock_data *data)260{261 unsigned int i, id;262 int ret;263 264 for (i = 0; i < num_clks; i++) {265 struct mpfs_cfg_hw_clock *cfg_hw = &cfg_hws[i];266 267 cfg_hw->cfg.reg = data->base + cfg_hw->reg_offset;268 ret = devm_clk_hw_register(dev, &cfg_hw->cfg.hw);269 if (ret)270 return dev_err_probe(dev, ret, "failed to register clock id: %d\n",271 cfg_hw->id);272 273 id = cfg_hw->id;274 data->hw_data.hws[id] = &cfg_hw->cfg.hw;275 }276 277 return 0;278}279 280/*281 * peripheral clocks - devices connected to axi or ahb buses.282 */283 284#define CLK_PERIPH(_id, _name, _parent, _shift, _flags) { \285 .id = _id, \286 .periph.bit_idx = _shift, \287 .periph.hw.init = CLK_HW_INIT_HW(_name, _parent, &clk_gate_ops, \288 _flags), \289 .periph.lock = &mpfs_clk_lock, \290}291 292#define PARENT_CLK(PARENT) (&mpfs_cfg_clks[CLK_##PARENT##_OFFSET].cfg.hw)293 294/*295 * Critical clocks:296 * - CLK_ENVM: reserved by hart software services (hss) superloop monitor/m mode interrupt297 * trap handler298 * - CLK_MMUART0: reserved by the hss299 * - CLK_DDRC: provides clock to the ddr subsystem300 * - CLK_RTC: the onboard RTC's AHB bus clock must be kept running as the rtc will stop301 * if the AHB interface clock is disabled302 * - CLK_FICx: these provide the processor side clocks to the "FIC" (Fabric InterConnect)303 * clock domain crossers which provide the interface to the FPGA fabric. Disabling them304 * causes the FPGA fabric to go into reset.305 * - CLK_ATHENA: The athena clock is FIC4, which is reserved for the Athena TeraFire.306 */307 308static struct mpfs_periph_hw_clock mpfs_periph_clks[] = {309 CLK_PERIPH(CLK_ENVM, "clk_periph_envm", PARENT_CLK(AHB), 0, CLK_IS_CRITICAL),310 CLK_PERIPH(CLK_MAC0, "clk_periph_mac0", PARENT_CLK(AHB), 1, 0),311 CLK_PERIPH(CLK_MAC1, "clk_periph_mac1", PARENT_CLK(AHB), 2, 0),312 CLK_PERIPH(CLK_MMC, "clk_periph_mmc", PARENT_CLK(AHB), 3, 0),313 CLK_PERIPH(CLK_TIMER, "clk_periph_timer", PARENT_CLK(RTCREF), 4, 0),314 CLK_PERIPH(CLK_MMUART0, "clk_periph_mmuart0", PARENT_CLK(AHB), 5, CLK_IS_CRITICAL),315 CLK_PERIPH(CLK_MMUART1, "clk_periph_mmuart1", PARENT_CLK(AHB), 6, 0),316 CLK_PERIPH(CLK_MMUART2, "clk_periph_mmuart2", PARENT_CLK(AHB), 7, 0),317 CLK_PERIPH(CLK_MMUART3, "clk_periph_mmuart3", PARENT_CLK(AHB), 8, 0),318 CLK_PERIPH(CLK_MMUART4, "clk_periph_mmuart4", PARENT_CLK(AHB), 9, 0),319 CLK_PERIPH(CLK_SPI0, "clk_periph_spi0", PARENT_CLK(AHB), 10, 0),320 CLK_PERIPH(CLK_SPI1, "clk_periph_spi1", PARENT_CLK(AHB), 11, 0),321 CLK_PERIPH(CLK_I2C0, "clk_periph_i2c0", PARENT_CLK(AHB), 12, 0),322 CLK_PERIPH(CLK_I2C1, "clk_periph_i2c1", PARENT_CLK(AHB), 13, 0),323 CLK_PERIPH(CLK_CAN0, "clk_periph_can0", PARENT_CLK(AHB), 14, 0),324 CLK_PERIPH(CLK_CAN1, "clk_periph_can1", PARENT_CLK(AHB), 15, 0),325 CLK_PERIPH(CLK_USB, "clk_periph_usb", PARENT_CLK(AHB), 16, 0),326 CLK_PERIPH(CLK_RTC, "clk_periph_rtc", PARENT_CLK(AHB), 18, CLK_IS_CRITICAL),327 CLK_PERIPH(CLK_QSPI, "clk_periph_qspi", PARENT_CLK(AHB), 19, 0),328 CLK_PERIPH(CLK_GPIO0, "clk_periph_gpio0", PARENT_CLK(AHB), 20, 0),329 CLK_PERIPH(CLK_GPIO1, "clk_periph_gpio1", PARENT_CLK(AHB), 21, 0),330 CLK_PERIPH(CLK_GPIO2, "clk_periph_gpio2", PARENT_CLK(AHB), 22, 0),331 CLK_PERIPH(CLK_DDRC, "clk_periph_ddrc", PARENT_CLK(AHB), 23, CLK_IS_CRITICAL),332 CLK_PERIPH(CLK_FIC0, "clk_periph_fic0", PARENT_CLK(AXI), 24, CLK_IS_CRITICAL),333 CLK_PERIPH(CLK_FIC1, "clk_periph_fic1", PARENT_CLK(AXI), 25, CLK_IS_CRITICAL),334 CLK_PERIPH(CLK_FIC2, "clk_periph_fic2", PARENT_CLK(AXI), 26, CLK_IS_CRITICAL),335 CLK_PERIPH(CLK_FIC3, "clk_periph_fic3", PARENT_CLK(AXI), 27, CLK_IS_CRITICAL),336 CLK_PERIPH(CLK_ATHENA, "clk_periph_athena", PARENT_CLK(AXI), 28, CLK_IS_CRITICAL),337 CLK_PERIPH(CLK_CFM, "clk_periph_cfm", PARENT_CLK(AHB), 29, 0),338};339 340static int mpfs_clk_register_periphs(struct device *dev, struct mpfs_periph_hw_clock *periph_hws,341 int num_clks, struct mpfs_clock_data *data)342{343 unsigned int i, id;344 int ret;345 346 for (i = 0; i < num_clks; i++) {347 struct mpfs_periph_hw_clock *periph_hw = &periph_hws[i];348 349 periph_hw->periph.reg = data->base + REG_SUBBLK_CLOCK_CR;350 ret = devm_clk_hw_register(dev, &periph_hw->periph.hw);351 if (ret)352 return dev_err_probe(dev, ret, "failed to register clock id: %d\n",353 periph_hw->id);354 355 id = periph_hws[i].id;356 data->hw_data.hws[id] = &periph_hw->periph.hw;357 }358 359 return 0;360}361 362static int mpfs_clk_probe(struct platform_device *pdev)363{364 struct device *dev = &pdev->dev;365 struct mpfs_clock_data *clk_data;366 unsigned int num_clks;367 int ret;368 369 /* CLK_RESERVED is not part of clock arrays, so add 1 */370 num_clks = ARRAY_SIZE(mpfs_msspll_clks) + ARRAY_SIZE(mpfs_msspll_out_clks)371 + ARRAY_SIZE(mpfs_cfg_clks) + ARRAY_SIZE(mpfs_periph_clks) + 1;372 373 clk_data = devm_kzalloc(dev, struct_size(clk_data, hw_data.hws, num_clks), GFP_KERNEL);374 if (!clk_data)375 return -ENOMEM;376 377 clk_data->base = devm_platform_ioremap_resource(pdev, 0);378 if (IS_ERR(clk_data->base))379 return PTR_ERR(clk_data->base);380 381 clk_data->msspll_base = devm_platform_ioremap_resource(pdev, 1);382 if (IS_ERR(clk_data->msspll_base))383 return PTR_ERR(clk_data->msspll_base);384 385 clk_data->hw_data.num = num_clks;386 clk_data->dev = dev;387 dev_set_drvdata(dev, clk_data);388 389 ret = mpfs_clk_register_mssplls(dev, mpfs_msspll_clks, ARRAY_SIZE(mpfs_msspll_clks),390 clk_data);391 if (ret)392 return ret;393 394 ret = mpfs_clk_register_msspll_outs(dev, mpfs_msspll_out_clks,395 ARRAY_SIZE(mpfs_msspll_out_clks),396 clk_data);397 if (ret)398 return ret;399 400 ret = mpfs_clk_register_cfgs(dev, mpfs_cfg_clks, ARRAY_SIZE(mpfs_cfg_clks), clk_data);401 if (ret)402 return ret;403 404 ret = mpfs_clk_register_periphs(dev, mpfs_periph_clks, ARRAY_SIZE(mpfs_periph_clks),405 clk_data);406 if (ret)407 return ret;408 409 ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, &clk_data->hw_data);410 if (ret)411 return ret;412 413 return mpfs_reset_controller_register(dev, clk_data->base + REG_SUBBLK_RESET_CR);414}415 416static const struct of_device_id mpfs_clk_of_match_table[] = {417 { .compatible = "microchip,mpfs-clkcfg", },418 {}419};420MODULE_DEVICE_TABLE(of, mpfs_clk_of_match_table);421 422static struct platform_driver mpfs_clk_driver = {423 .probe = mpfs_clk_probe,424 .driver = {425 .name = "microchip-mpfs-clkcfg",426 .of_match_table = mpfs_clk_of_match_table,427 },428};429 430static int __init clk_mpfs_init(void)431{432 return platform_driver_register(&mpfs_clk_driver);433}434core_initcall(clk_mpfs_init);435 436static void __exit clk_mpfs_exit(void)437{438 platform_driver_unregister(&mpfs_clk_driver);439}440module_exit(clk_mpfs_exit);441 442MODULE_DESCRIPTION("Microchip PolarFire SoC Clock Driver");443MODULE_AUTHOR("Padmarao Begari <padmarao.begari@microchip.com>");444MODULE_AUTHOR("Daire McNamara <daire.mcnamara@microchip.com>");445MODULE_AUTHOR("Conor Dooley <conor.dooley@microchip.com>");446MODULE_IMPORT_NS(MCHP_CLK_MPFS);447