Skip to content

Commit

Permalink
clk: microchip: mpfs: re-parent the configurable clocks
Browse files Browse the repository at this point in the history
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: 635e5e7 ("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>
  • Loading branch information
Conor Dooley authored and Stephen Boyd committed Apr 23, 2022
1 parent 8e8fbab commit 445c2da
Showing 1 changed file with 132 additions and 19 deletions.
151 changes: 132 additions & 19 deletions drivers/clk/microchip/clk-mpfs.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,47 @@
#include <dt-bindings/clock/microchip,mpfs-clock.h>

/* address offset of control registers */
#define REG_MSSPLL_REF_CR 0x08u
#define REG_MSSPLL_POSTDIV_CR 0x10u
#define REG_MSSPLL_SSCG_2_CR 0x2Cu
#define REG_CLOCK_CONFIG_CR 0x08u
#define REG_SUBBLK_CLOCK_CR 0x84u
#define REG_SUBBLK_RESET_CR 0x88u

#define MSSPLL_FBDIV_SHIFT 0x00u
#define MSSPLL_FBDIV_WIDTH 0x0Cu
#define MSSPLL_REFDIV_SHIFT 0x08u
#define MSSPLL_REFDIV_WIDTH 0x06u
#define MSSPLL_POSTDIV_SHIFT 0x08u
#define MSSPLL_POSTDIV_WIDTH 0x07u
#define MSSPLL_FIXED_DIV 4u

struct mpfs_clock_data {
void __iomem *base;
void __iomem *msspll_base;
struct clk_hw_onecell_data hw_data;
};

struct mpfs_msspll_hw_clock {
void __iomem *base;
unsigned int id;
u32 reg_offset;
u32 shift;
u32 width;
u32 flags;
struct clk_hw hw;
struct clk_init_data init;
};

#define to_mpfs_msspll_clk(_hw) container_of(_hw, struct mpfs_msspll_hw_clock, hw)

struct mpfs_cfg_clock {
const struct clk_div_table *table;
unsigned int id;
u32 reg_offset;
u8 shift;
u8 width;
u8 flags;
};

struct mpfs_cfg_hw_clock {
Expand Down Expand Up @@ -55,7 +82,7 @@ struct mpfs_periph_hw_clock {
*/
static DEFINE_SPINLOCK(mpfs_clk_lock);

static const struct clk_parent_data mpfs_cfg_parent[] = {
static const struct clk_parent_data mpfs_ext_ref[] = {
{ .index = 0 },
};

Expand All @@ -69,17 +96,86 @@ static const struct clk_div_table mpfs_div_ahb_table[] = {
{ 0, 0 }
};

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;

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);
}

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, \
.shift = _shift, \
.width = _width, \
.reg_offset = _offset, \
.flags = _flags, \
.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, "clk_msspll", mpfs_ext_ref, MSSPLL_FBDIV_SHIFT,
MSSPLL_FBDIV_WIDTH, 0, REG_MSSPLL_SSCG_2_CR),
};

static int mpfs_clk_register_msspll(struct device *dev, struct mpfs_msspll_hw_clock *msspll_hw,
void __iomem *base)
{
msspll_hw->base = base;

return devm_clk_hw_register(dev, &msspll_hw->hw);
}

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)
{
void __iomem *base = data->msspll_base;
unsigned int i;
int ret;

for (i = 0; i < num_clks; i++) {
struct mpfs_msspll_hw_clock *msspll_hw = &msspll_hws[i];

ret = mpfs_clk_register_msspll(dev, msspll_hw, base);
if (ret)
return dev_err_probe(dev, ret, "failed to register msspll id: %d\n",
CLK_MSSPLL);

data->hw_data.hws[msspll_hw->id] = &msspll_hw->hw;
}

return 0;
}

/*
* "CFG" clocks
*/

static unsigned long mpfs_cfg_clk_recalc_rate(struct clk_hw *hw, unsigned long prate)
{
struct mpfs_cfg_hw_clock *cfg_hw = to_mpfs_cfg_clk(hw);
struct mpfs_cfg_clock *cfg = &cfg_hw->cfg;
void __iomem *base_addr = cfg_hw->sys_base;
u32 val;

val = readl_relaxed(base_addr + REG_CLOCK_CONFIG_CR) >> cfg->shift;
val = readl_relaxed(base_addr + cfg->reg_offset) >> cfg->shift;
val &= clk_div_mask(cfg->width);

return prate / (1u << val);
return divider_recalc_rate(hw, prate, val, cfg->table, cfg->flags, cfg->width);
}

