mirror of
https://github.com/openwrt/openwrt.git
synced 2024-12-29 10:08:59 +00:00
05ed7dc50d
Patches automatically rebased. Signed-off-by: Rui Salvaterra <rsalvaterra@gmail.com>
313 lines
10 KiB
Diff
313 lines
10 KiB
Diff
From e76d2af5009f52aa02d3db7ae32d150ad66398f9 Mon Sep 17 00:00:00 2001
|
|
From: Claudiu Beznea <claudiu.beznea@microchip.com>
|
|
Date: Mon, 11 Oct 2021 14:27:15 +0300
|
|
Subject: [PATCH 243/247] 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 <claudiu.beznea@microchip.com>
|
|
Link: https://lore.kernel.org/r/20211011112719.3951784-12-claudiu.beznea@microchip.com
|
|
Acked-by: Nicolas Ferre <nicolas.ferre@microchip.com>
|
|
Signed-off-by: Stephen Boyd <sboyd@kernel.org>
|
|
---
|
|
drivers/clk/at91/clk-sam9x60-pll.c | 102 ++++++++++++++++++++++-------
|
|
drivers/clk/at91/pmc.h | 3 +-
|
|
drivers/clk/at91/sam9x60.c | 6 +-
|
|
drivers/clk/at91/sama7g5.c | 13 +++-
|
|
4 files changed, 95 insertions(+), 29 deletions(-)
|
|
|
|
--- a/drivers/clk/at91/clk-sam9x60-pll.c
|
|
+++ b/drivers/clk/at91/clk-sam9x60-pll.c
|
|
@@ -5,6 +5,7 @@
|
|
*/
|
|
|
|
#include <linux/bitfield.h>
|
|
+#include <linux/clk.h>
|
|
#include <linux/clk-provider.h>
|
|
#include <linux/clkdev.h>
|
|
#include <linux/clk/at91_pmc.h>
|
|
@@ -47,12 +48,15 @@ struct sam9x60_div {
|
|
struct sam9x60_pll_core core;
|
|
struct at91_clk_pms pms;
|
|
u8 div;
|
|
+ u8 safe_div;
|
|
};
|
|
|
|
#define to_sam9x60_pll_core(hw) container_of(hw, struct sam9x60_pll_core, hw)
|
|
#define to_sam9x60_frac(core) container_of(core, struct sam9x60_frac, core)
|
|
#define to_sam9x60_div(core) container_of(core, struct sam9x60_div, core)
|
|
|
|
+static struct sam9x60_div *notifier_div;
|
|
+
|
|
static inline bool sam9x60_pll_ready(struct regmap *regmap, int id)
|
|
{
|
|
unsigned int status;
|
|
@@ -329,6 +333,26 @@ static const struct clk_ops sam9x60_frac
|
|
.restore_context = sam9x60_frac_pll_restore_context,
|
|
};
|
|
|
|
+/* This function should be called with spinlock acquired. */
|
|
+static void sam9x60_div_pll_set_div(struct sam9x60_pll_core *core, u32 div,
|
|
+ bool enable)
|
|
+{
|
|
+ struct regmap *regmap = core->regmap;
|
|
+ u32 ena_msk = enable ? core->layout->endiv_mask : 0;
|
|
+ u32 ena_val = enable ? (1 << core->layout->endiv_shift) : 0;
|
|
+
|
|
+ regmap_update_bits(regmap, AT91_PMC_PLL_CTRL0,
|
|
+ core->layout->div_mask | ena_msk,
|
|
+ (div << core->layout->div_shift) | ena_val);
|
|
+
|
|
+ regmap_update_bits(regmap, AT91_PMC_PLL_UPDT,
|
|
+ AT91_PMC_PLL_UPDT_UPDATE | AT91_PMC_PLL_UPDT_ID_MSK,
|
|
+ AT91_PMC_PLL_UPDT_UPDATE | core->id);
|
|
+
|
|
+ while (!sam9x60_pll_ready(regmap, core->id))
|
|
+ cpu_relax();
|
|
+}
|
|
+
|
|
static int sam9x60_div_pll_set(struct sam9x60_pll_core *core)
|
|
{
|
|
struct sam9x60_div *div = to_sam9x60_div(core);
|
|
@@ -346,17 +370,7 @@ static int sam9x60_div_pll_set(struct sa
|
|
if (!!(val & core->layout->endiv_mask) && cdiv == div->div)
|
|
goto unlock;
|
|
|
|
- regmap_update_bits(regmap, AT91_PMC_PLL_CTRL0,
|
|
- core->layout->div_mask | core->layout->endiv_mask,
|
|
- (div->div << core->layout->div_shift) |
|
|
- (1 << core->layout->endiv_shift));
|
|
-
|
|
- regmap_update_bits(regmap, AT91_PMC_PLL_UPDT,
|
|
- AT91_PMC_PLL_UPDT_UPDATE | AT91_PMC_PLL_UPDT_ID_MSK,
|
|
- AT91_PMC_PLL_UPDT_UPDATE | core->id);
|
|
-
|
|
- while (!sam9x60_pll_ready(regmap, core->id))
|
|
- cpu_relax();
|
|
+ sam9x60_div_pll_set_div(core, div->div, 1);
|
|
|
|
unlock:
|
|
spin_unlock_irqrestore(core->lock, flags);
|
|
@@ -502,16 +516,7 @@ static int sam9x60_div_pll_set_rate_chg(
|
|
if (cdiv == div->div)
|
|
goto unlock;
|
|
|
|
- regmap_update_bits(regmap, AT91_PMC_PLL_CTRL0,
|
|
- core->layout->div_mask,
|
|
- (div->div << core->layout->div_shift));
|
|
-
|
|
- regmap_update_bits(regmap, AT91_PMC_PLL_UPDT,
|
|
- AT91_PMC_PLL_UPDT_UPDATE | AT91_PMC_PLL_UPDT_ID_MSK,
|
|
- AT91_PMC_PLL_UPDT_UPDATE | core->id);
|
|
-
|
|
- while (!sam9x60_pll_ready(regmap, core->id))
|
|
- cpu_relax();
|
|
+ sam9x60_div_pll_set_div(core, div->div, 0);
|
|
|
|
unlock:
|
|
spin_unlock_irqrestore(core->lock, irqflags);
|
|
@@ -538,6 +543,48 @@ static void sam9x60_div_pll_restore_cont
|
|
sam9x60_div_pll_set(core);
|
|
}
|
|
|
|
+static int sam9x60_div_pll_notifier_fn(struct notifier_block *notifier,
|
|
+ unsigned long code, void *data)
|
|
+{
|
|
+ struct sam9x60_div *div = notifier_div;
|
|
+ struct sam9x60_pll_core core = div->core;
|
|
+ struct regmap *regmap = core.regmap;
|
|
+ unsigned long irqflags;
|
|
+ u32 val, cdiv;
|
|
+ int ret = NOTIFY_DONE;
|
|
+
|
|
+ if (code != PRE_RATE_CHANGE)
|
|
+ return ret;
|
|
+
|
|
+ /*
|
|
+ * We switch to safe divider to avoid overclocking of other domains
|
|
+ * feed by us while the frac PLL (our parent) is changed.
|
|
+ */
|
|
+ div->div = div->safe_div;
|
|
+
|
|
+ spin_lock_irqsave(core.lock, irqflags);
|
|
+ regmap_update_bits(regmap, AT91_PMC_PLL_UPDT, AT91_PMC_PLL_UPDT_ID_MSK,
|
|
+ core.id);
|
|
+ regmap_read(regmap, AT91_PMC_PLL_CTRL0, &val);
|
|
+ cdiv = (val & core.layout->div_mask) >> core.layout->div_shift;
|
|
+
|
|
+ /* Stop if nothing changed. */
|
|
+ if (cdiv == div->safe_div)
|
|
+ goto unlock;
|
|
+
|
|
+ sam9x60_div_pll_set_div(&core, div->div, 0);
|
|
+ ret = NOTIFY_OK;
|
|
+
|
|
+unlock:
|
|
+ spin_unlock_irqrestore(core.lock, irqflags);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static struct notifier_block sam9x60_div_pll_notifier = {
|
|
+ .notifier_call = sam9x60_div_pll_notifier_fn,
|
|
+};
|
|
+
|
|
static const struct clk_ops sam9x60_div_pll_ops = {
|
|
.prepare = sam9x60_div_pll_prepare,
|
|
.unprepare = sam9x60_div_pll_unprepare,
|
|
@@ -647,7 +694,8 @@ struct clk_hw * __init
|
|
sam9x60_clk_register_div_pll(struct regmap *regmap, spinlock_t *lock,
|
|
const char *name, const char *parent_name, u8 id,
|
|
const struct clk_pll_characteristics *characteristics,
|
|
- const struct clk_pll_layout *layout, u32 flags)
|
|
+ const struct clk_pll_layout *layout, u32 flags,
|
|
+ u32 safe_div)
|
|
{
|
|
struct sam9x60_div *div;
|
|
struct clk_hw *hw;
|
|
@@ -656,9 +704,13 @@ sam9x60_clk_register_div_pll(struct regm
|
|
unsigned int val;
|
|
int ret;
|
|
|
|
- if (id > PLL_MAX_ID || !lock)
|
|
+ /* We only support one changeable PLL. */
|
|
+ if (id > PLL_MAX_ID || !lock || (safe_div && notifier_div))
|
|
return ERR_PTR(-EINVAL);
|
|
|
|
+ if (safe_div >= PLL_DIV_MAX)
|
|
+ safe_div = PLL_DIV_MAX - 1;
|
|
+
|
|
div = kzalloc(sizeof(*div), GFP_KERNEL);
|
|
if (!div)
|
|
return ERR_PTR(-ENOMEM);
|
|
@@ -678,6 +730,7 @@ sam9x60_clk_register_div_pll(struct regm
|
|
div->core.layout = layout;
|
|
div->core.regmap = regmap;
|
|
div->core.lock = lock;
|
|
+ div->safe_div = safe_div;
|
|
|
|
spin_lock_irqsave(div->core.lock, irqflags);
|
|
|
|
@@ -693,6 +746,9 @@ sam9x60_clk_register_div_pll(struct regm
|
|
if (ret) {
|
|
kfree(div);
|
|
hw = ERR_PTR(ret);
|
|
+ } else if (div->safe_div) {
|
|
+ notifier_div = div;
|
|
+ clk_notifier_register(hw->clk, &sam9x60_div_pll_notifier);
|
|
}
|
|
|
|
return hw;
|
|
--- a/drivers/clk/at91/pmc.h
|
|
+++ b/drivers/clk/at91/pmc.h
|
|
@@ -214,7 +214,8 @@ struct clk_hw * __init
|
|
sam9x60_clk_register_div_pll(struct regmap *regmap, spinlock_t *lock,
|
|
const char *name, const char *parent_name, u8 id,
|
|
const struct clk_pll_characteristics *characteristics,
|
|
- const struct clk_pll_layout *layout, u32 flags);
|
|
+ const struct clk_pll_layout *layout, u32 flags,
|
|
+ u32 safe_div);
|
|
|
|
struct clk_hw * __init
|
|
sam9x60_clk_register_frac_pll(struct regmap *regmap, spinlock_t *lock,
|
|
--- a/drivers/clk/at91/sam9x60.c
|
|
+++ b/drivers/clk/at91/sam9x60.c
|
|
@@ -242,7 +242,7 @@ static void __init sam9x60_pmc_setup(str
|
|
* This feeds CPU. It should not
|
|
* be disabled.
|
|
*/
|
|
- CLK_IS_CRITICAL | CLK_SET_RATE_GATE);
|
|
+ CLK_IS_CRITICAL | CLK_SET_RATE_GATE, 0);
|
|
if (IS_ERR(hw))
|
|
goto err_free;
|
|
|
|
@@ -260,7 +260,7 @@ static void __init sam9x60_pmc_setup(str
|
|
&pll_div_layout,
|
|
CLK_SET_RATE_GATE |
|
|
CLK_SET_PARENT_GATE |
|
|
- CLK_SET_RATE_PARENT);
|
|
+ CLK_SET_RATE_PARENT, 0);
|
|
if (IS_ERR(hw))
|
|
goto err_free;
|
|
|
|
@@ -279,7 +279,7 @@ static void __init sam9x60_pmc_setup(str
|
|
hw = at91_clk_register_master_div(regmap, "masterck_div",
|
|
"masterck_pres", &sam9x60_master_layout,
|
|
&mck_characteristics, &mck_lock,
|
|
- CLK_SET_RATE_GATE);
|
|
+ CLK_SET_RATE_GATE, 0);
|
|
if (IS_ERR(hw))
|
|
goto err_free;
|
|
|
|
--- a/drivers/clk/at91/sama7g5.c
|
|
+++ b/drivers/clk/at91/sama7g5.c
|
|
@@ -127,6 +127,8 @@ static const struct clk_pll_characterist
|
|
* @t: clock type
|
|
* @f: clock flags
|
|
* @eid: export index in sama7g5->chws[] array
|
|
+ * @safe_div: intermediate divider need to be set on PRE_RATE_CHANGE
|
|
+ * notification
|
|
*/
|
|
static const struct {
|
|
const char *n;
|
|
@@ -136,6 +138,7 @@ static const struct {
|
|
unsigned long f;
|
|
u8 t;
|
|
u8 eid;
|
|
+ u8 safe_div;
|
|
} sama7g5_plls[][PLL_ID_MAX] = {
|
|
[PLL_ID_CPU] = {
|
|
{ .n = "cpupll_fracck",
|
|
@@ -156,7 +159,12 @@ static const struct {
|
|
.t = PLL_TYPE_DIV,
|
|
/* This feeds CPU. It should not be disabled. */
|
|
.f = CLK_IS_CRITICAL | CLK_SET_RATE_PARENT,
|
|
- .eid = PMC_CPUPLL, },
|
|
+ .eid = PMC_CPUPLL,
|
|
+ /*
|
|
+ * Safe div=15 should be safe even for switching b/w 1GHz and
|
|
+ * 90MHz (frac pll might go up to 1.2GHz).
|
|
+ */
|
|
+ .safe_div = 15, },
|
|
},
|
|
|
|
[PLL_ID_SYS] = {
|
|
@@ -967,7 +975,8 @@ static void __init sama7g5_pmc_setup(str
|
|
sama7g5_plls[i][j].p, i,
|
|
sama7g5_plls[i][j].c,
|
|
sama7g5_plls[i][j].l,
|
|
- sama7g5_plls[i][j].f);
|
|
+ sama7g5_plls[i][j].f,
|
|
+ sama7g5_plls[i][j].safe_div);
|
|
break;
|
|
|
|
default:
|