Skip to content

Commit 1e229c2

Browse files
claudiubezneabebarino
authored andcommitted
clk: at91: clk-sam9x60-pll: add notifier for div part of PLL
SAM9X60's PLL which is also part of SAMA7G5 is composed of 2 parts: one fractional part and one divider. On SAMA7G5 the CPU PLL could be changed at run-time to implement DVFS. The hardware clock tree on SAMA7G5 for CPU PLL is as follows: +---- div1 ----------------> cpuck | FRAC PLL ---> DIV PLL -+-> prescaler ---> div0 ---> mck0 The div1 block is not implemented in Linux; on prescaler block it has been discovered a bug on some scenarios and will be removed from Linux in next commits. Thus, the final clock tree that will be used in Linux will be as follows: +-----------> cpuck | FRAC PLL ---> DIV PLL -+-> div0 ---> mck0 It has been proposed in [1] to not introduce a new CPUFreq driver but to overload the proper clock drivers with proper operation such that cpufreq-dt to be used. To accomplish this DIV PLL and div0 implement clock notifiers which applies safe dividers before FRAC PLL is changed. The current commit treats only the DIV PLL by adding a notifier that sets a safe divider on PRE_RATE_CHANGE events. The safe divider is provided by initialization clock code (sama7g5.c). The div0 is treated in next commits (to keep the changes as clean as possible). [1] https://lore.kernel.org/lkml/20210105104426.4tmgc2l3vyicwedd@vireshk-i7/ Signed-off-by: Claudiu Beznea <[email protected]> Link: https://lore.kernel.org/r/[email protected] Acked-by: Nicolas Ferre <[email protected]> Signed-off-by: Stephen Boyd <[email protected]>
1 parent 0ef99f8 commit 1e229c2

File tree

4 files changed

+95
-29
lines changed

4 files changed

+95
-29
lines changed

drivers/clk/at91/clk-sam9x60-pll.c

Lines changed: 79 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
*/
66

77
#include <linux/bitfield.h>
8+
#include <linux/clk.h>
89
#include <linux/clk-provider.h>
910
#include <linux/clkdev.h>
1011
#include <linux/clk/at91_pmc.h>
@@ -47,12 +48,15 @@ struct sam9x60_div {
4748
struct sam9x60_pll_core core;
4849
struct at91_clk_pms pms;
4950
u8 div;
51+
u8 safe_div;
5052
};
5153

5254
#define to_sam9x60_pll_core(hw) container_of(hw, struct sam9x60_pll_core, hw)
5355
#define to_sam9x60_frac(core) container_of(core, struct sam9x60_frac, core)
5456
#define to_sam9x60_div(core) container_of(core, struct sam9x60_div, core)
5557

