Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

clk: microchip: mpfs: re-parent the configurable clocks

Currently the mpfs clock driver uses a reference clock called the
"msspll", set in the device tree, as the parent for the cpu/axi/ahb
(config) clocks. The frequency of the msspll is determined by the FPGA
bitstream & the bootloader configures the clock to match the bitstream.
The real reference is provided by a 100 or 125 MHz off chip oscillator.

However, the msspll clock is not actually the parent of all clocks on
the system - the reference clock for the rtc/mtimer actually has the
off chip oscillator as its parent.

In order to fix this, add support for reading the configuration of the
msspll & reparent the "config" clocks so that they are derived from
this clock rather than the reference in the device tree.

Fixes: 635e5e73370e ("clk: microchip: Add driver for Microchip PolarFire SoC")
Reviewed-by: Daire McNamara <daire.mcnamara@microchip.com>
Signed-off-by: Conor Dooley <conor.dooley@microchip.com>
Link: https://lore.kernel.org/r/20220413075835.3354193-8-conor.dooley@microchip.com
Acked-by: Palmer Dabbelt <palmer@rivosinc.com>
Signed-off-by: Stephen Boyd <sboyd@kernel.org>

authored by

Conor Dooley and committed by
Stephen Boyd
445c2da8 8e8fbab4

