f101f71fcd8b4ead50d59589d888494aa3e13078
[openwrt/staging/dedeckeh.git] / target / linux / at91 / patches-5.10 / 243-clk-at91-clk-sam9x60-pll-add-notifier-for-div-part-o.patch
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
5 of PLL
6
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:
11
12 +---- div1 ----------------> cpuck
13 |
14 FRAC PLL ---> DIV PLL -+-> prescaler ---> div0 ---> mck0
15
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
19 will be as follows:
20
21 +-----------> cpuck
22 |
23 FRAC PLL ---> DIV PLL -+-> div0 ---> mck0
24
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).
33
34 [1] https://lore.kernel.org/lkml/20210105104426.4tmgc2l3vyicwedd@vireshk-i7/
35
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>
40 ---
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(-)
46
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
51 @@ -5,6 +5,7 @@
52 */
53
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;
62 u8 div;
63 + u8 safe_div;
64 };
65
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)
69
70 +static struct sam9x60_div *notifier_div;
71 +
72 static inline bool sam9x60_pll_ready(struct regmap *regmap, int id)
73 {
74 unsigned int status;
75 @@ -329,6 +333,26 @@ static const struct clk_ops sam9x60_frac_pll_ops_chg = {
76 .restore_context = sam9x60_frac_pll_restore_context,
77 };
78
79 +/* This function should be called with spinlock acquired. */
80 +static void sam9x60_div_pll_set_div(struct sam9x60_pll_core *core, u32 div,
81 + bool enable)
82 +{
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;
86 +
87 + regmap_update_bits(regmap, AT91_PMC_PLL_CTRL0,
88 + core->layout->div_mask | ena_msk,
89 + (div << core->layout->div_shift) | ena_val);
90 +
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);
94 +
95 + while (!sam9x60_pll_ready(regmap, core->id))
96 + cpu_relax();
97 +}
98 +
99 static int sam9x60_div_pll_set(struct sam9x60_pll_core *core)
100 {
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)
104 goto unlock;
105
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));
110 -
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);
114 -
115 - while (!sam9x60_pll_ready(regmap, core->id))
116 - cpu_relax();
117 + sam9x60_div_pll_set_div(core, div->div, 1);
118
119 unlock:
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)
123 goto unlock;
124
125 - regmap_update_bits(regmap, AT91_PMC_PLL_CTRL0,
126 - core->layout->div_mask,
127 - (div->div << core->layout->div_shift));
128 -
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);
132 -
133 - while (!sam9x60_pll_ready(regmap, core->id))
134 - cpu_relax();
135 + sam9x60_div_pll_set_div(core, div->div, 0);
136
137 unlock:
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);
141 }
142
143 +static int sam9x60_div_pll_notifier_fn(struct notifier_block *notifier,
144 + unsigned long code, void *data)
145 +{
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;
150 + u32 val, cdiv;
151 + int ret = NOTIFY_DONE;
152 +
153 + if (code != PRE_RATE_CHANGE)
154 + return ret;
155 +
156 + /*
157 + * We switch to safe divider to avoid overclocking of other domains
158 + * feed by us while the frac PLL (our parent) is changed.
159 + */
160 + div->div = div->safe_div;
161 +
162 + spin_lock_irqsave(core.lock, irqflags);
163 + regmap_update_bits(regmap, AT91_PMC_PLL_UPDT, AT91_PMC_PLL_UPDT_ID_MSK,
164 + core.id);
165 + regmap_read(regmap, AT91_PMC_PLL_CTRL0, &val);
166 + cdiv = (val & core.layout->div_mask) >> core.layout->div_shift;
167 +
168 + /* Stop if nothing changed. */
169 + if (cdiv == div->safe_div)
170 + goto unlock;
171 +
172 + sam9x60_div_pll_set_div(&core, div->div, 0);
173 + ret = NOTIFY_OK;
174 +
175 +unlock:
176 + spin_unlock_irqrestore(core.lock, irqflags);
177 +
178 + return ret;
179 +}
180 +
181 +static struct notifier_block sam9x60_div_pll_notifier = {
182 + .notifier_call = sam9x60_div_pll_notifier_fn,
183 +};
184 +
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,
194 + u32 safe_div)
195 {
196 struct sam9x60_div *div;
197 struct clk_hw *hw;
198 @@ -656,9 +704,13 @@ sam9x60_clk_register_div_pll(struct regmap *regmap, spinlock_t *lock,
199 unsigned int val;
200 int ret;
201
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);
206
207 + if (safe_div >= PLL_DIV_MAX)
208 + safe_div = PLL_DIV_MAX - 1;
209 +
210 div = kzalloc(sizeof(*div), GFP_KERNEL);
211 if (!div)
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;
218
219 spin_lock_irqsave(div->core.lock, irqflags);
220
221 @@ -693,6 +746,9 @@ sam9x60_clk_register_div_pll(struct regmap *regmap, spinlock_t *lock,
222 if (ret) {
223 kfree(div);
224 hw = ERR_PTR(ret);
225 + } else if (div->safe_div) {
226 + notifier_div = div;
227 + clk_notifier_register(hw->clk, &sam9x60_div_pll_notifier);
228 }
229
230 return hw;
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,
241 + u32 safe_div);
242
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
251 * be disabled.
252 */
253 - CLK_IS_CRITICAL | CLK_SET_RATE_GATE);
254 + CLK_IS_CRITICAL | CLK_SET_RATE_GATE, 0);
255 if (IS_ERR(hw))
256 goto err_free;
257
258 @@ -260,7 +260,7 @@ static void __init sam9x60_pmc_setup(struct device_node *np)
259 &pll_div_layout,
260 CLK_SET_RATE_GATE |
261 CLK_SET_PARENT_GATE |
262 - CLK_SET_RATE_PARENT);
263 + CLK_SET_RATE_PARENT, 0);
264 if (IS_ERR(hw))
265 goto err_free;
266
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);
273 if (IS_ERR(hw))
274 goto err_free;
275
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 = {
281 * @t: clock type
282 * @f: clock flags
283 * @eid: export index in sama7g5->chws[] array
284 + * @safe_div: intermediate divider need to be set on PRE_RATE_CHANGE
285 + * notification
286 */
287 static const struct {
288 const char *n;
289 @@ -136,6 +138,7 @@ static const struct {
290 unsigned long f;
291 u8 t;
292 u8 eid;
293 + u8 safe_div;
294 } sama7g5_plls[][PLL_ID_MAX] = {
295 [PLL_ID_CPU] = {
296 { .n = "cpupll_fracck",
297 @@ -156,7 +159,12 @@ static const struct {
298 .t = PLL_TYPE_DIV,
299 /* This feeds CPU. It should not be disabled. */
300 .f = CLK_IS_CRITICAL | CLK_SET_RATE_PARENT,
301 - .eid = PMC_CPUPLL, },
302 + .eid = PMC_CPUPLL,
303 + /*
304 + * Safe div=15 should be safe even for switching b/w 1GHz and
305 + * 90MHz (frac pll might go up to 1.2GHz).
306 + */
307 + .safe_div = 15, },
308 },
309
310 [PLL_ID_SYS] = {
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);
318 break;
319
320 default:
321 --
322 2.32.0
323