1 From e76d2af5009f52aa02d3db7ae32d150ad66398f9 Mon Sep 17 00:00:00 2001
2 From: Claudiu Beznea <claudiu.beznea@microchip.com>
3 Date: Mon, 11 Oct 2021 14:27:15 +0300
4 Subject: [PATCH 243/247] clk: at91: clk-sam9x60-pll: add notifier for div part
7 SAM9X60's PLL which is also part of SAMA7G5 is composed of 2 parts:
8 one fractional part and one divider. On SAMA7G5 the CPU PLL could be
9 changed at run-time to implement DVFS. The hardware clock tree on
10 SAMA7G5 for CPU PLL is as follows:
12 +---- div1 ----------------> cpuck
14 FRAC PLL ---> DIV PLL -+-> prescaler ---> div0 ---> mck0
16 The div1 block is not implemented in Linux; on prescaler block it has
17 been discovered a bug on some scenarios and will be removed from Linux
18 in next commits. Thus, the final clock tree that will be used in Linux
23 FRAC PLL ---> DIV PLL -+-> div0 ---> mck0
25 It has been proposed in [1] to not introduce a new CPUFreq driver but
26 to overload the proper clock drivers with proper operation such that
27 cpufreq-dt to be used. To accomplish this DIV PLL and div0 implement
28 clock notifiers which applies safe dividers before FRAC PLL is changed.
29 The current commit treats only the DIV PLL by adding a notifier that
30 sets a safe divider on PRE_RATE_CHANGE events. The safe divider is
31 provided by initialization clock code (sama7g5.c). The div0 is treated
32 in next commits (to keep the changes as clean as possible).
34 [1] https://lore.kernel.org/lkml/20210105104426.4tmgc2l3vyicwedd@vireshk-i7/
36 Signed-off-by: Claudiu Beznea <claudiu.beznea@microchip.com>
37 Link: https://lore.kernel.org/r/20211011112719.3951784-12-claudiu.beznea@microchip.com
38 Acked-by: Nicolas Ferre <nicolas.ferre@microchip.com>
39 Signed-off-by: Stephen Boyd <sboyd@kernel.org>
41 drivers/clk/at91/clk-sam9x60-pll.c | 102 ++++++++++++++++++++++-------
42 drivers/clk/at91/pmc.h | 3 +-
43 drivers/clk/at91/sam9x60.c | 6 +-
44 drivers/clk/at91/sama7g5.c | 13 +++-
45 4 files changed, 95 insertions(+), 29 deletions(-)
47 diff --git a/drivers/clk/at91/clk-sam9x60-pll.c b/drivers/clk/at91/clk-sam9x60-pll.c
48 index a73d7c96ce1d..d757003004cb 100644
49 --- a/drivers/clk/at91/clk-sam9x60-pll.c
50 +++ b/drivers/clk/at91/clk-sam9x60-pll.c
54 #include <linux/bitfield.h>
55 +#include <linux/clk.h>
56 #include <linux/clk-provider.h>
57 #include <linux/clkdev.h>
58 #include <linux/clk/at91_pmc.h>
59 @@ -47,12 +48,15 @@ struct sam9x60_div {
60 struct sam9x60_pll_core core;
61 struct at91_clk_pms pms;
66 #define to_sam9x60_pll_core(hw) container_of(hw, struct sam9x60_pll_core, hw)
67 #define to_sam9x60_frac(core) container_of(core, struct sam9x60_frac, core)
68 #define to_sam9x60_div(core) container_of(core, struct sam9x60_div, core)
70 +static struct sam9x60_div *notifier_div;
72 static inline bool sam9x60_pll_ready(struct regmap *regmap, int id)
75 @@ -329,6 +333,26 @@ static const struct clk_ops sam9x60_frac_pll_ops_chg = {
76 .restore_context = sam9x60_frac_pll_restore_context,
79 +/* This function should be called with spinlock acquired. */
80 +static void sam9x60_div_pll_set_div(struct sam9x60_pll_core *core, u32 div,
83 + struct regmap *regmap = core->regmap;
84 + u32 ena_msk = enable ? core->layout->endiv_mask : 0;
85 + u32 ena_val = enable ? (1 << core->layout->endiv_shift) : 0;
87 + regmap_update_bits(regmap, AT91_PMC_PLL_CTRL0,
88 + core->layout->div_mask | ena_msk,
89 + (div << core->layout->div_shift) | ena_val);
91 + regmap_update_bits(regmap, AT91_PMC_PLL_UPDT,
92 + AT91_PMC_PLL_UPDT_UPDATE | AT91_PMC_PLL_UPDT_ID_MSK,
93 + AT91_PMC_PLL_UPDT_UPDATE | core->id);
95 + while (!sam9x60_pll_ready(regmap, core->id))
99 static int sam9x60_div_pll_set(struct sam9x60_pll_core *core)
101 struct sam9x60_div *div = to_sam9x60_div(core);
102 @@ -346,17 +370,7 @@ static int sam9x60_div_pll_set(struct sam9x60_pll_core *core)
103 if (!!(val & core->layout->endiv_mask) && cdiv == div->div)
106 - regmap_update_bits(regmap, AT91_PMC_PLL_CTRL0,
107 - core->layout->div_mask | core->layout->endiv_mask,
108 - (div->div << core->layout->div_shift) |
109 - (1 << core->layout->endiv_shift));
111 - regmap_update_bits(regmap, AT91_PMC_PLL_UPDT,
112 - AT91_PMC_PLL_UPDT_UPDATE | AT91_PMC_PLL_UPDT_ID_MSK,
113 - AT91_PMC_PLL_UPDT_UPDATE | core->id);
115 - while (!sam9x60_pll_ready(regmap, core->id))
117 + sam9x60_div_pll_set_div(core, div->div, 1);
120 spin_unlock_irqrestore(core->lock, flags);
121 @@ -502,16 +516,7 @@ static int sam9x60_div_pll_set_rate_chg(struct clk_hw *hw, unsigned long rate,
122 if (cdiv == div->div)
125 - regmap_update_bits(regmap, AT91_PMC_PLL_CTRL0,
126 - core->layout->div_mask,
127 - (div->div << core->layout->div_shift));
129 - regmap_update_bits(regmap, AT91_PMC_PLL_UPDT,
130 - AT91_PMC_PLL_UPDT_UPDATE | AT91_PMC_PLL_UPDT_ID_MSK,
131 - AT91_PMC_PLL_UPDT_UPDATE | core->id);
133 - while (!sam9x60_pll_ready(regmap, core->id))
135 + sam9x60_div_pll_set_div(core, div->div, 0);
138 spin_unlock_irqrestore(core->lock, irqflags);
139 @@ -538,6 +543,48 @@ static void sam9x60_div_pll_restore_context(struct clk_hw *hw)
140 sam9x60_div_pll_set(core);
143 +static int sam9x60_div_pll_notifier_fn(struct notifier_block *notifier,
144 + unsigned long code, void *data)
146 + struct sam9x60_div *div = notifier_div;
147 + struct sam9x60_pll_core core = div->core;
148 + struct regmap *regmap = core.regmap;
149 + unsigned long irqflags;
151 + int ret = NOTIFY_DONE;
153 + if (code != PRE_RATE_CHANGE)
157 + * We switch to safe divider to avoid overclocking of other domains
158 + * feed by us while the frac PLL (our parent) is changed.
160 + div->div = div->safe_div;
162 + spin_lock_irqsave(core.lock, irqflags);
163 + regmap_update_bits(regmap, AT91_PMC_PLL_UPDT, AT91_PMC_PLL_UPDT_ID_MSK,
165 + regmap_read(regmap, AT91_PMC_PLL_CTRL0, &val);
166 + cdiv = (val & core.layout->div_mask) >> core.layout->div_shift;
168 + /* Stop if nothing changed. */
169 + if (cdiv == div->safe_div)
172 + sam9x60_div_pll_set_div(&core, div->div, 0);
176 + spin_unlock_irqrestore(core.lock, irqflags);
181 +static struct notifier_block sam9x60_div_pll_notifier = {
182 + .notifier_call = sam9x60_div_pll_notifier_fn,
185 static const struct clk_ops sam9x60_div_pll_ops = {
186 .prepare = sam9x60_div_pll_prepare,
187 .unprepare = sam9x60_div_pll_unprepare,
188 @@ -647,7 +694,8 @@ struct clk_hw * __init
189 sam9x60_clk_register_div_pll(struct regmap *regmap, spinlock_t *lock,
190 const char *name, const char *parent_name, u8 id,
191 const struct clk_pll_characteristics *characteristics,
192 - const struct clk_pll_layout *layout, u32 flags)
193 + const struct clk_pll_layout *layout, u32 flags,
196 struct sam9x60_div *div;
198 @@ -656,9 +704,13 @@ sam9x60_clk_register_div_pll(struct regmap *regmap, spinlock_t *lock,
202 - if (id > PLL_MAX_ID || !lock)
203 + /* We only support one changeable PLL. */
204 + if (id > PLL_MAX_ID || !lock || (safe_div && notifier_div))
205 return ERR_PTR(-EINVAL);
207 + if (safe_div >= PLL_DIV_MAX)
208 + safe_div = PLL_DIV_MAX - 1;
210 div = kzalloc(sizeof(*div), GFP_KERNEL);
212 return ERR_PTR(-ENOMEM);
213 @@ -678,6 +730,7 @@ sam9x60_clk_register_div_pll(struct regmap *regmap, spinlock_t *lock,
214 div->core.layout = layout;
215 div->core.regmap = regmap;
216 div->core.lock = lock;
217 + div->safe_div = safe_div;
219 spin_lock_irqsave(div->core.lock, irqflags);
221 @@ -693,6 +746,9 @@ sam9x60_clk_register_div_pll(struct regmap *regmap, spinlock_t *lock,
225 + } else if (div->safe_div) {
226 + notifier_div = div;
227 + clk_notifier_register(hw->clk, &sam9x60_div_pll_notifier);
231 diff --git a/drivers/clk/at91/pmc.h b/drivers/clk/at91/pmc.h
232 index 45df094498ce..207ecccef29f 100644
233 --- a/drivers/clk/at91/pmc.h
234 +++ b/drivers/clk/at91/pmc.h
235 @@ -214,7 +214,8 @@ struct clk_hw * __init
236 sam9x60_clk_register_div_pll(struct regmap *regmap, spinlock_t *lock,
237 const char *name, const char *parent_name, u8 id,
238 const struct clk_pll_characteristics *characteristics,
239 - const struct clk_pll_layout *layout, u32 flags);
240 + const struct clk_pll_layout *layout, u32 flags,
243 struct clk_hw * __init
244 sam9x60_clk_register_frac_pll(struct regmap *regmap, spinlock_t *lock,
245 diff --git a/drivers/clk/at91/sam9x60.c b/drivers/clk/at91/sam9x60.c
246 index 5f6fa89571b7..5c264185f261 100644
247 --- a/drivers/clk/at91/sam9x60.c
248 +++ b/drivers/clk/at91/sam9x60.c
249 @@ -242,7 +242,7 @@ static void __init sam9x60_pmc_setup(struct device_node *np)
250 * This feeds CPU. It should not
253 - CLK_IS_CRITICAL | CLK_SET_RATE_GATE);
254 + CLK_IS_CRITICAL | CLK_SET_RATE_GATE, 0);
258 @@ -260,7 +260,7 @@ static void __init sam9x60_pmc_setup(struct device_node *np)
261 CLK_SET_PARENT_GATE |
262 - CLK_SET_RATE_PARENT);
263 + CLK_SET_RATE_PARENT, 0);
267 @@ -279,7 +279,7 @@ static void __init sam9x60_pmc_setup(struct device_node *np)
268 hw = at91_clk_register_master_div(regmap, "masterck_div",
269 "masterck_pres", &sam9x60_master_layout,
270 &mck_characteristics, &mck_lock,
271 - CLK_SET_RATE_GATE);
272 + CLK_SET_RATE_GATE, 0);
276 diff --git a/drivers/clk/at91/sama7g5.c b/drivers/clk/at91/sama7g5.c
277 index 970135e19a75..ae52c10af040 100644
278 --- a/drivers/clk/at91/sama7g5.c
279 +++ b/drivers/clk/at91/sama7g5.c
280 @@ -127,6 +127,8 @@ static const struct clk_pll_characteristics pll_characteristics = {
283 * @eid: export index in sama7g5->chws[] array
284 + * @safe_div: intermediate divider need to be set on PRE_RATE_CHANGE
287 static const struct {
289 @@ -136,6 +138,7 @@ static const struct {
294 } sama7g5_plls[][PLL_ID_MAX] = {
296 { .n = "cpupll_fracck",
297 @@ -156,7 +159,12 @@ static const struct {
299 /* This feeds CPU. It should not be disabled. */
300 .f = CLK_IS_CRITICAL | CLK_SET_RATE_PARENT,
301 - .eid = PMC_CPUPLL, },
304 + * Safe div=15 should be safe even for switching b/w 1GHz and
305 + * 90MHz (frac pll might go up to 1.2GHz).
311 @@ -967,7 +975,8 @@ static void __init sama7g5_pmc_setup(struct device_node *np)
312 sama7g5_plls[i][j].p, i,
313 sama7g5_plls[i][j].c,
314 sama7g5_plls[i][j].l,
315 - sama7g5_plls[i][j].f);
316 + sama7g5_plls[i][j].f,
317 + sama7g5_plls[i][j].safe_div);