Skip to content

Commit 445c2da

Browse files
ConchuODbebarino
authored andcommitted
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: 635e5e7 ("clk: microchip: Add driver for Microchip PolarFire SoC") Reviewed-by: Daire McNamara <[email protected]> Signed-off-by: Conor Dooley <[email protected]> Link: https://lore.kernel.org/r/[email protected] Acked-by: Palmer Dabbelt <[email protected]> Signed-off-by: Stephen Boyd <[email protected]>
1 parent 8e8fbab commit 445c2da

File tree

1 file changed

+132
-19
lines changed

1 file changed

+132
-19
lines changed

drivers/clk/microchip/clk-mpfs.c

Lines changed: 132 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -11,20 +11,47 @@
1111
#include <dt-bindings/clock/microchip,mpfs-clock.h>
1212

1313
/* 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
1417
#define REG_CLOCK_CONFIG_CR 0x08u
1518
#define REG_SUBBLK_CLOCK_CR 0x84u
1619
#define REG_SUBBLK_RESET_CR 0x88u
1720

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+
1829
struct mpfs_clock_data {
1930
void __iomem *base;
31+
void __iomem *msspll_base;
2032
struct clk_hw_onecell_data hw_data;
2133
};
2234

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)
47+
2348
struct mpfs_cfg_clock {
2449
const struct clk_div_table *table;
2550
unsigned int id;
51+
u32 reg_offset;
2652
u8 shift;
2753
u8 width;
54+
u8 flags;
2855
};
2956

3057
struct mpfs_cfg_hw_clock {
@@ -55,7 +82,7 @@ struct mpfs_periph_hw_clock {
5582
*/
5683
static DEFINE_SPINLOCK(mpfs_clk_lock);
5784

58-
static const struct clk_parent_data mpfs_cfg_parent[] = {
85+
static const struct clk_parent_data mpfs_ext_ref[] = {
5986
{ .index = 0 },
6087
};
6188

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

99+
static unsigned long mpfs_clk_msspll_recalc_rate(struct clk_hw *hw, unsigned long prate)
100+
{
101+
struct mpfs_msspll_hw_clock *msspll_hw = to_mpfs_msspll_clk(hw);
102+
void __iomem *mult_addr = msspll_hw->base + msspll_hw->reg_offset;
103+
void __iomem *ref_div_addr = msspll_hw->base + REG_MSSPLL_REF_CR;
104+
void __iomem *postdiv_addr = msspll_hw->base + REG_MSSPLL_POSTDIV_CR;
105+
u32 mult, ref_div, postdiv;
106+
107+
mult = readl_relaxed(mult_addr) >> MSSPLL_FBDIV_SHIFT;
108+
mult &= clk_div_mask(MSSPLL_FBDIV_WIDTH);
109+
ref_div = readl_relaxed(ref_div_addr) >> MSSPLL_REFDIV_SHIFT;
110+
ref_div &= clk_div_mask(MSSPLL_REFDIV_WIDTH);
111+
postdiv = readl_relaxed(postdiv_addr) >> MSSPLL_POSTDIV_SHIFT;
112+
postdiv &= clk_div_mask(MSSPLL_POSTDIV_WIDTH);
113+
114+
return prate * mult / (ref_div * MSSPLL_FIXED_DIV * postdiv);
115+
}
116+
117+
static const struct clk_ops mpfs_clk_msspll_ops = {
118+
.recalc_rate = mpfs_clk_msspll_recalc_rate,
119+
};
120+
121+
#define CLK_PLL(_id, _name, _parent, _shift, _width, _flags, _offset) { \
122+
.id = _id, \
123+
.shift = _shift, \
124+
.width = _width, \
125+
.reg_offset = _offset, \
126+
.flags = _flags, \
127+
.hw.init = CLK_HW_INIT_PARENTS_DATA(_name, _parent, &mpfs_clk_msspll_ops, 0), \
128+
}
129+
130+
static struct mpfs_msspll_hw_clock mpfs_msspll_clks[] = {
131+
CLK_PLL(CLK_MSSPLL, "clk_msspll", mpfs_ext_ref, MSSPLL_FBDIV_SHIFT,
132+
MSSPLL_FBDIV_WIDTH, 0, REG_MSSPLL_SSCG_2_CR),
133+
};
134+
135+
static int mpfs_clk_register_msspll(struct device *dev, struct mpfs_msspll_hw_clock *msspll_hw,
136+
void __iomem *base)
137+
{
138+
msspll_hw->base = base;
139+
140+
return devm_clk_hw_register(dev, &msspll_hw->hw);
141+
}
142+
143+
static int mpfs_clk_register_mssplls(struct device *dev, struct mpfs_msspll_hw_clock *msspll_hws,
144+
unsigned int num_clks, struct mpfs_clock_data *data)
145+
{
146+
void __iomem *base = data->msspll_base;
147+
unsigned int i;
148+
int ret;
149+
150+
for (i = 0; i < num_clks; i++) {
151+
struct mpfs_msspll_hw_clock *msspll_hw = &msspll_hws[i];
152+
153+
ret = mpfs_clk_register_msspll(dev, msspll_hw, base);
154+
if (ret)
155+
return dev_err_probe(dev, ret, "failed to register msspll id: %d\n",
156+
CLK_MSSPLL);
157+
158+
data->hw_data.hws[msspll_hw->id] = &msspll_hw->hw;
159+
}
160+
161+
return 0;
162+
}
163+
164+
/*
165+
* "CFG" clocks
166+
*/
167+
72168
static unsigned long mpfs_cfg_clk_recalc_rate(struct clk_hw *hw, unsigned long prate)
73169
{
74170
struct mpfs_cfg_hw_clock *cfg_hw = to_mpfs_cfg_clk(hw);
75171
struct mpfs_cfg_clock *cfg = &cfg_hw->cfg;
76172
void __iomem *base_addr = cfg_hw->sys_base;
77173
u32 val;
78174

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

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

85181
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
105201
return divider_setting;
106202

107203
spin_lock_irqsave(&mpfs_clk_lock, flags);
108-
109-
val = readl_relaxed(base_addr + REG_CLOCK_CONFIG_CR);
204+
val = readl_relaxed(base_addr + cfg->reg_offset);
110205
val &= ~(clk_div_mask(cfg->width) << cfg_hw->cfg.shift);
111206
val |= divider_setting << cfg->shift;
112-
writel_relaxed(val, base_addr + REG_CLOCK_CONFIG_CR);
207+
writel_relaxed(val, base_addr + cfg->reg_offset);
113208

114209
spin_unlock_irqrestore(&mpfs_clk_lock, flags);
115210

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

125-
#define CLK_CFG(_id, _name, _parent, _shift, _width, _table, _flags) { \
126-
.cfg.id = _id, \
127-
.cfg.shift = _shift, \
128-
.cfg.width = _width, \
129-
.cfg.table = _table, \
130-
.hw.init = CLK_HW_INIT_PARENTS_DATA(_name, _parent, &mpfs_clk_cfg_ops, \
131-
_flags), \
220+
#define CLK_CFG(_id, _name, _parent, _shift, _width, _table, _flags, _offset) { \
221+
.cfg.id = _id, \
222+
.cfg.shift = _shift, \
223+
.cfg.width = _width, \
224+
.cfg.table = _table, \
225+
.cfg.reg_offset = _offset, \
226+
.cfg.flags = _flags, \
227+
.hw.init = CLK_HW_INIT(_name, _parent, &mpfs_clk_cfg_ops, 0), \
132228
}
133229