+132 -19
+132 -19
drivers/clk/microchip/clk-mpfs.c
··· 11 11 #include <dt-bindings/clock/microchip,mpfs-clock.h> 12 12 13 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 14 17 #define REG_CLOCK_CONFIG_CR 0x08u 15 18 #define REG_SUBBLK_CLOCK_CR 0x84u 16 19 #define REG_SUBBLK_RESET_CR 0x88u 17 20 21 + #define MSSPLL_FBDIV_SHIFT 0x00u 22 + #define MSSPLL_FBDIV_WIDTH 0x0Cu 23 + #define MSSPLL_REFDIV_SHIFT 0x08u 24 + #define MSSPLL_REFDIV_WIDTH 0x06u 25 + #define MSSPLL_POSTDIV_SHIFT 0x08u 26 + #define MSSPLL_POSTDIV_WIDTH 0x07u 27 + #define MSSPLL_FIXED_DIV 4u 28 + 18 29 struct mpfs_clock_data { 19 30 void __iomem *base; 31 + void __iomem *msspll_base; 20 32 struct clk_hw_onecell_data hw_data; 21 33 }; 34 + 35 + struct mpfs_msspll_hw_clock { 36 + void __iomem *base; 37 + unsigned int id; 38 + u32 reg_offset; 39 + u32 shift; 40 + u32 width; 41 + u32 flags; 42 + struct clk_hw hw; 43 + struct clk_init_data init; 44 + }; 45 + 46 + #define to_mpfs_msspll_clk(_hw) container_of(_hw, struct mpfs_msspll_hw_clock, hw) 22 47 23 48 struct mpfs_cfg_clock { 24 49 const struct clk_div_table *table; 25 50 unsigned int id; 51 + u32 reg_offset; 26 52 u8 shift; 27 53 u8 width; 54 + u8 flags; 28 55 }; 29 56 30 57 struct mpfs_cfg_hw_clock { ··· 82 55 */ 83 56 static DEFINE_SPINLOCK(mpfs_clk_lock); 84 57 85 - static const struct clk_parent_data mpfs_cfg_parent[] = { 58 + static const struct clk_parent_data mpfs_ext_ref[] = { 86 59 { .index = 0 }, 87 60 }; 88 61 ··· 96 69 { 0, 0 } 97 70 }; 98 71 72 + static unsigned long mpfs_clk_msspll_recalc_rate(struct clk_hw *hw, unsigned long prate) 73 + { 74 + struct mpfs_msspll_hw_clock *msspll_hw = to_mpfs_msspll_clk(hw); 75 + void __iomem *mult_addr = msspll_hw->base + msspll_hw->reg_offset; 76 + void __iomem *ref_div_addr = msspll_hw->base + REG_MSSPLL_REF_CR; 77 + void __iomem *postdiv_addr = msspll_hw->base + REG_MSSPLL_POSTDIV_CR; 78 + u32 mult, ref_div, postdiv; 79 + 80 + mult = readl_relaxed(mult_addr) >> MSSPLL_FBDIV_SHIFT; 81 + mult &= clk_div_mask(MSSPLL_FBDIV_WIDTH); 82 + ref_div = readl_relaxed(ref_div_addr) >> MSSPLL_REFDIV_SHIFT; 83 + ref_div &= clk_div_mask(MSSPLL_REFDIV_WIDTH); 84 + postdiv = readl_relaxed(postdiv_addr) >> MSSPLL_POSTDIV_SHIFT; 85 + postdiv &= clk_div_mask(MSSPLL_POSTDIV_WIDTH); 86 + 87 + return prate * mult / (ref_div * MSSPLL_FIXED_DIV * postdiv); 88 + } 89 + 90 + static const struct clk_ops mpfs_clk_msspll_ops = { 91 + .recalc_rate = mpfs_clk_msspll_recalc_rate, 92 + }; 93 + 94 + #define CLK_PLL(_id, _name, _parent, _shift, _width, _flags, _offset) { \ 95 + .id = _id, \ 96 + .shift = _shift, \ 97 + .width = _width, \ 98 + .reg_offset = _offset, \ 99 + .flags = _flags, \ 100 + .hw.init = CLK_HW_INIT_PARENTS_DATA(_name, _parent, &mpfs_clk_msspll_ops, 0), \ 101 + } 102 + 103 + static struct mpfs_msspll_hw_clock mpfs_msspll_clks[] = { 104 + CLK_PLL(CLK_MSSPLL, "clk_msspll", mpfs_ext_ref, MSSPLL_FBDIV_SHIFT, 105 + MSSPLL_FBDIV_WIDTH, 0, REG_MSSPLL_SSCG_2_CR), 106 + }; 107 + 108 + static int mpfs_clk_register_msspll(struct device *dev, struct mpfs_msspll_hw_clock *msspll_hw, 109 + void __iomem *base) 110 + { 111 + msspll_hw->base = base; 112 + 113 + return devm_clk_hw_register(dev, &msspll_hw->hw); 114 + } 115 + 116 + static int mpfs_clk_register_mssplls(struct device *dev, struct mpfs_msspll_hw_clock *msspll_hws, 117 + unsigned int num_clks, struct mpfs_clock_data *data) 118 + { 119 + void __iomem *base = data->msspll_base; 120 + unsigned int i; 121 + int ret; 122 + 123 + for (i = 0; i < num_clks; i++) { 124 + struct mpfs_msspll_hw_clock *msspll_hw = &msspll_hws[i]; 125 + 126 + ret = mpfs_clk_register_msspll(dev, msspll_hw, base); 127 + if (ret) 128 + return dev_err_probe(dev, ret, "failed to register msspll id: %d\n", 129 + CLK_MSSPLL); 130 + 131 + data->hw_data.hws[msspll_hw->id] = &msspll_hw->hw; 132 + } 133 + 134 + return 0; 135 + } 136 + 137 + /* 138 + * "CFG" clocks 139 + */ 140 + 99 141 static unsigned long mpfs_cfg_clk_recalc_rate(struct clk_hw *hw, unsigned long prate) 100 142 { 101 143 struct mpfs_cfg_hw_clock *cfg_hw = to_mpfs_cfg_clk(hw); ··· 172 76 void __iomem *base_addr = cfg_hw->sys_base; 173 77 u32 val; 174 78 175 - val = readl_relaxed(base_addr + REG_CLOCK_CONFIG_CR) >> cfg->shift; 79 + val = readl_relaxed(base_addr + cfg->reg_offset) >> cfg->shift; 176 80 val &= clk_div_mask(cfg->width); 177 81 178 - return prate / (1u << val); 82 + return divider_recalc_rate(hw, prate, val, cfg->table, cfg->flags, cfg->width); 179 83 } 180 84 181 85 static long mpfs_cfg_clk_round_rate(struct clk_hw *hw, unsigned long rate, unsigned long *prate) ··· 201 105 return divider_setting; 202 106 203 107 spin_lock_irqsave(&mpfs_clk_lock, flags); 204 - 205 - val = readl_relaxed(base_addr + REG_CLOCK_CONFIG_CR); 108 + val = readl_relaxed(base_addr + cfg->reg_offset); 206 109 val &= ~(clk_div_mask(cfg->width) << cfg_hw->cfg.shift); 207 110 val |= divider_setting << cfg->shift; 208 - writel_relaxed(val, base_addr + REG_CLOCK_CONFIG_CR); 111 + writel_relaxed(val, base_addr + cfg->reg_offset); 209 112 210 113 spin_unlock_irqrestore(&mpfs_clk_lock, flags); 211 114 ··· 217 122 .set_rate = mpfs_cfg_clk_set_rate, 218 123 }; 219 124 220 - #define CLK_CFG(_id, _name, _parent, _shift, _width, _table, _flags) { \ 221 - .cfg.id = _id, \ 222 - .cfg.shift = _shift, \ 223 - .cfg.width = _width, \ 224 - .cfg.table = _table, \ 225 - .hw.init = CLK_HW_INIT_PARENTS_DATA(_name, _parent, &mpfs_clk_cfg_ops, \ 226 - _flags), \ 125 + #define CLK_CFG(_id, _name, _parent, _shift, _width, _table, _flags, _offset) { \ 126 + .cfg.id = _id, \ 127 + .cfg.shift = _shift, \ 128 + .cfg.width = _width, \ 129 + .cfg.table = _table, \ 130 + .cfg.reg_offset = _offset, \ 131 + .cfg.flags = _flags, \ 132 + .hw.init = CLK_HW_INIT(_name, _parent, &mpfs_clk_cfg_ops, 0), \ 227 133 } 228 134 229 135 static struct mpfs_cfg_hw_clock mpfs_cfg_clks[] = { 230 - CLK_CFG(CLK_CPU, "clk_cpu", mpfs_cfg_parent, 0, 2, mpfs_div_cpu_axi_table, 0), 231 - CLK_CFG(CLK_AXI, "clk_axi", mpfs_cfg_parent, 2, 2, mpfs_div_cpu_axi_table, 0), 232 - CLK_CFG(CLK_AHB, "clk_ahb", mpfs_cfg_parent, 4, 2, mpfs_div_ahb_table, 0), 136 + CLK_CFG(CLK_CPU, "clk_cpu", "clk_msspll", 0, 2, mpfs_div_cpu_axi_table, 0, 137 + REG_CLOCK_CONFIG_CR), 138 + CLK_CFG(CLK_AXI, "clk_axi", "clk_msspll", 2, 2, mpfs_div_cpu_axi_table, 0, 139 + REG_CLOCK_CONFIG_CR), 140 + CLK_CFG(CLK_AHB, "clk_ahb", "clk_msspll", 4, 2, mpfs_div_ahb_table, 0, 141 + REG_CLOCK_CONFIG_CR), 233 142 }; 234 143 235 144 static int mpfs_clk_register_cfg(struct device *dev, struct mpfs_cfg_hw_clock *cfg_hw, ··· 259 160 return dev_err_probe(dev, ret, "failed to register clock id: %d\n", 260 161 cfg_hw->cfg.id); 261 162 262 - id = cfg_hws[i].cfg.id; 163 + id = cfg_hw->cfg.id; 263 164 data->hw_data.hws[id] = &cfg_hw->hw; 264 165 } 265 166 266 167 return 0; 267 168 } 169 + 170 + /* 171 + * peripheral clocks - devices connected to axi or ahb buses. 172 + */ 268 173 269 174 static int mpfs_periph_clk_enable(struct clk_hw *hw) 270 175 { ··· 423 320 unsigned int num_clks; 424 321 int ret; 425 322 426 - /* CLK_RESERVED is not part of cfg_clks nor periph_clks, so add 1 */ 427 - num_clks = ARRAY_SIZE(mpfs_cfg_clks) + ARRAY_SIZE(mpfs_periph_clks) + 1; 323 + /* CLK_RESERVED is not part of clock arrays, so add 1 */ 324 + num_clks = ARRAY_SIZE(mpfs_msspll_clks) + ARRAY_SIZE(mpfs_cfg_clks) 325 + + ARRAY_SIZE(mpfs_periph_clks) + 1; 428 326 429 327 clk_data = devm_kzalloc(dev, struct_size(clk_data, hw_data.hws, num_clks), GFP_KERNEL); 430 328 if (!clk_data) ··· 435 331 if (IS_ERR(clk_data->base)) 436 332 return PTR_ERR(clk_data->base); 437 333 334 + clk_data->msspll_base = devm_platform_ioremap_resource(pdev, 1); 335 + if (IS_ERR(clk_data->msspll_base)) 336 + return PTR_ERR(clk_data->msspll_base); 337 + 438 338 clk_data->hw_data.num = num_clks; 339 + 340 + ret = mpfs_clk_register_mssplls(dev, mpfs_msspll_clks, ARRAY_SIZE(mpfs_msspll_clks), 341 + clk_data); 342 + if (ret) 343 + return ret; 439 344 440 345 ret = mpfs_clk_register_cfgs(dev, mpfs_cfg_clks, ARRAY_SIZE(mpfs_cfg_clks), clk_data); 441 346 if (ret)