162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * PolarFire SoC MSS/core complex clock control 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2020-2022 Microchip Technology Inc. All rights reserved. 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci#include <linux/auxiliary_bus.h> 862306a36Sopenharmony_ci#include <linux/clk-provider.h> 962306a36Sopenharmony_ci#include <linux/io.h> 1062306a36Sopenharmony_ci#include <linux/module.h> 1162306a36Sopenharmony_ci#include <linux/platform_device.h> 1262306a36Sopenharmony_ci#include <linux/slab.h> 1362306a36Sopenharmony_ci#include <dt-bindings/clock/microchip,mpfs-clock.h> 1462306a36Sopenharmony_ci#include <soc/microchip/mpfs.h> 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci/* address offset of control registers */ 1762306a36Sopenharmony_ci#define REG_MSSPLL_REF_CR 0x08u 1862306a36Sopenharmony_ci#define REG_MSSPLL_POSTDIV_CR 0x10u 1962306a36Sopenharmony_ci#define REG_MSSPLL_SSCG_2_CR 0x2Cu 2062306a36Sopenharmony_ci#define REG_CLOCK_CONFIG_CR 0x08u 2162306a36Sopenharmony_ci#define REG_RTC_CLOCK_CR 0x0Cu 2262306a36Sopenharmony_ci#define REG_SUBBLK_CLOCK_CR 0x84u 2362306a36Sopenharmony_ci#define REG_SUBBLK_RESET_CR 0x88u 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#define MSSPLL_FBDIV_SHIFT 0x00u 2662306a36Sopenharmony_ci#define MSSPLL_FBDIV_WIDTH 0x0Cu 2762306a36Sopenharmony_ci#define MSSPLL_REFDIV_SHIFT 0x08u 2862306a36Sopenharmony_ci#define MSSPLL_REFDIV_WIDTH 0x06u 2962306a36Sopenharmony_ci#define MSSPLL_POSTDIV_SHIFT 0x08u 3062306a36Sopenharmony_ci#define MSSPLL_POSTDIV_WIDTH 0x07u 3162306a36Sopenharmony_ci#define MSSPLL_FIXED_DIV 4u 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_cistruct mpfs_clock_data { 3462306a36Sopenharmony_ci struct device *dev; 3562306a36Sopenharmony_ci void __iomem *base; 3662306a36Sopenharmony_ci void __iomem *msspll_base; 3762306a36Sopenharmony_ci struct clk_hw_onecell_data hw_data; 3862306a36Sopenharmony_ci}; 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_cistruct mpfs_msspll_hw_clock { 4162306a36Sopenharmony_ci void __iomem *base; 4262306a36Sopenharmony_ci unsigned int id; 4362306a36Sopenharmony_ci u32 reg_offset; 4462306a36Sopenharmony_ci u32 shift; 4562306a36Sopenharmony_ci u32 width; 4662306a36Sopenharmony_ci u32 flags; 4762306a36Sopenharmony_ci struct clk_hw hw; 4862306a36Sopenharmony_ci struct clk_init_data init; 4962306a36Sopenharmony_ci}; 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci#define to_mpfs_msspll_clk(_hw) container_of(_hw, struct mpfs_msspll_hw_clock, hw) 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_cistruct mpfs_cfg_hw_clock { 5462306a36Sopenharmony_ci struct clk_divider cfg; 5562306a36Sopenharmony_ci struct clk_init_data init; 5662306a36Sopenharmony_ci unsigned int id; 5762306a36Sopenharmony_ci u32 reg_offset; 5862306a36Sopenharmony_ci}; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_cistruct mpfs_periph_hw_clock { 6162306a36Sopenharmony_ci struct clk_gate periph; 6262306a36Sopenharmony_ci unsigned int id; 6362306a36Sopenharmony_ci}; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci/* 6662306a36Sopenharmony_ci * mpfs_clk_lock prevents anything else from writing to the 6762306a36Sopenharmony_ci * mpfs clk block while a software locked register is being written. 6862306a36Sopenharmony_ci */ 6962306a36Sopenharmony_cistatic DEFINE_SPINLOCK(mpfs_clk_lock); 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_cistatic const struct clk_parent_data mpfs_ext_ref[] = { 7262306a36Sopenharmony_ci { .index = 0 }, 7362306a36Sopenharmony_ci}; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_cistatic const struct clk_div_table mpfs_div_cpu_axi_table[] = { 7662306a36Sopenharmony_ci { 0, 1 }, { 1, 2 }, { 2, 4 }, { 3, 8 }, 7762306a36Sopenharmony_ci { 0, 0 } 7862306a36Sopenharmony_ci}; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_cistatic const struct clk_div_table mpfs_div_ahb_table[] = { 8162306a36Sopenharmony_ci { 1, 2 }, { 2, 4}, { 3, 8 }, 8262306a36Sopenharmony_ci { 0, 0 } 8362306a36Sopenharmony_ci}; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci/* 8662306a36Sopenharmony_ci * The only two supported reference clock frequencies for the PolarFire SoC are 8762306a36Sopenharmony_ci * 100 and 125 MHz, as the rtc reference is required to be 1 MHz. 8862306a36Sopenharmony_ci * It therefore only needs to have divider table entries corresponding to 8962306a36Sopenharmony_ci * divide by 100 and 125. 9062306a36Sopenharmony_ci */ 9162306a36Sopenharmony_cistatic const struct clk_div_table mpfs_div_rtcref_table[] = { 9262306a36Sopenharmony_ci { 100, 100 }, { 125, 125 }, 9362306a36Sopenharmony_ci { 0, 0 } 9462306a36Sopenharmony_ci}; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_cistatic unsigned long mpfs_clk_msspll_recalc_rate(struct clk_hw *hw, unsigned long prate) 9762306a36Sopenharmony_ci{ 9862306a36Sopenharmony_ci struct mpfs_msspll_hw_clock *msspll_hw = to_mpfs_msspll_clk(hw); 9962306a36Sopenharmony_ci void __iomem *mult_addr = msspll_hw->base + msspll_hw->reg_offset; 10062306a36Sopenharmony_ci void __iomem *ref_div_addr = msspll_hw->base + REG_MSSPLL_REF_CR; 10162306a36Sopenharmony_ci void __iomem *postdiv_addr = msspll_hw->base + REG_MSSPLL_POSTDIV_CR; 10262306a36Sopenharmony_ci u32 mult, ref_div, postdiv; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci mult = readl_relaxed(mult_addr) >> MSSPLL_FBDIV_SHIFT; 10562306a36Sopenharmony_ci mult &= clk_div_mask(MSSPLL_FBDIV_WIDTH); 10662306a36Sopenharmony_ci ref_div = readl_relaxed(ref_div_addr) >> MSSPLL_REFDIV_SHIFT; 10762306a36Sopenharmony_ci ref_div &= clk_div_mask(MSSPLL_REFDIV_WIDTH); 10862306a36Sopenharmony_ci postdiv = readl_relaxed(postdiv_addr) >> MSSPLL_POSTDIV_SHIFT; 10962306a36Sopenharmony_ci postdiv &= clk_div_mask(MSSPLL_POSTDIV_WIDTH); 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci return prate * mult / (ref_div * MSSPLL_FIXED_DIV * postdiv); 11262306a36Sopenharmony_ci} 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_cistatic long mpfs_clk_msspll_round_rate(struct clk_hw *hw, unsigned long rate, unsigned long *prate) 11562306a36Sopenharmony_ci{ 11662306a36Sopenharmony_ci struct mpfs_msspll_hw_clock *msspll_hw = to_mpfs_msspll_clk(hw); 11762306a36Sopenharmony_ci void __iomem *mult_addr = msspll_hw->base + msspll_hw->reg_offset; 11862306a36Sopenharmony_ci void __iomem *ref_div_addr = msspll_hw->base + REG_MSSPLL_REF_CR; 11962306a36Sopenharmony_ci u32 mult, ref_div; 12062306a36Sopenharmony_ci unsigned long rate_before_ctrl; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci mult = readl_relaxed(mult_addr) >> MSSPLL_FBDIV_SHIFT; 12362306a36Sopenharmony_ci mult &= clk_div_mask(MSSPLL_FBDIV_WIDTH); 12462306a36Sopenharmony_ci ref_div = readl_relaxed(ref_div_addr) >> MSSPLL_REFDIV_SHIFT; 12562306a36Sopenharmony_ci ref_div &= clk_div_mask(MSSPLL_REFDIV_WIDTH); 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci rate_before_ctrl = rate * (ref_div * MSSPLL_FIXED_DIV) / mult; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci return divider_round_rate(hw, rate_before_ctrl, prate, NULL, MSSPLL_POSTDIV_WIDTH, 13062306a36Sopenharmony_ci msspll_hw->flags); 13162306a36Sopenharmony_ci} 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_cistatic int mpfs_clk_msspll_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long prate) 13462306a36Sopenharmony_ci{ 13562306a36Sopenharmony_ci struct mpfs_msspll_hw_clock *msspll_hw = to_mpfs_msspll_clk(hw); 13662306a36Sopenharmony_ci void __iomem *mult_addr = msspll_hw->base + msspll_hw->reg_offset; 13762306a36Sopenharmony_ci void __iomem *ref_div_addr = msspll_hw->base + REG_MSSPLL_REF_CR; 13862306a36Sopenharmony_ci void __iomem *postdiv_addr = msspll_hw->base + REG_MSSPLL_POSTDIV_CR; 13962306a36Sopenharmony_ci u32 mult, ref_div, postdiv; 14062306a36Sopenharmony_ci int divider_setting; 14162306a36Sopenharmony_ci unsigned long rate_before_ctrl, flags; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci mult = readl_relaxed(mult_addr) >> MSSPLL_FBDIV_SHIFT; 14462306a36Sopenharmony_ci mult &= clk_div_mask(MSSPLL_FBDIV_WIDTH); 14562306a36Sopenharmony_ci ref_div = readl_relaxed(ref_div_addr) >> MSSPLL_REFDIV_SHIFT; 14662306a36Sopenharmony_ci ref_div &= clk_div_mask(MSSPLL_REFDIV_WIDTH); 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci rate_before_ctrl = rate * (ref_div * MSSPLL_FIXED_DIV) / mult; 14962306a36Sopenharmony_ci divider_setting = divider_get_val(rate_before_ctrl, prate, NULL, MSSPLL_POSTDIV_WIDTH, 15062306a36Sopenharmony_ci msspll_hw->flags); 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci if (divider_setting < 0) 15362306a36Sopenharmony_ci return divider_setting; 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci spin_lock_irqsave(&mpfs_clk_lock, flags); 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci postdiv = readl_relaxed(postdiv_addr); 15862306a36Sopenharmony_ci postdiv &= ~(clk_div_mask(MSSPLL_POSTDIV_WIDTH) << MSSPLL_POSTDIV_SHIFT); 15962306a36Sopenharmony_ci writel_relaxed(postdiv, postdiv_addr); 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci spin_unlock_irqrestore(&mpfs_clk_lock, flags); 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci return 0; 16462306a36Sopenharmony_ci} 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_cistatic const struct clk_ops mpfs_clk_msspll_ops = { 16762306a36Sopenharmony_ci .recalc_rate = mpfs_clk_msspll_recalc_rate, 16862306a36Sopenharmony_ci .round_rate = mpfs_clk_msspll_round_rate, 16962306a36Sopenharmony_ci .set_rate = mpfs_clk_msspll_set_rate, 17062306a36Sopenharmony_ci}; 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci#define CLK_PLL(_id, _name, _parent, _shift, _width, _flags, _offset) { \ 17362306a36Sopenharmony_ci .id = _id, \ 17462306a36Sopenharmony_ci .shift = _shift, \ 17562306a36Sopenharmony_ci .width = _width, \ 17662306a36Sopenharmony_ci .reg_offset = _offset, \ 17762306a36Sopenharmony_ci .flags = _flags, \ 17862306a36Sopenharmony_ci .hw.init = CLK_HW_INIT_PARENTS_DATA(_name, _parent, &mpfs_clk_msspll_ops, 0), \ 17962306a36Sopenharmony_ci} 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_cistatic struct mpfs_msspll_hw_clock mpfs_msspll_clks[] = { 18262306a36Sopenharmony_ci CLK_PLL(CLK_MSSPLL, "clk_msspll", mpfs_ext_ref, MSSPLL_FBDIV_SHIFT, 18362306a36Sopenharmony_ci MSSPLL_FBDIV_WIDTH, 0, REG_MSSPLL_SSCG_2_CR), 18462306a36Sopenharmony_ci}; 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_cistatic int mpfs_clk_register_mssplls(struct device *dev, struct mpfs_msspll_hw_clock *msspll_hws, 18762306a36Sopenharmony_ci unsigned int num_clks, struct mpfs_clock_data *data) 18862306a36Sopenharmony_ci{ 18962306a36Sopenharmony_ci unsigned int i; 19062306a36Sopenharmony_ci int ret; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci for (i = 0; i < num_clks; i++) { 19362306a36Sopenharmony_ci struct mpfs_msspll_hw_clock *msspll_hw = &msspll_hws[i]; 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci msspll_hw->base = data->msspll_base; 19662306a36Sopenharmony_ci ret = devm_clk_hw_register(dev, &msspll_hw->hw); 19762306a36Sopenharmony_ci if (ret) 19862306a36Sopenharmony_ci return dev_err_probe(dev, ret, "failed to register msspll id: %d\n", 19962306a36Sopenharmony_ci CLK_MSSPLL); 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci data->hw_data.hws[msspll_hw->id] = &msspll_hw->hw; 20262306a36Sopenharmony_ci } 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci return 0; 20562306a36Sopenharmony_ci} 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci/* 20862306a36Sopenharmony_ci * "CFG" clocks 20962306a36Sopenharmony_ci */ 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci#define CLK_CFG(_id, _name, _parent, _shift, _width, _table, _flags, _offset) { \ 21262306a36Sopenharmony_ci .id = _id, \ 21362306a36Sopenharmony_ci .cfg.shift = _shift, \ 21462306a36Sopenharmony_ci .cfg.width = _width, \ 21562306a36Sopenharmony_ci .cfg.table = _table, \ 21662306a36Sopenharmony_ci .reg_offset = _offset, \ 21762306a36Sopenharmony_ci .cfg.flags = _flags, \ 21862306a36Sopenharmony_ci .cfg.hw.init = CLK_HW_INIT(_name, _parent, &clk_divider_ops, 0), \ 21962306a36Sopenharmony_ci .cfg.lock = &mpfs_clk_lock, \ 22062306a36Sopenharmony_ci} 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci#define CLK_CPU_OFFSET 0u 22362306a36Sopenharmony_ci#define CLK_AXI_OFFSET 1u 22462306a36Sopenharmony_ci#define CLK_AHB_OFFSET 2u 22562306a36Sopenharmony_ci#define CLK_RTCREF_OFFSET 3u 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_cistatic struct mpfs_cfg_hw_clock mpfs_cfg_clks[] = { 22862306a36Sopenharmony_ci CLK_CFG(CLK_CPU, "clk_cpu", "clk_msspll", 0, 2, mpfs_div_cpu_axi_table, 0, 22962306a36Sopenharmony_ci REG_CLOCK_CONFIG_CR), 23062306a36Sopenharmony_ci CLK_CFG(CLK_AXI, "clk_axi", "clk_msspll", 2, 2, mpfs_div_cpu_axi_table, 0, 23162306a36Sopenharmony_ci REG_CLOCK_CONFIG_CR), 23262306a36Sopenharmony_ci CLK_CFG(CLK_AHB, "clk_ahb", "clk_msspll", 4, 2, mpfs_div_ahb_table, 0, 23362306a36Sopenharmony_ci REG_CLOCK_CONFIG_CR), 23462306a36Sopenharmony_ci { 23562306a36Sopenharmony_ci .id = CLK_RTCREF, 23662306a36Sopenharmony_ci .cfg.shift = 0, 23762306a36Sopenharmony_ci .cfg.width = 12, 23862306a36Sopenharmony_ci .cfg.table = mpfs_div_rtcref_table, 23962306a36Sopenharmony_ci .reg_offset = REG_RTC_CLOCK_CR, 24062306a36Sopenharmony_ci .cfg.flags = CLK_DIVIDER_ONE_BASED, 24162306a36Sopenharmony_ci .cfg.hw.init = 24262306a36Sopenharmony_ci CLK_HW_INIT_PARENTS_DATA("clk_rtcref", mpfs_ext_ref, &clk_divider_ops, 0), 24362306a36Sopenharmony_ci } 24462306a36Sopenharmony_ci}; 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_cistatic int mpfs_clk_register_cfgs(struct device *dev, struct mpfs_cfg_hw_clock *cfg_hws, 24762306a36Sopenharmony_ci unsigned int num_clks, struct mpfs_clock_data *data) 24862306a36Sopenharmony_ci{ 24962306a36Sopenharmony_ci unsigned int i, id; 25062306a36Sopenharmony_ci int ret; 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci for (i = 0; i < num_clks; i++) { 25362306a36Sopenharmony_ci struct mpfs_cfg_hw_clock *cfg_hw = &cfg_hws[i]; 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci cfg_hw->cfg.reg = data->base + cfg_hw->reg_offset; 25662306a36Sopenharmony_ci ret = devm_clk_hw_register(dev, &cfg_hw->cfg.hw); 25762306a36Sopenharmony_ci if (ret) 25862306a36Sopenharmony_ci return dev_err_probe(dev, ret, "failed to register clock id: %d\n", 25962306a36Sopenharmony_ci cfg_hw->id); 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci id = cfg_hw->id; 26262306a36Sopenharmony_ci data->hw_data.hws[id] = &cfg_hw->cfg.hw; 26362306a36Sopenharmony_ci } 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci return 0; 26662306a36Sopenharmony_ci} 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci/* 26962306a36Sopenharmony_ci * peripheral clocks - devices connected to axi or ahb buses. 27062306a36Sopenharmony_ci */ 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci#define CLK_PERIPH(_id, _name, _parent, _shift, _flags) { \ 27362306a36Sopenharmony_ci .id = _id, \ 27462306a36Sopenharmony_ci .periph.bit_idx = _shift, \ 27562306a36Sopenharmony_ci .periph.hw.init = CLK_HW_INIT_HW(_name, _parent, &clk_gate_ops, \ 27662306a36Sopenharmony_ci _flags), \ 27762306a36Sopenharmony_ci .periph.lock = &mpfs_clk_lock, \ 27862306a36Sopenharmony_ci} 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci#define PARENT_CLK(PARENT) (&mpfs_cfg_clks[CLK_##PARENT##_OFFSET].cfg.hw) 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci/* 28362306a36Sopenharmony_ci * Critical clocks: 28462306a36Sopenharmony_ci * - CLK_ENVM: reserved by hart software services (hss) superloop monitor/m mode interrupt 28562306a36Sopenharmony_ci * trap handler 28662306a36Sopenharmony_ci * - CLK_MMUART0: reserved by the hss 28762306a36Sopenharmony_ci * - CLK_DDRC: provides clock to the ddr subsystem 28862306a36Sopenharmony_ci * - CLK_RTC: the onboard RTC's AHB bus clock must be kept running as the rtc will stop 28962306a36Sopenharmony_ci * if the AHB interface clock is disabled 29062306a36Sopenharmony_ci * - CLK_FICx: these provide the processor side clocks to the "FIC" (Fabric InterConnect) 29162306a36Sopenharmony_ci * clock domain crossers which provide the interface to the FPGA fabric. Disabling them 29262306a36Sopenharmony_ci * causes the FPGA fabric to go into reset. 29362306a36Sopenharmony_ci * - CLK_ATHENA: The athena clock is FIC4, which is reserved for the Athena TeraFire. 29462306a36Sopenharmony_ci */ 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_cistatic struct mpfs_periph_hw_clock mpfs_periph_clks[] = { 29762306a36Sopenharmony_ci CLK_PERIPH(CLK_ENVM, "clk_periph_envm", PARENT_CLK(AHB), 0, CLK_IS_CRITICAL), 29862306a36Sopenharmony_ci CLK_PERIPH(CLK_MAC0, "clk_periph_mac0", PARENT_CLK(AHB), 1, 0), 29962306a36Sopenharmony_ci CLK_PERIPH(CLK_MAC1, "clk_periph_mac1", PARENT_CLK(AHB), 2, 0), 30062306a36Sopenharmony_ci CLK_PERIPH(CLK_MMC, "clk_periph_mmc", PARENT_CLK(AHB), 3, 0), 30162306a36Sopenharmony_ci CLK_PERIPH(CLK_TIMER, "clk_periph_timer", PARENT_CLK(RTCREF), 4, 0), 30262306a36Sopenharmony_ci CLK_PERIPH(CLK_MMUART0, "clk_periph_mmuart0", PARENT_CLK(AHB), 5, CLK_IS_CRITICAL), 30362306a36Sopenharmony_ci CLK_PERIPH(CLK_MMUART1, "clk_periph_mmuart1", PARENT_CLK(AHB), 6, 0), 30462306a36Sopenharmony_ci CLK_PERIPH(CLK_MMUART2, "clk_periph_mmuart2", PARENT_CLK(AHB), 7, 0), 30562306a36Sopenharmony_ci CLK_PERIPH(CLK_MMUART3, "clk_periph_mmuart3", PARENT_CLK(AHB), 8, 0), 30662306a36Sopenharmony_ci CLK_PERIPH(CLK_MMUART4, "clk_periph_mmuart4", PARENT_CLK(AHB), 9, 0), 30762306a36Sopenharmony_ci CLK_PERIPH(CLK_SPI0, "clk_periph_spi0", PARENT_CLK(AHB), 10, 0), 30862306a36Sopenharmony_ci CLK_PERIPH(CLK_SPI1, "clk_periph_spi1", PARENT_CLK(AHB), 11, 0), 30962306a36Sopenharmony_ci CLK_PERIPH(CLK_I2C0, "clk_periph_i2c0", PARENT_CLK(AHB), 12, 0), 31062306a36Sopenharmony_ci CLK_PERIPH(CLK_I2C1, "clk_periph_i2c1", PARENT_CLK(AHB), 13, 0), 31162306a36Sopenharmony_ci CLK_PERIPH(CLK_CAN0, "clk_periph_can0", PARENT_CLK(AHB), 14, 0), 31262306a36Sopenharmony_ci CLK_PERIPH(CLK_CAN1, "clk_periph_can1", PARENT_CLK(AHB), 15, 0), 31362306a36Sopenharmony_ci CLK_PERIPH(CLK_USB, "clk_periph_usb", PARENT_CLK(AHB), 16, 0), 31462306a36Sopenharmony_ci CLK_PERIPH(CLK_RTC, "clk_periph_rtc", PARENT_CLK(AHB), 18, CLK_IS_CRITICAL), 31562306a36Sopenharmony_ci CLK_PERIPH(CLK_QSPI, "clk_periph_qspi", PARENT_CLK(AHB), 19, 0), 31662306a36Sopenharmony_ci CLK_PERIPH(CLK_GPIO0, "clk_periph_gpio0", PARENT_CLK(AHB), 20, 0), 31762306a36Sopenharmony_ci CLK_PERIPH(CLK_GPIO1, "clk_periph_gpio1", PARENT_CLK(AHB), 21, 0), 31862306a36Sopenharmony_ci CLK_PERIPH(CLK_GPIO2, "clk_periph_gpio2", PARENT_CLK(AHB), 22, 0), 31962306a36Sopenharmony_ci CLK_PERIPH(CLK_DDRC, "clk_periph_ddrc", PARENT_CLK(AHB), 23, CLK_IS_CRITICAL), 32062306a36Sopenharmony_ci CLK_PERIPH(CLK_FIC0, "clk_periph_fic0", PARENT_CLK(AXI), 24, CLK_IS_CRITICAL), 32162306a36Sopenharmony_ci CLK_PERIPH(CLK_FIC1, "clk_periph_fic1", PARENT_CLK(AXI), 25, CLK_IS_CRITICAL), 32262306a36Sopenharmony_ci CLK_PERIPH(CLK_FIC2, "clk_periph_fic2", PARENT_CLK(AXI), 26, CLK_IS_CRITICAL), 32362306a36Sopenharmony_ci CLK_PERIPH(CLK_FIC3, "clk_periph_fic3", PARENT_CLK(AXI), 27, CLK_IS_CRITICAL), 32462306a36Sopenharmony_ci CLK_PERIPH(CLK_ATHENA, "clk_periph_athena", PARENT_CLK(AXI), 28, CLK_IS_CRITICAL), 32562306a36Sopenharmony_ci CLK_PERIPH(CLK_CFM, "clk_periph_cfm", PARENT_CLK(AHB), 29, 0), 32662306a36Sopenharmony_ci}; 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_cistatic int mpfs_clk_register_periphs(struct device *dev, struct mpfs_periph_hw_clock *periph_hws, 32962306a36Sopenharmony_ci int num_clks, struct mpfs_clock_data *data) 33062306a36Sopenharmony_ci{ 33162306a36Sopenharmony_ci unsigned int i, id; 33262306a36Sopenharmony_ci int ret; 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci for (i = 0; i < num_clks; i++) { 33562306a36Sopenharmony_ci struct mpfs_periph_hw_clock *periph_hw = &periph_hws[i]; 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci periph_hw->periph.reg = data->base + REG_SUBBLK_CLOCK_CR; 33862306a36Sopenharmony_ci ret = devm_clk_hw_register(dev, &periph_hw->periph.hw); 33962306a36Sopenharmony_ci if (ret) 34062306a36Sopenharmony_ci return dev_err_probe(dev, ret, "failed to register clock id: %d\n", 34162306a36Sopenharmony_ci periph_hw->id); 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci id = periph_hws[i].id; 34462306a36Sopenharmony_ci data->hw_data.hws[id] = &periph_hw->periph.hw; 34562306a36Sopenharmony_ci } 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci return 0; 34862306a36Sopenharmony_ci} 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci/* 35162306a36Sopenharmony_ci * Peripheral clock resets 35262306a36Sopenharmony_ci */ 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_RESET_CONTROLLER) 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ciu32 mpfs_reset_read(struct device *dev) 35762306a36Sopenharmony_ci{ 35862306a36Sopenharmony_ci struct mpfs_clock_data *clock_data = dev_get_drvdata(dev->parent); 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci return readl_relaxed(clock_data->base + REG_SUBBLK_RESET_CR); 36162306a36Sopenharmony_ci} 36262306a36Sopenharmony_ciEXPORT_SYMBOL_NS_GPL(mpfs_reset_read, MCHP_CLK_MPFS); 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_civoid mpfs_reset_write(struct device *dev, u32 val) 36562306a36Sopenharmony_ci{ 36662306a36Sopenharmony_ci struct mpfs_clock_data *clock_data = dev_get_drvdata(dev->parent); 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci writel_relaxed(val, clock_data->base + REG_SUBBLK_RESET_CR); 36962306a36Sopenharmony_ci} 37062306a36Sopenharmony_ciEXPORT_SYMBOL_NS_GPL(mpfs_reset_write, MCHP_CLK_MPFS); 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_cistatic void mpfs_reset_unregister_adev(void *_adev) 37362306a36Sopenharmony_ci{ 37462306a36Sopenharmony_ci struct auxiliary_device *adev = _adev; 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci auxiliary_device_delete(adev); 37762306a36Sopenharmony_ci auxiliary_device_uninit(adev); 37862306a36Sopenharmony_ci} 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_cistatic void mpfs_reset_adev_release(struct device *dev) 38162306a36Sopenharmony_ci{ 38262306a36Sopenharmony_ci struct auxiliary_device *adev = to_auxiliary_dev(dev); 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci kfree(adev); 38562306a36Sopenharmony_ci} 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_cistatic struct auxiliary_device *mpfs_reset_adev_alloc(struct mpfs_clock_data *clk_data) 38862306a36Sopenharmony_ci{ 38962306a36Sopenharmony_ci struct auxiliary_device *adev; 39062306a36Sopenharmony_ci int ret; 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci adev = kzalloc(sizeof(*adev), GFP_KERNEL); 39362306a36Sopenharmony_ci if (!adev) 39462306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci adev->name = "reset-mpfs"; 39762306a36Sopenharmony_ci adev->dev.parent = clk_data->dev; 39862306a36Sopenharmony_ci adev->dev.release = mpfs_reset_adev_release; 39962306a36Sopenharmony_ci adev->id = 666u; 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci ret = auxiliary_device_init(adev); 40262306a36Sopenharmony_ci if (ret) { 40362306a36Sopenharmony_ci kfree(adev); 40462306a36Sopenharmony_ci return ERR_PTR(ret); 40562306a36Sopenharmony_ci } 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci return adev; 40862306a36Sopenharmony_ci} 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_cistatic int mpfs_reset_controller_register(struct mpfs_clock_data *clk_data) 41162306a36Sopenharmony_ci{ 41262306a36Sopenharmony_ci struct auxiliary_device *adev; 41362306a36Sopenharmony_ci int ret; 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci adev = mpfs_reset_adev_alloc(clk_data); 41662306a36Sopenharmony_ci if (IS_ERR(adev)) 41762306a36Sopenharmony_ci return PTR_ERR(adev); 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci ret = auxiliary_device_add(adev); 42062306a36Sopenharmony_ci if (ret) { 42162306a36Sopenharmony_ci auxiliary_device_uninit(adev); 42262306a36Sopenharmony_ci return ret; 42362306a36Sopenharmony_ci } 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci return devm_add_action_or_reset(clk_data->dev, mpfs_reset_unregister_adev, adev); 42662306a36Sopenharmony_ci} 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci#else /* !CONFIG_RESET_CONTROLLER */ 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_cistatic int mpfs_reset_controller_register(struct mpfs_clock_data *clk_data) 43162306a36Sopenharmony_ci{ 43262306a36Sopenharmony_ci return 0; 43362306a36Sopenharmony_ci} 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci#endif /* !CONFIG_RESET_CONTROLLER */ 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_cistatic int mpfs_clk_probe(struct platform_device *pdev) 43862306a36Sopenharmony_ci{ 43962306a36Sopenharmony_ci struct device *dev = &pdev->dev; 44062306a36Sopenharmony_ci struct mpfs_clock_data *clk_data; 44162306a36Sopenharmony_ci unsigned int num_clks; 44262306a36Sopenharmony_ci int ret; 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci /* CLK_RESERVED is not part of clock arrays, so add 1 */ 44562306a36Sopenharmony_ci num_clks = ARRAY_SIZE(mpfs_msspll_clks) + ARRAY_SIZE(mpfs_cfg_clks) 44662306a36Sopenharmony_ci + ARRAY_SIZE(mpfs_periph_clks) + 1; 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci clk_data = devm_kzalloc(dev, struct_size(clk_data, hw_data.hws, num_clks), GFP_KERNEL); 44962306a36Sopenharmony_ci if (!clk_data) 45062306a36Sopenharmony_ci return -ENOMEM; 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci clk_data->base = devm_platform_ioremap_resource(pdev, 0); 45362306a36Sopenharmony_ci if (IS_ERR(clk_data->base)) 45462306a36Sopenharmony_ci return PTR_ERR(clk_data->base); 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci clk_data->msspll_base = devm_platform_ioremap_resource(pdev, 1); 45762306a36Sopenharmony_ci if (IS_ERR(clk_data->msspll_base)) 45862306a36Sopenharmony_ci return PTR_ERR(clk_data->msspll_base); 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci clk_data->hw_data.num = num_clks; 46162306a36Sopenharmony_ci clk_data->dev = dev; 46262306a36Sopenharmony_ci dev_set_drvdata(dev, clk_data); 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci ret = mpfs_clk_register_mssplls(dev, mpfs_msspll_clks, ARRAY_SIZE(mpfs_msspll_clks), 46562306a36Sopenharmony_ci clk_data); 46662306a36Sopenharmony_ci if (ret) 46762306a36Sopenharmony_ci return ret; 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci ret = mpfs_clk_register_cfgs(dev, mpfs_cfg_clks, ARRAY_SIZE(mpfs_cfg_clks), clk_data); 47062306a36Sopenharmony_ci if (ret) 47162306a36Sopenharmony_ci return ret; 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci ret = mpfs_clk_register_periphs(dev, mpfs_periph_clks, ARRAY_SIZE(mpfs_periph_clks), 47462306a36Sopenharmony_ci clk_data); 47562306a36Sopenharmony_ci if (ret) 47662306a36Sopenharmony_ci return ret; 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, &clk_data->hw_data); 47962306a36Sopenharmony_ci if (ret) 48062306a36Sopenharmony_ci return ret; 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci return mpfs_reset_controller_register(clk_data); 48362306a36Sopenharmony_ci} 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_cistatic const struct of_device_id mpfs_clk_of_match_table[] = { 48662306a36Sopenharmony_ci { .compatible = "microchip,mpfs-clkcfg", }, 48762306a36Sopenharmony_ci {} 48862306a36Sopenharmony_ci}; 48962306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, mpfs_clk_of_match_table); 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_cistatic struct platform_driver mpfs_clk_driver = { 49262306a36Sopenharmony_ci .probe = mpfs_clk_probe, 49362306a36Sopenharmony_ci .driver = { 49462306a36Sopenharmony_ci .name = "microchip-mpfs-clkcfg", 49562306a36Sopenharmony_ci .of_match_table = mpfs_clk_of_match_table, 49662306a36Sopenharmony_ci }, 49762306a36Sopenharmony_ci}; 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_cistatic int __init clk_mpfs_init(void) 50062306a36Sopenharmony_ci{ 50162306a36Sopenharmony_ci return platform_driver_register(&mpfs_clk_driver); 50262306a36Sopenharmony_ci} 50362306a36Sopenharmony_cicore_initcall(clk_mpfs_init); 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_cistatic void __exit clk_mpfs_exit(void) 50662306a36Sopenharmony_ci{ 50762306a36Sopenharmony_ci platform_driver_unregister(&mpfs_clk_driver); 50862306a36Sopenharmony_ci} 50962306a36Sopenharmony_cimodule_exit(clk_mpfs_exit); 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ciMODULE_DESCRIPTION("Microchip PolarFire SoC Clock Driver"); 51262306a36Sopenharmony_ciMODULE_AUTHOR("Padmarao Begari <padmarao.begari@microchip.com>"); 51362306a36Sopenharmony_ciMODULE_AUTHOR("Daire McNamara <daire.mcnamara@microchip.com>"); 51462306a36Sopenharmony_ciMODULE_AUTHOR("Conor Dooley <conor.dooley@microchip.com>"); 515