134230
static struct mpfs_cfg_hw_clock mpfs_cfg_clks[] = {
135-
CLK_CFG(CLK_CPU, "clk_cpu", mpfs_cfg_parent, 0, 2, mpfs_div_cpu_axi_table, 0),
136-
CLK_CFG(CLK_AXI, "clk_axi", mpfs_cfg_parent, 2, 2, mpfs_div_cpu_axi_table, 0),
137-
CLK_CFG(CLK_AHB, "clk_ahb", mpfs_cfg_parent, 4, 2, mpfs_div_ahb_table, 0),
231+
CLK_CFG(CLK_CPU, "clk_cpu", "clk_msspll", 0, 2, mpfs_div_cpu_axi_table, 0,
232+
REG_CLOCK_CONFIG_CR),
233+
CLK_CFG(CLK_AXI, "clk_axi", "clk_msspll", 2, 2, mpfs_div_cpu_axi_table, 0,
234+
REG_CLOCK_CONFIG_CR),
235+
CLK_CFG(CLK_AHB, "clk_ahb", "clk_msspll", 4, 2, mpfs_div_ahb_table, 0,
236+
REG_CLOCK_CONFIG_CR),
138237
};
139238

140239
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 *
160259
return dev_err_probe(dev, ret, "failed to register clock id: %d\n",
161260
cfg_hw->cfg.id);
162261

163-
id = cfg_hws[i].cfg.id;
262+
id = cfg_hw->cfg.id;
164263
data->hw_data.hws[id] = &cfg_hw->hw;
165264
}
166265

167266
return 0;
168267
}
169268

269+
/*
270+
* peripheral clocks - devices connected to axi or ahb buses.
271+
*/
272+
170273
static int mpfs_periph_clk_enable(struct clk_hw *hw)
171274
{
172275
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)
320423
unsigned int num_clks;
321424
int ret;
322425

323-
/* CLK_RESERVED is not part of cfg_clks nor periph_clks, so add 1 */
324-
num_clks = ARRAY_SIZE(mpfs_cfg_clks) + ARRAY_SIZE(mpfs_periph_clks) + 1;
426+
/* CLK_RESERVED is not part of clock arrays, so add 1 */
427+
num_clks = ARRAY_SIZE(mpfs_msspll_clks) + ARRAY_SIZE(mpfs_cfg_clks)
428+
+ ARRAY_SIZE(mpfs_periph_clks) + 1;
325429

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

438+
clk_data->msspll_base = devm_platform_ioremap_resource(pdev, 1);
439+
if (IS_ERR(clk_data->msspll_base))
440+
return PTR_ERR(clk_data->msspll_base);
441+
334442
clk_data->hw_data.num = num_clks;
335443

444+
ret = mpfs_clk_register_mssplls(dev, mpfs_msspll_clks, ARRAY_SIZE(mpfs_msspll_clks),
445+
clk_data);
446+
if (ret)
447+
return ret;
448+
336449
ret = mpfs_clk_register_cfgs(dev, mpfs_cfg_clks, ARRAY_SIZE(mpfs_cfg_clks), clk_data);
337450
if (ret)
338451
return ret;

0 commit comments

Comments
 (0)