58+
static struct sam9x60_div *notifier_div;
59+
5660
static inline bool sam9x60_pll_ready(struct regmap *regmap, int id)
5761
{
5862
unsigned int status;
@@ -329,6 +333,26 @@ static const struct clk_ops sam9x60_frac_pll_ops_chg = {
329333
.restore_context = sam9x60_frac_pll_restore_context,
330334
};
331335

336+
/* This function should be called with spinlock acquired. */
337+
static void sam9x60_div_pll_set_div(struct sam9x60_pll_core *core, u32 div,
338+
bool enable)
339+
{
340+
struct regmap *regmap = core->regmap;
341+
u32 ena_msk = enable ? core->layout->endiv_mask : 0;
342+
u32 ena_val = enable ? (1 << core->layout->endiv_shift) : 0;
343+
344+
regmap_update_bits(regmap, AT91_PMC_PLL_CTRL0,
345+
core->layout->div_mask | ena_msk,
346+
(div << core->layout->div_shift) | ena_val);
347+
348+
regmap_update_bits(regmap, AT91_PMC_PLL_UPDT,
349+
AT91_PMC_PLL_UPDT_UPDATE | AT91_PMC_PLL_UPDT_ID_MSK,
350+
AT91_PMC_PLL_UPDT_UPDATE | core->id);
351+
352+
while (!sam9x60_pll_ready(regmap, core->id))
353+
cpu_relax();
354+
}
355+
332356
static int sam9x60_div_pll_set(struct sam9x60_pll_core *core)
333357
{
334358
struct sam9x60_div *div = to_sam9x60_div(core);
@@ -346,17 +370,7 @@ static int sam9x60_div_pll_set(struct sam9x60_pll_core *core)
346370
if (!!(val & core->layout->endiv_mask) && cdiv == div->div)
347371
goto unlock;
348372

349-
regmap_update_bits(regmap, AT91_PMC_PLL_CTRL0,
350-
core->layout->div_mask | core->layout->endiv_mask,
351-
(div->div << core->layout->div_shift) |
352-
(1 << core->layout->endiv_shift));
353-
354-
regmap_update_bits(regmap, AT91_PMC_PLL_UPDT,
355-
AT91_PMC_PLL_UPDT_UPDATE | AT91_PMC_PLL_UPDT_ID_MSK,
356-
AT91_PMC_PLL_UPDT_UPDATE | core->id);
357-
358-
while (!sam9x60_pll_ready(regmap, core->id))
359-
cpu_relax();
373+
sam9x60_div_pll_set_div(core, div->div, 1);
360374

361375
unlock:
362376
spin_unlock_irqrestore(core->lock, flags);
@@ -502,16 +516,7 @@ static int sam9x60_div_pll_set_rate_chg(struct clk_hw *hw, unsigned long rate,
502516
if (cdiv == div->div)
503517
goto unlock;
504518

505-
regmap_update_bits(regmap, AT91_PMC_PLL_CTRL0,
506-
core->layout->div_mask,
507-
(div->div << core->layout->div_shift));
508-
509-
regmap_update_bits(regmap, AT91_PMC_PLL_UPDT,
510-
AT91_PMC_PLL_UPDT_UPDATE | AT91_PMC_PLL_UPDT_ID_MSK,
511-
AT91_PMC_PLL_UPDT_UPDATE | core->id);
512-
513-
while (!sam9x60_pll_ready(regmap, core->id))
514-
cpu_relax();
519+
sam9x60_div_pll_set_div(core, div->div, 0);
515520

516521
unlock:
517522
spin_unlock_irqrestore(core->lock, irqflags);
@@ -538,6 +543,48 @@ static void sam9x60_div_pll_restore_context(struct clk_hw *hw)
538543
sam9x60_div_pll_set(core);
539544
}
540545

546+
static int sam9x60_div_pll_notifier_fn(struct notifier_block *notifier,
547+
unsigned long code, void *data)
548+
{
549+
struct sam9x60_div *div = notifier_div;
550+
struct sam9x60_pll_core core = div->core;
551+
struct regmap *regmap = core.regmap;
552+
unsigned long irqflags;
553+
u32 val, cdiv;
554+
int ret = NOTIFY_DONE;
555+
556+
if (code != PRE_RATE_CHANGE)
557+
return ret;
558+
559+
/*
560+
* We switch to safe divider to avoid overclocking of other domains
561+
* feed by us while the frac PLL (our parent) is changed.
562+
*/
563+
div->div = div->safe_div;
564+
565+
spin_lock_irqsave(core.lock, irqflags);
566+
regmap_update_bits(regmap, AT91_PMC_PLL_UPDT, AT91_PMC_PLL_UPDT_ID_MSK,
567+
core.id);
568+
regmap_read(regmap, AT91_PMC_PLL_CTRL0, &val);
569+
cdiv = (val & core.layout->div_mask) >> core.layout->div_shift;
570+
571+
/* Stop if nothing changed. */
572+
if (cdiv == div->safe_div)
573+
goto unlock;
574+
575+
sam9x60_div_pll_set_div(&core, div->div, 0);
576+
ret = NOTIFY_OK;
577+
578+
unlock:
579+
spin_unlock_irqrestore(core.lock, irqflags);
580+
581+
return ret;
582+
}
583+
584+
static struct notifier_block sam9x60_div_pll_notifier = {
585+
.notifier_call = sam9x60_div_pll_notifier_fn,
586+
};
587+
541588
static const struct clk_ops sam9x60_div_pll_ops = {
542589
.prepare = sam9x60_div_pll_prepare,
543590
.unprepare = sam9x60_div_pll_unprepare,
@@ -647,7 +694,8 @@ struct clk_hw * __init
647694
sam9x60_clk_register_div_pll(struct regmap *regmap, spinlock_t *lock,
648695
const char *name, const char *parent_name, u8 id,
649696
const struct clk_pll_characteristics *characteristics,
650-
const struct clk_pll_layout *layout, u32 flags)
697+
const struct clk_pll_layout *layout, u32 flags,
698+
u32 safe_div)
651699
{
652700
struct sam9x60_div *div;
653701
struct clk_hw *hw;
@@ -656,9 +704,13 @@ sam9x60_clk_register_div_pll(struct regmap *regmap, spinlock_t *lock,
656704
unsigned int val;
657705
int ret;
658706

659-
if (id > PLL_MAX_ID || !lock)
707+
/* We only support one changeable PLL. */
708+
if (id > PLL_MAX_ID || !lock || (safe_div && notifier_div))
660709
return ERR_PTR(-EINVAL);
661710

711+
if (safe_div >= PLL_DIV_MAX)
712+
safe_div = PLL_DIV_MAX - 1;
713+
662714
div = kzalloc(sizeof(*div), GFP_KERNEL);
663715
if (!div)
664716
return ERR_PTR(-ENOMEM);
@@ -678,6 +730,7 @@ sam9x60_clk_register_div_pll(struct regmap *regmap, spinlock_t *lock,
678730
div->core.layout = layout;
679731
div->core.regmap = regmap;
680732
div->core.lock = lock;
733+
div->safe_div = safe_div;
681734

682735
spin_lock_irqsave(div->core.lock, irqflags);
683736

@@ -693,6 +746,9 @@ sam9x60_clk_register_div_pll(struct regmap *regmap, spinlock_t *lock,
693746
if (ret) {
694747
kfree(div);
695748
hw = ERR_PTR(ret);
749+
} else if (div->safe_div) {
750+
notifier_div = div;
751+
clk_notifier_register(hw->clk, &sam9x60_div_pll_notifier);
696752
}
697753

698754
return hw;

drivers/clk/at91/pmc.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,8 @@ struct clk_hw * __init
214214
sam9x60_clk_register_div_pll(struct regmap *regmap, spinlock_t *lock,
215215
const char *name, const char *parent_name, u8 id,
216216
const struct clk_pll_characteristics *characteristics,
217-
const struct clk_pll_layout *layout, u32 flags);
217+
const struct clk_pll_layout *layout, u32 flags,
218+
u32 safe_div);
218219

219220
struct clk_hw * __init
220221
sam9x60_clk_register_frac_pll(struct regmap *regmap, spinlock_t *lock,

drivers/clk/at91/sam9x60.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -242,7 +242,7 @@ static void __init sam9x60_pmc_setup(struct device_node *np)
242242
* This feeds CPU. It should not
243243
* be disabled.
244244
*/
245-
CLK_IS_CRITICAL | CLK_SET_RATE_GATE);
245+
CLK_IS_CRITICAL | CLK_SET_RATE_GATE, 0);
246246
if (IS_ERR(hw))
247247
goto err_free;
248248

@@ -260,7 +260,7 @@ static void __init sam9x60_pmc_setup(struct device_node *np)
260260
&pll_div_layout,
261261
CLK_SET_RATE_GATE |
262262
CLK_SET_PARENT_GATE |
263-
CLK_SET_RATE_PARENT);
263+
CLK_SET_RATE_PARENT, 0);
264264
if (IS_ERR(hw))
265265
goto err_free;
266266

@@ -279,7 +279,7 @@ static void __init sam9x60_pmc_setup(struct device_node *np)
279279
hw = at91_clk_register_master_div(regmap, "masterck_div",
280280
"masterck_pres", &sam9x60_master_layout,
281281
&mck_characteristics, &mck_lock,
282-
CLK_SET_RATE_GATE);
282+
CLK_SET_RATE_GATE, 0);
283283
if (IS_ERR(hw))
284284
goto err_free;
285285

drivers/clk/at91/sama7g5.c

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,8 @@ static const struct clk_pll_characteristics pll_characteristics = {
127127
* @t: clock type
128128
* @f: clock flags
129129
* @eid: export index in sama7g5->chws[] array
130+
* @safe_div: intermediate divider need to be set on PRE_RATE_CHANGE
131+
* notification
130132
*/
131133
static const struct {
132134
const char *n;
@@ -136,6 +138,7 @@ static const struct {
136138
unsigned long f;
137139
u8 t;
138140
u8 eid;
141+
u8 safe_div;
139142
} sama7g5_plls[][PLL_ID_MAX] = {
140143
[PLL_ID_CPU] = {
141144
{ .n = "cpupll_fracck",
@@ -156,7 +159,12 @@ static const struct {
156159
.t = PLL_TYPE_DIV,
157160
/* This feeds CPU. It should not be disabled. */
158161
.f = CLK_IS_CRITICAL | CLK_SET_RATE_PARENT,
159-
.eid = PMC_CPUPLL, },
162+
.eid = PMC_CPUPLL,
163+
/*
164+
* Safe div=15 should be safe even for switching b/w 1GHz and
165+
* 90MHz (frac pll might go up to 1.2GHz).
166+
*/
167+
.safe_div = 15, },
160168
},
161169

162170
[PLL_ID_SYS] = {
@@ -967,7 +975,8 @@ static void __init sama7g5_pmc_setup(struct device_node *np)
967975
sama7g5_plls[i][j].p, i,
968976
sama7g5_plls[i][j].c,
969977
sama7g5_plls[i][j].l,
970-
sama7g5_plls[i][j].f);
978+
sama7g5_plls[i][j].f,
979+
sama7g5_plls[i][j].safe_div);
971980
break;
972981

973982
default:

0 commit comments

Comments
 (0)