mirror of
https://github.com/Fishwaldo/linux-bl808.git
synced 2025-03-17 12:34:01 +00:00
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: 635e5e7337
("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>
This commit is contained in:
parent
8e8fbab4f1
commit
445c2da897
1 changed files with 132 additions and 19 deletions
|
@ -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 {
|
||||
|
@ -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 },
|
||||
};
|
||||
|
||||
|
@ -69,6 +96,75 @@ 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);
|
||||
|
@ -76,10 +172,10 @@ static unsigned long mpfs_cfg_clk_recalc_rate(struct clk_hw *hw, unsigned long p
|
|||
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)
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
|
Loading…
Add table
Reference in a new issue