Skip to content

Commit 65b3516

Browse files
passgatbebarino
authored andcommitted
clk: stm32f4: support spread spectrum clock generation
Support spread spectrum clock generation for the main PLL, the only one for which this functionality is available. Tested on the STM32F469I-DISCO board. Signed-off-by: Dario Binacchi <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Stephen Boyd <[email protected]>
1 parent a132837 commit 65b3516

File tree

1 file changed

+140
-3
lines changed

1 file changed

+140
-3
lines changed

drivers/clk/clk-stm32f4.c

Lines changed: 140 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,13 +35,20 @@
3535
#define STM32F4_RCC_APB2ENR 0x44
3636
#define STM32F4_RCC_BDCR 0x70
3737
#define STM32F4_RCC_CSR 0x74
38+
#define STM32F4_RCC_SSCGR 0x80
3839
#define STM32F4_RCC_PLLI2SCFGR 0x84
3940
#define STM32F4_RCC_PLLSAICFGR 0x88
4041
#define STM32F4_RCC_DCKCFGR 0x8c
4142
#define STM32F7_RCC_DCKCFGR2 0x90
4243

4344
#define STM32F4_RCC_PLLCFGR_N_MASK GENMASK(14, 6)
4445

46+
#define STM32F4_RCC_SSCGR_SSCGEN BIT(31)
47+
#define STM32F4_RCC_SSCGR_SPREADSEL BIT(30)
48+
#define STM32F4_RCC_SSCGR_RESERVED_MASK GENMASK(29, 28)
49+
#define STM32F4_RCC_SSCGR_INCSTEP_MASK GENMASK(27, 13)
50+
#define STM32F4_RCC_SSCGR_MODPER_MASK GENMASK(12, 0)
51+
4552
#define NONE -1
4653
#define NO_IDX NONE
4754
#define NO_MUX NONE
@@ -367,6 +374,16 @@ static const struct stm32f4_gate_data stm32f769_gates[] __initconst = {
367374
{ STM32F4_RCC_APB2ENR, 30, "mdio", "apb2_div" },
368375
};
369376

