diff options
author | Conor Dooley <conor.dooley@microchip.com> | 2024-01-22 15:19:51 +0300 |
---|---|---|
committer | Conor Dooley <conor.dooley@microchip.com> | 2024-02-06 17:07:18 +0300 |
commit | 1afa9480c997b016a20b6a292824ad1056307176 (patch) | |
tree | 58d36c3abc2d97d1a4fae84013ab145d3934d632 /drivers/clk/microchip | |
parent | 8c2b1b48ad83d37a58a555c3bcf372b4ab477c64 (diff) | |
download | linux-1afa9480c997b016a20b6a292824ad1056307176.tar.xz |
clk: microchip: mpfs: split MSSPLL in two
The MSSPLL is really two stages - there's the PLL itself and 4 outputs,
each with their own divider. The current driver models this as a single
entity, outputting a single clock, used for both the CPU and AHB/AXI
buses. The other 3 outputs are used for the eMMC, "user crypto" and CAN
controller. Split the MSSPLL in two, as a precursor to adding support
for the other 3 outputs, with the PLL itself as one "hw" clock and the
output divider stage as another.
Signed-off-by: Conor Dooley <conor.dooley@microchip.com>
Diffstat (limited to 'drivers/clk/microchip')
-rw-r--r-- | drivers/clk/microchip/clk-mpfs.c | 174 |
1 files changed, 116 insertions, 58 deletions
diff --git a/drivers/clk/microchip/clk-mpfs.c b/drivers/clk/microchip/clk-mpfs.c index c8ffa755b58d..acf598a32ce0 100644 --- a/drivers/clk/microchip/clk-mpfs.c +++ b/drivers/clk/microchip/clk-mpfs.c @@ -30,6 +30,13 @@ #define MSSPLL_POSTDIV_WIDTH 0x07u #define MSSPLL_FIXED_DIV 4u +/* + * This clock ID is defined here, rather than the binding headers, as it is an + * internal clock only, and therefore has no consumers in other peripheral + * blocks. + */ +#define CLK_MSSPLL_INTERNAL 38u + struct mpfs_clock_data { struct device *dev; void __iomem *base; @@ -39,16 +46,26 @@ struct mpfs_clock_data { struct mpfs_msspll_hw_clock { void __iomem *base; + struct clk_hw hw; + struct clk_init_data init; unsigned int id; u32 reg_offset; u32 shift; u32 width; u32 flags; +}; + +#define to_mpfs_msspll_clk(_hw) container_of(_hw, struct mpfs_msspll_hw_clock, hw) + +struct mpfs_msspll_out_hw_clock { + void __iomem *base; struct clk_hw hw; struct clk_init_data init; + unsigned int id; + u32 flags; }; -#define to_mpfs_msspll_clk(_hw) container_of(_hw, struct mpfs_msspll_hw_clock, hw) +#define to_mpfs_msspll_out_clk(_hw) container_of(_hw, struct mpfs_msspll_out_hw_clock, hw) struct mpfs_cfg_hw_clock { struct clk_divider cfg; @@ -93,61 +110,99 @@ static const struct clk_div_table mpfs_div_rtcref_table[] = { { 0, 0 } }; +/* + * MSS PLL internal clock + */ + static unsigned long mpfs_clk_msspll_recalc_rate(struct clk_hw *hw, unsigned long prate) { struct mpfs_msspll_hw_clock *msspll_hw = to_mpfs_msspll_clk(hw); void __iomem *mult_addr = msspll_hw->base + msspll_hw->reg_offset; void __iomem *ref_div_addr = msspll_hw->base + REG_MSSPLL_REF_CR; - void __iomem *postdiv_addr = msspll_hw->base + REG_MSSPLL_POSTDIV_CR; - u32 mult, ref_div, postdiv; + u32 mult, ref_div; mult = readl_relaxed(mult_addr) >> MSSPLL_FBDIV_SHIFT; mult &= clk_div_mask(MSSPLL_FBDIV_WIDTH); ref_div = readl_relaxed(ref_div_addr) >> MSSPLL_REFDIV_SHIFT; ref_div &= clk_div_mask(MSSPLL_REFDIV_WIDTH); - postdiv = readl_relaxed(postdiv_addr) >> MSSPLL_POSTDIV_SHIFT; - postdiv &= clk_div_mask(MSSPLL_POSTDIV_WIDTH); - return prate * mult / (ref_div * MSSPLL_FIXED_DIV * postdiv); + return prate * mult / (ref_div * MSSPLL_FIXED_DIV); } -static long mpfs_clk_msspll_round_rate(struct clk_hw *hw, unsigned long rate, unsigned long *prate) +static const struct clk_ops mpfs_clk_msspll_ops = { + .recalc_rate = mpfs_clk_msspll_recalc_rate, +}; + +#define CLK_PLL(_id, _name, _parent, _shift, _width, _flags, _offset) { \ + .id = _id, \ + .flags = _flags, \ + .shift = _shift, \ + .width = _width, \ + .reg_offset = _offset, \ + .hw.init = CLK_HW_INIT_PARENTS_DATA(_name, _parent, &mpfs_clk_msspll_ops, 0), \ +} + +static struct mpfs_msspll_hw_clock mpfs_msspll_clks[] = { + CLK_PLL(CLK_MSSPLL_INTERNAL, "clk_msspll_internal", mpfs_ext_ref, MSSPLL_FBDIV_SHIFT, + MSSPLL_FBDIV_WIDTH, 0, REG_MSSPLL_SSCG_2_CR), +}; + +static int mpfs_clk_register_mssplls(struct device *dev, struct mpfs_msspll_hw_clock *msspll_hws, + unsigned int num_clks, struct mpfs_clock_data *data) { - struct mpfs_msspll_hw_clock *msspll_hw = to_mpfs_msspll_clk(hw); - void __iomem *mult_addr = msspll_hw->base + msspll_hw->reg_offset; - void __iomem *ref_div_addr = msspll_hw->base + REG_MSSPLL_REF_CR; - u32 mult, ref_div; - unsigned long rate_before_ctrl; + unsigned int i; + int ret; - mult = readl_relaxed(mult_addr) >> MSSPLL_FBDIV_SHIFT; - mult &= clk_div_mask(MSSPLL_FBDIV_WIDTH); - ref_div = readl_relaxed(ref_div_addr) >> MSSPLL_REFDIV_SHIFT; - ref_div &= clk_div_mask(MSSPLL_REFDIV_WIDTH); + for (i = 0; i < num_clks; i++) { + struct mpfs_msspll_hw_clock *msspll_hw = &msspll_hws[i]; + + msspll_hw->base = data->msspll_base; + ret = devm_clk_hw_register(dev, &msspll_hw->hw); + if (ret) + return dev_err_probe(dev, ret, "failed to register msspll id: %d\n", + CLK_MSSPLL_INTERNAL); - rate_before_ctrl = rate * (ref_div * MSSPLL_FIXED_DIV) / mult; + data->hw_data.hws[msspll_hw->id] = &msspll_hw->hw; + } - return divider_round_rate(hw, rate_before_ctrl, prate, NULL, MSSPLL_POSTDIV_WIDTH, - msspll_hw->flags); + return 0; } -static int mpfs_clk_msspll_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long prate) +/* + * MSS PLL output clocks + */ + +static unsigned long mpfs_clk_msspll_out_recalc_rate(struct clk_hw *hw, unsigned long prate) { - struct mpfs_msspll_hw_clock *msspll_hw = to_mpfs_msspll_clk(hw); - void __iomem *mult_addr = msspll_hw->base + msspll_hw->reg_offset; - void __iomem *ref_div_addr = msspll_hw->base + REG_MSSPLL_REF_CR; - void __iomem *postdiv_addr = msspll_hw->base + REG_MSSPLL_POSTDIV_CR; - u32 mult, ref_div, postdiv; - int divider_setting; - unsigned long rate_before_ctrl, flags; + struct mpfs_msspll_out_hw_clock *msspll_out_hw = to_mpfs_msspll_out_clk(hw); + void __iomem *postdiv_addr = msspll_out_hw->base + REG_MSSPLL_POSTDIV_CR; + u32 postdiv; - mult = readl_relaxed(mult_addr) >> MSSPLL_FBDIV_SHIFT; - mult &= clk_div_mask(MSSPLL_FBDIV_WIDTH); - ref_div = readl_relaxed(ref_div_addr) >> MSSPLL_REFDIV_SHIFT; - ref_div &= clk_div_mask(MSSPLL_REFDIV_WIDTH); + postdiv = readl_relaxed(postdiv_addr) >> MSSPLL_POSTDIV_SHIFT; + postdiv &= clk_div_mask(MSSPLL_POSTDIV_WIDTH); - rate_before_ctrl = rate * (ref_div * MSSPLL_FIXED_DIV) / mult; - divider_setting = divider_get_val(rate_before_ctrl, prate, NULL, MSSPLL_POSTDIV_WIDTH, - msspll_hw->flags); + return prate / postdiv; +} + +static long mpfs_clk_msspll_out_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + struct mpfs_msspll_out_hw_clock *msspll_out_hw = to_mpfs_msspll_out_clk(hw); + + return divider_round_rate(hw, rate, prate, NULL, MSSPLL_POSTDIV_WIDTH, + msspll_out_hw->flags); +} + +static int mpfs_clk_msspll_out_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long prate) +{ + struct mpfs_msspll_out_hw_clock *msspll_out_hw = to_mpfs_msspll_out_clk(hw); + void __iomem *postdiv_addr = msspll_out_hw->base + REG_MSSPLL_POSTDIV_CR; + u32 postdiv; + int divider_setting; + unsigned long flags; + + divider_setting = divider_get_val(rate, prate, NULL, MSSPLL_POSTDIV_WIDTH, + msspll_out_hw->flags); if (divider_setting < 0) return divider_setting; @@ -163,42 +218,39 @@ static int mpfs_clk_msspll_set_rate(struct clk_hw *hw, unsigned long rate, unsig return 0; } -static const struct clk_ops mpfs_clk_msspll_ops = { - .recalc_rate = mpfs_clk_msspll_recalc_rate, - .round_rate = mpfs_clk_msspll_round_rate, - .set_rate = mpfs_clk_msspll_set_rate, +static const struct clk_ops mpfs_clk_msspll_out_ops = { + .recalc_rate = mpfs_clk_msspll_out_recalc_rate, + .round_rate = mpfs_clk_msspll_out_round_rate, + .set_rate = mpfs_clk_msspll_out_set_rate, }; -#define CLK_PLL(_id, _name, _parent, _shift, _width, _flags, _offset) { \ - .id = _id, \ - .shift = _shift, \ - .width = _width, \ - .reg_offset = _offset, \ - .flags = _flags, \ - .hw.init = CLK_HW_INIT_PARENTS_DATA(_name, _parent, &mpfs_clk_msspll_ops, 0), \ +#define CLK_PLL_OUT(_id, _name, _parent, _flags) { \ + .id = _id, \ + .flags = _flags, \ + .hw.init = CLK_HW_INIT(_name, _parent, &mpfs_clk_msspll_out_ops, 0), \ } -static struct mpfs_msspll_hw_clock mpfs_msspll_clks[] = { - CLK_PLL(CLK_MSSPLL, "clk_msspll", mpfs_ext_ref, MSSPLL_FBDIV_SHIFT, - MSSPLL_FBDIV_WIDTH, 0, REG_MSSPLL_SSCG_2_CR), +static struct mpfs_msspll_out_hw_clock mpfs_msspll_out_clks[] = { + CLK_PLL_OUT(CLK_MSSPLL0, "clk_msspll", "clk_msspll_internal", 0), }; -static int mpfs_clk_register_mssplls(struct device *dev, struct mpfs_msspll_hw_clock *msspll_hws, - unsigned int num_clks, struct mpfs_clock_data *data) +static int mpfs_clk_register_msspll_outs(struct device *dev, + struct mpfs_msspll_out_hw_clock *msspll_out_hws, + unsigned int num_clks, struct mpfs_clock_data *data) { unsigned int i; int ret; for (i = 0; i < num_clks; i++) { - struct mpfs_msspll_hw_clock *msspll_hw = &msspll_hws[i]; + struct mpfs_msspll_out_hw_clock *msspll_out_hw = &msspll_out_hws[i]; - msspll_hw->base = data->msspll_base; - ret = devm_clk_hw_register(dev, &msspll_hw->hw); + msspll_out_hw->base = data->msspll_base; + ret = devm_clk_hw_register(dev, &msspll_out_hw->hw); if (ret) - return dev_err_probe(dev, ret, "failed to register msspll id: %d\n", - CLK_MSSPLL); + return dev_err_probe(dev, ret, "failed to register msspll out id: %d\n", + msspll_out_hw->id); - data->hw_data.hws[msspll_hw->id] = &msspll_hw->hw; + data->hw_data.hws[msspll_out_hw->id] = &msspll_out_hw->hw; } return 0; @@ -442,8 +494,8 @@ static int mpfs_clk_probe(struct platform_device *pdev) int ret; /* CLK_RESERVED is not part of clock arrays, so add 1 */ - num_clks = ARRAY_SIZE(mpfs_msspll_clks) + ARRAY_SIZE(mpfs_cfg_clks) - + ARRAY_SIZE(mpfs_periph_clks) + 1; + num_clks = ARRAY_SIZE(mpfs_msspll_clks) + ARRAY_SIZE(mpfs_msspll_out_clks) + + ARRAY_SIZE(mpfs_cfg_clks) + ARRAY_SIZE(mpfs_periph_clks) + 1; clk_data = devm_kzalloc(dev, struct_size(clk_data, hw_data.hws, num_clks), GFP_KERNEL); if (!clk_data) @@ -466,6 +518,12 @@ static int mpfs_clk_probe(struct platform_device *pdev) if (ret) return ret; + ret = mpfs_clk_register_msspll_outs(dev, mpfs_msspll_out_clks, + ARRAY_SIZE(mpfs_msspll_out_clks), + clk_data); + if (ret) + return ret; + ret = mpfs_clk_register_cfgs(dev, mpfs_cfg_clks, ARRAY_SIZE(mpfs_cfg_clks), clk_data); if (ret) return ret; |