diff options
Diffstat (limited to 'target/linux/at91/patches-5.15/243-clk-at91-clk-sam9x60-pll-add-notifier-for-div-part-o.patch')
-rw-r--r-- | target/linux/at91/patches-5.15/243-clk-at91-clk-sam9x60-pll-add-notifier-for-div-part-o.patch | 312 |
1 files changed, 312 insertions, 0 deletions
diff --git a/target/linux/at91/patches-5.15/243-clk-at91-clk-sam9x60-pll-add-notifier-for-div-part-o.patch b/target/linux/at91/patches-5.15/243-clk-at91-clk-sam9x60-pll-add-notifier-for-div-part-o.patch new file mode 100644 index 0000000000..678303da37 --- /dev/null +++ b/target/linux/at91/patches-5.15/243-clk-at91-clk-sam9x60-pll-add-notifier-for-div-part-o.patch @@ -0,0 +1,312 @@ +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: |