static long mpfs_cfg_clk_round_rate(struct clk_hw *hw, unsigned long rate, unsigned long *prate)
Expand All @@ -105,11 +201,10 @@ static int mpfs_cfg_clk_set_rate(struct clk_hw *hw, unsigned long rate, unsigned
return divider_setting;

spin_lock_irqsave(&mpfs_clk_lock, flags);

val = readl_relaxed(base_addr + REG_CLOCK_CONFIG_CR);
val = readl_relaxed(base_addr + cfg->reg_offset);
val &= ~(clk_div_mask(cfg->width) << cfg_hw->cfg.shift);
val |= divider_setting << cfg->shift;
writel_relaxed(val, base_addr + REG_CLOCK_CONFIG_CR);
writel_relaxed(val, base_addr + cfg->reg_offset);

spin_unlock_irqrestore(&mpfs_clk_lock, flags);

Expand All @@ -122,19 +217,23 @@ static const struct clk_ops mpfs_clk_cfg_ops = {
.set_rate = mpfs_cfg_clk_set_rate,
};

#define CLK_CFG(_id, _name, _parent, _shift, _width, _table, _flags) { \
.cfg.id = _id, \
.cfg.shift = _shift, \
.cfg.width = _width, \
.cfg.table = _table, \
.hw.init = CLK_HW_INIT_PARENTS_DATA(_name, _parent, &mpfs_clk_cfg_ops, \
_flags), \
#define CLK_CFG(_id, _name, _parent, _shift, _width, _table, _flags, _offset) { \
.cfg.id = _id, \
.cfg.shift = _shift, \
.cfg.width = _width, \
.cfg.table = _table, \
.cfg.reg_offset = _offset, \
.cfg.flags = _flags, \
.hw.init = CLK_HW_INIT(_name, _parent, &mpfs_clk_cfg_ops, 0), \
}

static struct mpfs_cfg_hw_clock mpfs_cfg_clks[] = {
CLK_CFG(CLK_CPU, "clk_cpu", mpfs_cfg_parent, 0, 2, mpfs_div_cpu_axi_table, 0),
CLK_CFG(CLK_AXI, "clk_axi", mpfs_cfg_parent, 2, 2, mpfs_div_cpu_axi_table, 0),
CLK_CFG(CLK_AHB, "clk_ahb", mpfs_cfg_parent, 4, 2, mpfs_div_ahb_table, 0),
CLK_CFG(CLK_CPU, "clk_cpu", "clk_msspll", 0, 2, mpfs_div_cpu_axi_table, 0,
REG_CLOCK_CONFIG_CR),
CLK_CFG(CLK_AXI, "clk_axi", "clk_msspll", 2, 2, mpfs_div_cpu_axi_table, 0,
REG_CLOCK_CONFIG_CR),
CLK_CFG(CLK_AHB, "clk_ahb", "clk_msspll", 4, 2, mpfs_div_ahb_table, 0,
REG_CLOCK_CONFIG_CR),
};

static int mpfs_clk_register_cfg(struct device *dev, struct mpfs_cfg_hw_clock *cfg_hw,
Expand All @@ -160,13 +259,17 @@ static int mpfs_clk_register_cfgs(struct device *dev, struct mpfs_cfg_hw_clock *
return dev_err_probe(dev, ret, "failed to register clock id: %d\n",
cfg_hw->cfg.id);

id = cfg_hws[i].cfg.id;
id = cfg_hw->cfg.id;
data->hw_data.hws[id] = &cfg_hw->hw;
}

return 0;
}

/*
* peripheral clocks - devices connected to axi or ahb buses.
*/

static int mpfs_periph_clk_enable(struct clk_hw *hw)
{
struct mpfs_periph_hw_clock *periph_hw = to_mpfs_periph_clk(hw);
Expand Down Expand Up @@ -320,8 +423,9 @@ static int mpfs_clk_probe(struct platform_device *pdev)
unsigned int num_clks;
int ret;

/* CLK_RESERVED is not part of cfg_clks nor periph_clks, so add 1 */
num_clks = ARRAY_SIZE(mpfs_cfg_clks) + ARRAY_SIZE(mpfs_periph_clks) + 1;
/* 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;

clk_data = devm_kzalloc(dev, struct_size(clk_data, hw_data.hws, num_clks), GFP_KERNEL);
if (!clk_data)
Expand All @@ -331,8 +435,17 @@ static int mpfs_clk_probe(struct platform_device *pdev)
if (IS_ERR(clk_data->base))
return PTR_ERR(clk_data->base);

clk_data->msspll_base = devm_platform_ioremap_resource(pdev, 1);
if (IS_ERR(clk_data->msspll_base))
return PTR_ERR(clk_data->msspll_base);

clk_data->hw_data.num = num_clks;

ret = mpfs_clk_register_mssplls(dev, mpfs_msspll_clks, ARRAY_SIZE(mpfs_msspll_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;
Expand Down

0 comments on commit 445c2da

Please sign in to comment.