377+
enum stm32f4_pll_ssc_mod_type {
378+
STM32F4_PLL_SSC_CENTER_SPREAD,
379+
STM32F4_PLL_SSC_DOWN_SPREAD,
380+
};
381+
382+
static const char * const stm32f4_ssc_mod_methods[] __initconst = {
383+
[STM32F4_PLL_SSC_DOWN_SPREAD] = "down-spread",
384+
[STM32F4_PLL_SSC_CENTER_SPREAD] = "center-spread",
385+
};
386+
370387
/*
371388
* This bitmask tells us which bit offsets (0..192) on STM32F4[23]xxx
372389
* have gate bits associated with them. Its combined hweight is 71.
@@ -512,13 +529,21 @@ static const struct clk_div_table pll_divr_table[] = {
512529
{ 2, 2 }, { 3, 3 }, { 4, 4 }, { 5, 5 }, { 6, 6 }, { 7, 7 }, { 0 }
513530
};
514531

532+
struct stm32f4_pll_ssc {
533+
unsigned int mod_freq;
534+
unsigned int mod_depth;
535+
enum stm32f4_pll_ssc_mod_type mod_type;
536+
};
537+
515538
struct stm32f4_pll {
516539
spinlock_t *lock;
517540
struct clk_gate gate;
518541
u8 offset;
519542
u8 bit_rdy_idx;
520543
u8 status;
521544
u8 n_start;
545+
bool ssc_enable;
546+
struct stm32f4_pll_ssc ssc_conf;
522547
};
523548

524549
#define to_stm32f4_pll(_gate) container_of(_gate, struct stm32f4_pll, gate)
@@ -541,6 +566,7 @@ struct stm32f4_vco_data {
541566
u8 offset;
542567
u8 bit_idx;
543568
u8 bit_rdy_idx;
569+
bool sscg;
544570
};
545571

546572
static const struct stm32f4_vco_data vco_data[] = {
@@ -661,6 +687,32 @@ static long stm32f4_pll_round_rate(struct clk_hw *hw, unsigned long rate,
661687
return *prate * n;
662688
}
663689

690+
static void stm32f4_pll_set_ssc(struct clk_hw *hw, unsigned long parent_rate,
691+
unsigned int ndiv)
692+
{
693+
struct clk_gate *gate = to_clk_gate(hw);
694+
struct stm32f4_pll *pll = to_stm32f4_pll(gate);
695+
struct stm32f4_pll_ssc *ssc = &pll->ssc_conf;
696+
u32 modeper, incstep;
697+
u32 sscgr;
698+
699+
sscgr = readl(base + STM32F4_RCC_SSCGR);
700+
/* reserved field must be kept at reset value */
701+
sscgr &= STM32F4_RCC_SSCGR_RESERVED_MASK;
702+
703+
modeper = DIV_ROUND_CLOSEST(parent_rate, 4 * ssc->mod_freq);
704+
incstep = DIV_ROUND_CLOSEST(((1 << 15) - 1) * ssc->mod_depth * ndiv,
705+
5 * 10000 * modeper);
706+
sscgr |= STM32F4_RCC_SSCGR_SSCGEN |
707+
FIELD_PREP(STM32F4_RCC_SSCGR_INCSTEP_MASK, incstep) |
708+
FIELD_PREP(STM32F4_RCC_SSCGR_MODPER_MASK, modeper);
709+
710+
if (ssc->mod_type)
711+
sscgr |= STM32F4_RCC_SSCGR_SPREADSEL;
712+
713+
writel(sscgr, base + STM32F4_RCC_SSCGR);
714+
}
715+
664716
static int stm32f4_pll_set_rate(struct clk_hw *hw, unsigned long rate,
665717
unsigned long parent_rate)
666718
{
@@ -683,6 +735,9 @@ static int stm32f4_pll_set_rate(struct clk_hw *hw, unsigned long rate,
683735

684736
writel(val, base + pll->offset);
685737

738+
if (pll->ssc_enable)
739+
stm32f4_pll_set_ssc(hw, parent_rate, n);
740+
686741
if (pll_state)
687742
stm32f4_pll_enable(hw);
688743

@@ -788,6 +843,84 @@ static struct clk_hw *clk_register_pll_div(const char *name,
788843
return hw;
789844
}
790845

846+
static int __init stm32f4_pll_init_ssc(struct clk_hw *hw,
847+
const struct stm32f4_pll_ssc *conf)
848+
{
849+
struct clk_gate *gate = to_clk_gate(hw);
850+
struct stm32f4_pll *pll = to_stm32f4_pll(gate);
851+
struct clk_hw *parent;
852+
unsigned long parent_rate;
853+
int pll_state;
854+
unsigned long n, val;
855+
856+
parent = clk_hw_get_parent(hw);
857+
if (!parent) {
858+
pr_err("%s: failed to get clock parent\n", __func__);
859+
return -ENODEV;
860+
}
861+
862+
parent_rate = clk_hw_get_rate(parent);
863+
864+
pll->ssc_enable = true;
865+
memcpy(&pll->ssc_conf, conf, sizeof(pll->ssc_conf));
866+
867+
pll_state = stm32f4_pll_is_enabled(hw);
868+
869+
if (pll_state)
870+
stm32f4_pll_disable(hw);
871+
872+
val = readl(base + pll->offset);
873+
n = FIELD_GET(STM32F4_RCC_PLLCFGR_N_MASK, val);
874+
875+
pr_debug("%s: pll: %s, parent: %s, parent-rate: %lu, n: %lu\n",
876+
__func__, clk_hw_get_name(hw), clk_hw_get_name(parent),
877+
parent_rate, n);
878+
879+
stm32f4_pll_set_ssc(hw, parent_rate, n);
880+
881+
if (pll_state)
882+
stm32f4_pll_enable(hw);
883+
884+
return 0;
885+
}
886+
887+
static int __init stm32f4_pll_ssc_parse_dt(struct device_node *np,
888+
struct stm32f4_pll_ssc *conf)
889+
{
890+
int ret;
891+
const char *s;
892+
893+
if (!conf)
894+
return -EINVAL;
895+
896+
ret = of_property_read_u32(np, "st,ssc-modfreq-hz", &conf->mod_freq);
897+
if (ret)
898+
return ret;
899+
900+
ret = of_property_read_u32(np, "st,ssc-moddepth-permyriad",
901+
&conf->mod_depth);
902+
if (ret) {
903+
pr_err("%pOF: missing st,ssc-moddepth-permyriad\n", np);
904+
return ret;
905+
}
906+
907+
ret = fwnode_property_match_property_string(of_fwnode_handle(np),
908+
"st,ssc-modmethod",
909+
stm32f4_ssc_mod_methods,
910+
ARRAY_SIZE(stm32f4_ssc_mod_methods));
911+
if (ret < 0) {
912+
pr_err("%pOF: failed to get st,ssc-modmethod\n", np);
913+
return ret;
914+
}
915+
916+
conf->mod_type = ret;
917+
918+
pr_debug("%pOF: SSCG settings: mod_freq: %d, mod_depth: %d mod_method: %s [%d]\n",
919+
np, conf->mod_freq, conf->mod_depth, s, conf->mod_type);
920+
921+
return 0;
922+
}
923+
791924
static struct clk_hw *stm32f4_rcc_register_pll(const char *pllsrc,
792925
const struct stm32f4_pll_data *data, spinlock_t *lock)
793926
{
@@ -1695,7 +1828,8 @@ static void __init stm32f4_rcc_init(struct device_node *np)
16951828
const struct of_device_id *match;
16961829
const struct stm32f4_clk_data *data;
16971830
unsigned long pllm;
1698-
struct clk_hw *pll_src_hw;
1831+
struct clk_hw *pll_src_hw, *pll_vco_hw;
1832+
struct stm32f4_pll_ssc ssc_conf;
16991833

17001834
base = of_iomap(np, 0);
17011835
if (!base) {
@@ -1754,8 +1888,8 @@ static void __init stm32f4_rcc_init(struct device_node *np)
17541888
clk_hw_register_fixed_factor(NULL, "vco_in", pll_src,
17551889
0, 1, pllm);
17561890

1757-
stm32f4_rcc_register_pll("vco_in", &data->pll_data[0],
1758-
&stm32f4_clk_lock);
1891+
pll_vco_hw = stm32f4_rcc_register_pll("vco_in", &data->pll_data[0],
1892+
&stm32f4_clk_lock);
17591893

17601894
clks[PLL_VCO_I2S] = stm32f4_rcc_register_pll("vco_in",
17611895
&data->pll_data[1], &stm32f4_clk_lock);
@@ -1900,6 +2034,9 @@ static void __init stm32f4_rcc_init(struct device_node *np)
19002034

19012035
of_clk_add_hw_provider(np, stm32f4_rcc_lookup_clk, NULL);
19022036

2037+
if (!stm32f4_pll_ssc_parse_dt(np, &ssc_conf))
2038+
stm32f4_pll_init_ssc(pll_vco_hw, &ssc_conf);
2039+
19032040
return;
19042041
fail:
19052042
kfree(clks);

0 commit comments

Comments
 (0)