2021-03-14 15:15:15

by Dario Binacchi

[permalink] [raw]
Subject: [PATCH 0/4] clk: ti: add am33xx spread spectrum clock support


As reported by the TI spruh73x RM, MPU and LCD modules support spread
spectrum clocking (SSC) on their output clocks. SSC is used to spread
the spectral peaking of the clock to reduce any electromagnetic
interference (EMI) that may be caused due to the clock’s fundamental
or any of its harmonics.
The series allows you to enable and adjust the spread spectrum clocking
for all am33xx PLLs for which it is supported.



Dario Binacchi (4):
clk: ti: fix typo in routine description
dt-bindings: ti: dpll: add spread spectrum support
ARM: dts: am33xx-clocks: add spread spectrum support
clk: ti: add am33xx spread spectrum clock support

.../devicetree/bindings/clock/ti/dpll.txt | 20 +++++
arch/arm/boot/dts/am33xx-clocks.dtsi | 4 +-
drivers/clk/ti/dpll.c | 41 +++++++++
drivers/clk/ti/dpll3xxx.c | 87 ++++++++++++++++++-
include/linux/clk/ti.h | 24 +++++
5 files changed, 173 insertions(+), 3 deletions(-)

--
2.17.1


2021-03-14 15:15:48

by Dario Binacchi

[permalink] [raw]
Subject: [PATCH 4/4] clk: ti: add am33xx spread spectrum clock support

The patch enables spread spectrum clocking (SSC) for MPU and LCD PLLs.
As reported by the TI spruh73x RM, SSC is only supported for the
DISP/LCD and MPU PLLs on am33xx device. SSC is not supported for DDR,
PER, and CORE PLLs.

Calculating the required values and setting the registers accordingly
was taken from the set_mpu_spreadspectrum routine contained in the
arch/arm/mach-omap2/am33xx/clock_am33xx.c file of the u-boot project.

In locked condition, DPLL output clock = CLKINP *[M/N]. In case of
SSC enabled, the AM335x reference manual explains that there is a
restriction of range of M values. Since the omap2_dpll_round_rate
routine attempts to select the minimum possible N, the value of M
obtained is not guaranteed to be within the range required. With the new
"ti,min-div" parameter it is possible to increase N and consequently M
to satisfy the constraint imposed by SSC.

Signed-off-by: Dario Binacchi <[email protected]>

---

arch/arm/boot/dts/am33xx-clocks.dtsi | 4 +-
drivers/clk/ti/dpll.c | 41 ++++++++++++++
drivers/clk/ti/dpll3xxx.c | 85 ++++++++++++++++++++++++++++
include/linux/clk/ti.h | 24 ++++++++
4 files changed, 152 insertions(+), 2 deletions(-)

diff --git a/arch/arm/boot/dts/am33xx-clocks.dtsi b/arch/arm/boot/dts/am33xx-clocks.dtsi
index e7bbbf536a8c..a02e0b1229a4 100644
--- a/arch/arm/boot/dts/am33xx-clocks.dtsi
+++ b/arch/arm/boot/dts/am33xx-clocks.dtsi
@@ -164,7 +164,7 @@
#clock-cells = <0>;
compatible = "ti,am3-dpll-core-clock";
clocks = <&sys_clkin_ck>, <&sys_clkin_ck>;
- reg = <0x0490>, <0x045c>, <0x0468>, <0x0460>, <0x0464>;
+ reg = <0x0490>, <0x045c>, <0x0468>;
};

dpll_core_x2_ck: dpll_core_x2_ck {
@@ -204,7 +204,7 @@
#clock-cells = <0>;
compatible = "ti,am3-dpll-clock";
clocks = <&sys_clkin_ck>, <&sys_clkin_ck>;
- reg = <0x0488>, <0x0420>, <0x042c>;
+ reg = <0x0488>, <0x0420>, <0x042c>, <0x0424>, <0x0428>;
};

dpll_mpu_m2_ck: dpll_mpu_m2_ck@4a8 {
diff --git a/drivers/clk/ti/dpll.c b/drivers/clk/ti/dpll.c
index d6f1ac5b53e1..2738417a47b7 100644
--- a/drivers/clk/ti/dpll.c
+++ b/drivers/clk/ti/dpll.c
@@ -290,7 +290,9 @@ static void __init of_ti_dpll_setup(struct device_node *node,
struct clk_init_data *init = NULL;
const char **parent_names = NULL;
struct dpll_data *dd = NULL;
+ int ssc_clk_index;
u8 dpll_mode = 0;
+ u32 min_div;

dd = kmemdup(ddt, sizeof(*dd), GFP_KERNEL);
clk_hw = kzalloc(sizeof(*clk_hw), GFP_KERNEL);
@@ -345,6 +347,27 @@ static void __init of_ti_dpll_setup(struct device_node *node,
if (dd->autoidle_mask) {
if (ti_clk_get_reg_addr(node, 3, &dd->autoidle_reg))
goto cleanup;
+
+ ssc_clk_index = 4;
+ } else {
+ ssc_clk_index = 3;
+ }
+
+ if (dd->ssc_deltam_int_mask && dd->ssc_deltam_frac_mask &&
+ dd->ssc_modfreq_mant_mask && dd->ssc_modfreq_exp_mask) {
+ if (ti_clk_get_reg_addr(node, ssc_clk_index++,
+ &dd->ssc_deltam_reg))
+ goto cleanup;
+
+ if (ti_clk_get_reg_addr(node, ssc_clk_index++,
+ &dd->ssc_modfreq_reg))
+ goto cleanup;
+
+ of_property_read_u32(node, "ti,ssc-modfreq", &dd->ssc_modfreq);
+ of_property_read_u32(node, "ti,ssc-deltam", &dd->ssc_deltam);
+ if (of_property_read_bool(node, "ti,ssc-downspread"))
+ dd->ssc_downspread = 1;
+
}

if (of_property_read_bool(node, "ti,low-power-stop"))
@@ -356,6 +379,10 @@ static void __init of_ti_dpll_setup(struct device_node *node,
if (of_property_read_bool(node, "ti,lock"))
dpll_mode |= 1 << DPLL_LOCKED;

+ if (!of_property_read_u32(node, "ti,min-div", &min_div) &&
+ min_div > dd->min_divider)
+ dd->min_divider = min_div;
+
if (dpll_mode)
dd->modes = dpll_mode;

@@ -585,8 +612,15 @@ static void __init of_ti_am3_no_gate_dpll_setup(struct device_node *node)
const struct dpll_data dd = {
.idlest_mask = 0x1,
.enable_mask = 0x7,
+ .ssc_enable_mask = 0x1 << 12,
+ .ssc_ack_mask = 0x1 << 13,
+ .ssc_downspread_mask = 0x1 << 14,
.mult_mask = 0x7ff << 8,
.div1_mask = 0x7f,
+ .ssc_deltam_int_mask = 0x3 << 18,
+ .ssc_deltam_frac_mask = 0x3ffff,
+ .ssc_modfreq_mant_mask = 0x7f,
+ .ssc_modfreq_exp_mask = 0x7 << 8,
.max_multiplier = 2047,
.max_divider = 128,
.min_divider = 1,
@@ -645,8 +679,15 @@ static void __init of_ti_am3_dpll_setup(struct device_node *node)
const struct dpll_data dd = {
.idlest_mask = 0x1,
.enable_mask = 0x7,
+ .ssc_enable_mask = 0x1 << 12,
+ .ssc_ack_mask = 0x1 << 13,
+ .ssc_downspread_mask = 0x1 << 14,
.mult_mask = 0x7ff << 8,
.div1_mask = 0x7f,
+ .ssc_deltam_int_mask = 0x3 << 18,
+ .ssc_deltam_frac_mask = 0x3ffff,
+ .ssc_modfreq_mant_mask = 0x7f,
+ .ssc_modfreq_exp_mask = 0x7 << 8,
.max_multiplier = 2047,
.max_divider = 128,
.min_divider = 1,
diff --git a/drivers/clk/ti/dpll3xxx.c b/drivers/clk/ti/dpll3xxx.c
index 94d5b5fe9a2b..e32b3515f9e7 100644
--- a/drivers/clk/ti/dpll3xxx.c
+++ b/drivers/clk/ti/dpll3xxx.c
@@ -291,6 +291,88 @@ static void _lookup_sddiv(struct clk_hw_omap *clk, u8 *sd_div, u16 m, u8 n)
*sd_div = sd;
}

+/**
+ * omap3_noncore_dpll_ssc_program - set spread-spectrum clocking registers
+ * @clk: struct clk * of DPLL to set
+ *
+ * Enable the DPLL spread spectrum clocking if frequency modulation and
+ * frequency spreading have been set, otherwise disable it.
+ */
+static void omap3_noncore_dpll_ssc_program(struct clk_hw_omap *clk)
+{
+ struct dpll_data *dd = clk->dpll_data;
+ unsigned long ref_rate;
+ u32 v, ctrl, mod_freq_divider, exponent, mantissa;
+ u32 deltam_step, deltam_ceil;
+
+ ctrl = ti_clk_ll_ops->clk_readl(&dd->control_reg);
+
+ if (dd->ssc_modfreq && dd->ssc_deltam) {
+ ctrl |= dd->ssc_enable_mask;
+
+ if (dd->ssc_downspread)
+ ctrl |= dd->ssc_downspread_mask;
+ else
+ ctrl &= ~dd->ssc_downspread_mask;
+
+ ref_rate = clk_hw_get_rate(dd->clk_ref);
+ mod_freq_divider =
+ (ref_rate / dd->last_rounded_n) / (4 * dd->ssc_modfreq);
+ if (dd->ssc_modfreq > (ref_rate / 70))
+ pr_warn("clock: SSC modulation frequency of DPLL %s greater than %ld\n",
+ __clk_get_name(clk->hw.clk), ref_rate / 70);
+
+ exponent = 0;
+ mantissa = mod_freq_divider;
+ while ((mantissa > 127) && (exponent < 7)) {
+ exponent++;
+ mantissa /= 2;
+ }
+ if (mantissa > 127)
+ mantissa = 127;
+
+ v = ti_clk_ll_ops->clk_readl(&dd->ssc_modfreq_reg);
+ v &= ~(dd->ssc_modfreq_mant_mask | dd->ssc_modfreq_exp_mask);
+ v |= mantissa << __ffs(dd->ssc_modfreq_mant_mask);
+ v |= exponent << __ffs(dd->ssc_modfreq_exp_mask);
+ ti_clk_ll_ops->clk_writel(v, &dd->ssc_modfreq_reg);
+
+ deltam_step = dd->last_rounded_m * dd->ssc_deltam;
+ deltam_step /= 10;
+ if (dd->ssc_downspread)
+ deltam_step /= 2;
+
+ deltam_step <<= __ffs(dd->ssc_deltam_int_mask);
+ deltam_step /= 100;
+ deltam_step /= mod_freq_divider;
+ if (deltam_step > 0xFFFFF)
+ deltam_step = 0xFFFFF;
+
+ deltam_ceil = (deltam_step & dd->ssc_deltam_int_mask) >>
+ __ffs(dd->ssc_deltam_int_mask);
+ if (deltam_step & dd->ssc_deltam_frac_mask)
+ deltam_ceil++;
+
+ if ((dd->ssc_downspread &&
+ ((dd->last_rounded_m - (2 * deltam_ceil)) < 20 ||
+ dd->last_rounded_m > 2045)) ||
+ ((dd->last_rounded_m - deltam_ceil) < 20 ||
+ (dd->last_rounded_m + deltam_ceil) > 2045))
+ pr_warn("clock: SSC multiplier of DPLL %s is out of range\n",
+ __clk_get_name(clk->hw.clk));
+
+ v = ti_clk_ll_ops->clk_readl(&dd->ssc_deltam_reg);
+ v &= ~(dd->ssc_deltam_int_mask | dd->ssc_deltam_frac_mask);
+ v |= deltam_step << __ffs(dd->ssc_deltam_int_mask |
+ dd->ssc_deltam_frac_mask);
+ ti_clk_ll_ops->clk_writel(v, &dd->ssc_deltam_reg);
+ } else {
+ ctrl &= ~dd->ssc_enable_mask;
+ }
+
+ ti_clk_ll_ops->clk_writel(ctrl, &dd->control_reg);
+}
+
/**
* omap3_noncore_dpll_program - set non-core DPLL M,N values directly
* @clk: struct clk * of DPLL to set
@@ -390,6 +472,9 @@ static int omap3_noncore_dpll_program(struct clk_hw_omap *clk, u16 freqsel)
ti_clk_ll_ops->clk_writel(v, &dd->control_reg);
}

+ if (dd->ssc_enable_mask)
+ omap3_noncore_dpll_ssc_program(clk);
+
/* We let the clock framework set the other output dividers later */

/* REVISIT: Set ramp-up delay? */
diff --git a/include/linux/clk/ti.h b/include/linux/clk/ti.h
index c62f6fa6763d..cba093de62d8 100644
--- a/include/linux/clk/ti.h
+++ b/include/linux/clk/ti.h
@@ -63,6 +63,18 @@ struct clk_omap_reg {
* @auto_recal_bit: bitshift of the driftguard enable bit in @control_reg
* @recal_en_bit: bitshift of the PRM_IRQENABLE_* bit for recalibration IRQs
* @recal_st_bit: bitshift of the PRM_IRQSTATUS_* bit for recalibration IRQs
+ * @ssc_deltam_reg: register containing the DPLL SSC frequency spreading
+ * @ssc_modfreq_reg: register containing the DPLL SSC modulation frequency
+ * @ssc_modfreq_mant_mask: mask of the mantissa component in @ssc_modfreq_reg
+ * @ssc_modfreq_exp_mask: mask of the exponent component in @ssc_modfreq_reg
+ * @ssc_enable_mask: mask of the DPLL SSC enable bit in @control_reg
+ * @ssc_ack_mask: mask of the DPLL SSC turned on/off bit in @control_reg
+ * @ssc_downspread_mask: mask of the DPLL SSC low frequency only bit in
+ * @control_reg
+ * @ssc_modfreq: the DPLL SSC frequency modulation in kHz
+ * @ssc_deltam: the DPLL SSC frequency spreading in permille (10th of percent)
+ * @ssc_downspread: require the only low frequency spread of the DPLL in SSC
+ * mode
* @flags: DPLL type/features (see below)
*
* Possible values for @flags:
@@ -110,6 +122,18 @@ struct dpll_data {
u8 auto_recal_bit;
u8 recal_en_bit;
u8 recal_st_bit;
+ struct clk_omap_reg ssc_deltam_reg;
+ struct clk_omap_reg ssc_modfreq_reg;
+ u32 ssc_deltam_int_mask;
+ u32 ssc_deltam_frac_mask;
+ u32 ssc_modfreq_mant_mask;
+ u32 ssc_modfreq_exp_mask;
+ u32 ssc_enable_mask;
+ u32 ssc_ack_mask;
+ u32 ssc_downspread_mask;
+ u32 ssc_modfreq;
+ u32 ssc_deltam;
+ u8 ssc_downspread;
u8 flags;
};

--
2.17.1

2021-03-14 15:16:27

by Dario Binacchi

[permalink] [raw]
Subject: [PATCH 2/4] dt-bindings: ti: dpll: add spread spectrum support

DT bindings for enabling and adjusting spread spectrum clocking have
been added.

Signed-off-by: Dario Binacchi <[email protected]>
---

.../devicetree/bindings/clock/ti/dpll.txt | 20 +++++++++++++++++++
1 file changed, 20 insertions(+)

diff --git a/Documentation/devicetree/bindings/clock/ti/dpll.txt b/Documentation/devicetree/bindings/clock/ti/dpll.txt
index df57009ff8e7..0810ae073294 100644
--- a/Documentation/devicetree/bindings/clock/ti/dpll.txt
+++ b/Documentation/devicetree/bindings/clock/ti/dpll.txt
@@ -42,6 +42,11 @@ Required properties:
"idlest" - contains the idle status register base address
"mult-div1" - contains the multiplier / divider register base address
"autoidle" - contains the autoidle register base address (optional)
+ "ssc-deltam" - DPLL supports spread spectrum clocking (SSC), contains
+ the frequency spreading register base address (optional)
+ "ssc-modfreq" - DPLL supports spread spectrum clocking (SSC), contains
+ the modulation frequency register base address
+ (optional)
ti,am3-* dpll types do not have autoidle register
ti,omap2-* dpll type does not support idlest / autoidle registers

@@ -51,6 +56,14 @@ Optional properties:
- ti,low-power-stop : DPLL supports low power stop mode, gating output
- ti,low-power-bypass : DPLL output matches rate of parent bypass clock
- ti,lock : DPLL locks in programmed rate
+ - ti,min-div : the minimum divisor to start from to round the DPLL
+ target rate
+ - ti,ssc-deltam : DPLL supports spread spectrum clocking, frequency
+ spreading in permille (10th of a percent)
+ - ti,ssc-modfreq : DPLL supports spread spectrum clocking, spread
+ spectrum modulation frequency in kHz
+ - ti,ssc-downspread : DPLL supports spread spectrum clocking, boolean
+ to enable the downspread feature

Examples:
dpll_core_ck: dpll_core_ck@44e00490 {
@@ -83,3 +96,10 @@ Examples:
clocks = <&sys_ck>, <&sys_ck>;
reg = <0x0500>, <0x0540>;
};
+
+ dpll_disp_ck: dpll_disp_ck {
+ #clock-cells = <0>;
+ compatible = "ti,am3-dpll-no-gate-clock";
+ clocks = <&sys_clkin_ck>, <&sys_clkin_ck>;
+ reg = <0x0498>, <0x0448>, <0x0454>, <0x044c>, <0x0450>;
+ };
--
2.17.1

2021-03-14 15:17:14

by Dario Binacchi

[permalink] [raw]
Subject: [PATCH 3/4] ARM: dts: am33xx-clocks: add spread spectrum support

Registers for adjusting the spread spectrum clocking (SSC) have been
added. As reported by the TI spruh73x RM, SSC is supported only for
LCD and MPU PLLs.

Signed-off-by: Dario Binacchi <[email protected]>

---

arch/arm/boot/dts/am33xx-clocks.dtsi | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/arch/arm/boot/dts/am33xx-clocks.dtsi b/arch/arm/boot/dts/am33xx-clocks.dtsi
index dced92a8970e..e7bbbf536a8c 100644
--- a/arch/arm/boot/dts/am33xx-clocks.dtsi
+++ b/arch/arm/boot/dts/am33xx-clocks.dtsi
@@ -164,7 +164,7 @@
#clock-cells = <0>;
compatible = "ti,am3-dpll-core-clock";
clocks = <&sys_clkin_ck>, <&sys_clkin_ck>;
- reg = <0x0490>, <0x045c>, <0x0468>;
+ reg = <0x0490>, <0x045c>, <0x0468>, <0x0460>, <0x0464>;
};

dpll_core_x2_ck: dpll_core_x2_ck {
@@ -244,7 +244,7 @@
#clock-cells = <0>;
compatible = "ti,am3-dpll-no-gate-clock";
clocks = <&sys_clkin_ck>, <&sys_clkin_ck>;
- reg = <0x0498>, <0x0448>, <0x0454>;
+ reg = <0x0498>, <0x0448>, <0x0454>, <0x044c>, <0x0450>;
};

dpll_disp_m2_ck: dpll_disp_m2_ck@4a4 {
--
2.17.1

2021-03-14 15:17:34

by Dario Binacchi

[permalink] [raw]
Subject: [PATCH 1/4] clk: ti: fix typo in routine description

Replace _omap3_noncore_dpll_program with omap3_noncore_dpll_program.

Signed-off-by: Dario Binacchi <[email protected]>
---

drivers/clk/ti/dpll3xxx.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/clk/ti/dpll3xxx.c b/drivers/clk/ti/dpll3xxx.c
index 6097b099a5df..94d5b5fe9a2b 100644
--- a/drivers/clk/ti/dpll3xxx.c
+++ b/drivers/clk/ti/dpll3xxx.c
@@ -292,7 +292,7 @@ static void _lookup_sddiv(struct clk_hw_omap *clk, u8 *sd_div, u16 m, u8 n)
}

/**
- * _omap3_noncore_dpll_program - set non-core DPLL M,N values directly
+ * omap3_noncore_dpll_program - set non-core DPLL M,N values directly
* @clk: struct clk * of DPLL to set
* @freqsel: FREQSEL value to set
*
--
2.17.1

2021-03-16 15:43:25

by Grygorii Strashko

[permalink] [raw]
Subject: Re: [PATCH 4/4] clk: ti: add am33xx spread spectrum clock support



On 14/03/2021 17:12, Dario Binacchi wrote:
> The patch enables spread spectrum clocking (SSC) for MPU and LCD PLLs.
> As reported by the TI spruh73x RM, SSC is only supported for the
> DISP/LCD and MPU PLLs on am33xx device. SSC is not supported for DDR,
> PER, and CORE PLLs.
>
> Calculating the required values and setting the registers accordingly
> was taken from the set_mpu_spreadspectrum routine contained in the
> arch/arm/mach-omap2/am33xx/clock_am33xx.c file of the u-boot project.
>
> In locked condition, DPLL output clock = CLKINP *[M/N]. In case of
> SSC enabled, the AM335x reference manual explains that there is a
> restriction of range of M values. Since the omap2_dpll_round_rate
> routine attempts to select the minimum possible N, the value of M
> obtained is not guaranteed to be within the range required. With the new
> "ti,min-div" parameter it is possible to increase N and consequently M
> to satisfy the constraint imposed by SSC.
>
> Signed-off-by: Dario Binacchi <[email protected]>
>
> ---
>
> arch/arm/boot/dts/am33xx-clocks.dtsi | 4 +-
> drivers/clk/ti/dpll.c | 41 ++++++++++++++
> drivers/clk/ti/dpll3xxx.c | 85 ++++++++++++++++++++++++++++
> include/linux/clk/ti.h | 24 ++++++++
> 4 files changed, 152 insertions(+), 2 deletions(-)
>
> diff --git a/arch/arm/boot/dts/am33xx-clocks.dtsi b/arch/arm/boot/dts/am33xx-clocks.dtsi
> index e7bbbf536a8c..a02e0b1229a4 100644
> --- a/arch/arm/boot/dts/am33xx-clocks.dtsi
> +++ b/arch/arm/boot/dts/am33xx-clocks.dtsi
> @@ -164,7 +164,7 @@
> #clock-cells = <0>;
> compatible = "ti,am3-dpll-core-clock";
> clocks = <&sys_clkin_ck>, <&sys_clkin_ck>;
> - reg = <0x0490>, <0x045c>, <0x0468>, <0x0460>, <0x0464>;
> + reg = <0x0490>, <0x045c>, <0x0468>;
> };
>
> dpll_core_x2_ck: dpll_core_x2_ck {
> @@ -204,7 +204,7 @@
> #clock-cells = <0>;
> compatible = "ti,am3-dpll-clock";
> clocks = <&sys_clkin_ck>, <&sys_clkin_ck>;
> - reg = <0x0488>, <0x0420>, <0x042c>;
> + reg = <0x0488>, <0x0420>, <0x042c>, <0x0424>, <0x0428>;
> };

You can't mix DT vs code.

>
> dpll_mpu_m2_ck: dpll_mpu_m2_ck@4a8 {
> diff --git a/drivers/clk/ti/dpll.c b/drivers/clk/ti/dpll.c
> index d6f1ac5b53e1..2738417a47b7 100644
> --- a/drivers/clk/ti/dpll.c
> +++ b/drivers/clk/ti/dpll.c
> @@ -290,7 +290,9 @@ static void __init of_ti_dpll_setup(struct device_node *node,
> struct clk_init_data *init = NULL;
> const char **parent_names = NULL;
> struct dpll_data *dd = NULL;
> + int ssc_clk_index;
> u8 dpll_mode = 0;
> + u32 min_div;
>
> dd = kmemdup(ddt, sizeof(*dd), GFP_KERNEL);
> clk_hw = kzalloc(sizeof(*clk_hw), GFP_KERNEL);
> @@ -345,6 +347,27 @@ static void __init of_ti_dpll_setup(struct device_node *node,
> if (dd->autoidle_mask) {
> if (ti_clk_get_reg_addr(node, 3, &dd->autoidle_reg))
> goto cleanup;
> +
> + ssc_clk_index = 4;
> + } else {
> + ssc_clk_index = 3;
> + }
> +
> + if (dd->ssc_deltam_int_mask && dd->ssc_deltam_frac_mask &&
> + dd->ssc_modfreq_mant_mask && dd->ssc_modfreq_exp_mask) {
> + if (ti_clk_get_reg_addr(node, ssc_clk_index++,
> + &dd->ssc_deltam_reg))
> + goto cleanup;
> +
> + if (ti_clk_get_reg_addr(node, ssc_clk_index++,
> + &dd->ssc_modfreq_reg))
> + goto cleanup;
> +
> + of_property_read_u32(node, "ti,ssc-modfreq", &dd->ssc_modfreq);
> + of_property_read_u32(node, "ti,ssc-deltam", &dd->ssc_deltam);
> + if (of_property_read_bool(node, "ti,ssc-downspread"))
> + dd->ssc_downspread = 1;

New bindings.

> +
> }
>
> if (of_property_read_bool(node, "ti,low-power-stop"))
> @@ -356,6 +379,10 @@ static void __init of_ti_dpll_setup(struct device_node *node,
> if (of_property_read_bool(node, "ti,lock"))
> dpll_mode |= 1 << DPLL_LOCKED;
>
> + if (!of_property_read_u32(node, "ti,min-div", &min_div) &&
> + min_div > dd->min_divider)
> + dd->min_divider = min_div;
> +

New bindings.

[...]

--
Best regards,
grygorii

2021-03-18 07:40:23

by Dario Binacchi

[permalink] [raw]
Subject: Re: [PATCH 4/4] clk: ti: add am33xx spread spectrum clock support

Hi Grygorii,

> Il 16/03/2021 12:52 Grygorii Strashko <[email protected]> ha scritto:
>
>
> On 14/03/2021 17:12, Dario Binacchi wrote:
> > The patch enables spread spectrum clocking (SSC) for MPU and LCD PLLs.
> > As reported by the TI spruh73x RM, SSC is only supported for the
> > DISP/LCD and MPU PLLs on am33xx device. SSC is not supported for DDR,
> > PER, and CORE PLLs.
> >
> > Calculating the required values and setting the registers accordingly
> > was taken from the set_mpu_spreadspectrum routine contained in the
> > arch/arm/mach-omap2/am33xx/clock_am33xx.c file of the u-boot project.
> >
> > In locked condition, DPLL output clock = CLKINP *[M/N]. In case of
> > SSC enabled, the AM335x reference manual explains that there is a
> > restriction of range of M values. Since the omap2_dpll_round_rate
> > routine attempts to select the minimum possible N, the value of M
> > obtained is not guaranteed to be within the range required. With the new
> > "ti,min-div" parameter it is possible to increase N and consequently M
> > to satisfy the constraint imposed by SSC.
> >
> > Signed-off-by: Dario Binacchi <[email protected]>
> >
> > ---
> >
> > arch/arm/boot/dts/am33xx-clocks.dtsi | 4 +-
> > drivers/clk/ti/dpll.c | 41 ++++++++++++++
> > drivers/clk/ti/dpll3xxx.c | 85 ++++++++++++++++++++++++++++
> > include/linux/clk/ti.h | 24 ++++++++
> > 4 files changed, 152 insertions(+), 2 deletions(-)
> >
> > diff --git a/arch/arm/boot/dts/am33xx-clocks.dtsi b/arch/arm/boot/dts/am33xx-clocks.dtsi
> > index e7bbbf536a8c..a02e0b1229a4 100644
> > --- a/arch/arm/boot/dts/am33xx-clocks.dtsi
> > +++ b/arch/arm/boot/dts/am33xx-clocks.dtsi
> > @@ -164,7 +164,7 @@
> > #clock-cells = <0>;
> > compatible = "ti,am3-dpll-core-clock";
> > clocks = <&sys_clkin_ck>, <&sys_clkin_ck>;
> > - reg = <0x0490>, <0x045c>, <0x0468>, <0x0460>, <0x0464>;
> > + reg = <0x0490>, <0x045c>, <0x0468>;
> > };
> >
> > dpll_core_x2_ck: dpll_core_x2_ck {
> > @@ -204,7 +204,7 @@
> > #clock-cells = <0>;
> > compatible = "ti,am3-dpll-clock";
> > clocks = <&sys_clkin_ck>, <&sys_clkin_ck>;
> > - reg = <0x0488>, <0x0420>, <0x042c>;
> > + reg = <0x0488>, <0x0420>, <0x042c>, <0x0424>, <0x0428>;
> > };
>
> You can't mix DT vs code.

Right, I forgot to remove it during a rebase of the series.

>
> >
> > dpll_mpu_m2_ck: dpll_mpu_m2_ck@4a8 {
> > diff --git a/drivers/clk/ti/dpll.c b/drivers/clk/ti/dpll.c
> > index d6f1ac5b53e1..2738417a47b7 100644
> > --- a/drivers/clk/ti/dpll.c
> > +++ b/drivers/clk/ti/dpll.c
> > @@ -290,7 +290,9 @@ static void __init of_ti_dpll_setup(struct device_node *node,
> > struct clk_init_data *init = NULL;
> > const char **parent_names = NULL;
> > struct dpll_data *dd = NULL;
> > + int ssc_clk_index;
> > u8 dpll_mode = 0;
> > + u32 min_div;
> >
> > dd = kmemdup(ddt, sizeof(*dd), GFP_KERNEL);
> > clk_hw = kzalloc(sizeof(*clk_hw), GFP_KERNEL);
> > @@ -345,6 +347,27 @@ static void __init of_ti_dpll_setup(struct device_node *node,
> > if (dd->autoidle_mask) {
> > if (ti_clk_get_reg_addr(node, 3, &dd->autoidle_reg))
> > goto cleanup;
> > +
> > + ssc_clk_index = 4;
> > + } else {
> > + ssc_clk_index = 3;
> > + }
> > +
> > + if (dd->ssc_deltam_int_mask && dd->ssc_deltam_frac_mask &&
> > + dd->ssc_modfreq_mant_mask && dd->ssc_modfreq_exp_mask) {
> > + if (ti_clk_get_reg_addr(node, ssc_clk_index++,
> > + &dd->ssc_deltam_reg))
> > + goto cleanup;
> > +
> > + if (ti_clk_get_reg_addr(node, ssc_clk_index++,
> > + &dd->ssc_modfreq_reg))
> > + goto cleanup;
> > +
> > + of_property_read_u32(node, "ti,ssc-modfreq", &dd->ssc_modfreq);
> > + of_property_read_u32(node, "ti,ssc-deltam", &dd->ssc_deltam);
> > + if (of_property_read_bool(node, "ti,ssc-downspread"))
> > + dd->ssc_downspread = 1;
>
> New bindings.

I added the bindings documentation in another patch of the series.

Thanks and regards,
Dario
>
> > +
> > }
> >
> > if (of_property_read_bool(node, "ti,low-power-stop"))
> > @@ -356,6 +379,10 @@ static void __init of_ti_dpll_setup(struct device_node *node,
> > if (of_property_read_bool(node, "ti,lock"))
> > dpll_mode |= 1 << DPLL_LOCKED;
> >
> > + if (!of_property_read_u32(node, "ti,min-div", &min_div) &&
> > + min_div > dd->min_divider)
> > + dd->min_divider = min_div;
> > +
>
> New bindings.
>
> [...]
>
> --
> Best regards,
> grygorii

2021-03-18 12:11:56

by Grygorii Strashko

[permalink] [raw]
Subject: Re: [PATCH 4/4] clk: ti: add am33xx spread spectrum clock support



On 18/03/2021 09:38, Dario Binacchi wrote:
> Hi Grygorii,
>
>> Il 16/03/2021 12:52 Grygorii Strashko <[email protected]> ha scritto:
>>
>>
>> On 14/03/2021 17:12, Dario Binacchi wrote:
>>> The patch enables spread spectrum clocking (SSC) for MPU and LCD PLLs.
>>> As reported by the TI spruh73x RM, SSC is only supported for the
>>> DISP/LCD and MPU PLLs on am33xx device. SSC is not supported for DDR,
>>> PER, and CORE PLLs.
>>>
>>> Calculating the required values and setting the registers accordingly
>>> was taken from the set_mpu_spreadspectrum routine contained in the
>>> arch/arm/mach-omap2/am33xx/clock_am33xx.c file of the u-boot project.
>>>
>>> In locked condition, DPLL output clock = CLKINP *[M/N]. In case of
>>> SSC enabled, the AM335x reference manual explains that there is a
>>> restriction of range of M values. Since the omap2_dpll_round_rate
>>> routine attempts to select the minimum possible N, the value of M
>>> obtained is not guaranteed to be within the range required. With the new
>>> "ti,min-div" parameter it is possible to increase N and consequently M
>>> to satisfy the constraint imposed by SSC.
>>>
>>> Signed-off-by: Dario Binacchi <[email protected]>
>>>
>>> ---
>>>
>>> arch/arm/boot/dts/am33xx-clocks.dtsi | 4 +-
>>> drivers/clk/ti/dpll.c | 41 ++++++++++++++
>>> drivers/clk/ti/dpll3xxx.c | 85 ++++++++++++++++++++++++++++
>>> include/linux/clk/ti.h | 24 ++++++++
>>> 4 files changed, 152 insertions(+), 2 deletions(-)
>>>
>>> diff --git a/arch/arm/boot/dts/am33xx-clocks.dtsi b/arch/arm/boot/dts/am33xx-clocks.dtsi
>>> index e7bbbf536a8c..a02e0b1229a4 100644
>>> --- a/arch/arm/boot/dts/am33xx-clocks.dtsi
>>> +++ b/arch/arm/boot/dts/am33xx-clocks.dtsi
>>> @@ -164,7 +164,7 @@
>>> #clock-cells = <0>;
>>> compatible = "ti,am3-dpll-core-clock";
>>> clocks = <&sys_clkin_ck>, <&sys_clkin_ck>;
>>> - reg = <0x0490>, <0x045c>, <0x0468>, <0x0460>, <0x0464>;
>>> + reg = <0x0490>, <0x045c>, <0x0468>;
>>> };
>>>
>>> dpll_core_x2_ck: dpll_core_x2_ck {
>>> @@ -204,7 +204,7 @@
>>> #clock-cells = <0>;
>>> compatible = "ti,am3-dpll-clock";
>>> clocks = <&sys_clkin_ck>, <&sys_clkin_ck>;
>>> - reg = <0x0488>, <0x0420>, <0x042c>;
>>> + reg = <0x0488>, <0x0420>, <0x042c>, <0x0424>, <0x0428>;
>>> };
>>
>> You can't mix DT vs code.
>
> Right, I forgot to remove it during a rebase of the series.
>
>>
>>>
>>> dpll_mpu_m2_ck: dpll_mpu_m2_ck@4a8 {
>>> diff --git a/drivers/clk/ti/dpll.c b/drivers/clk/ti/dpll.c
>>> index d6f1ac5b53e1..2738417a47b7 100644
>>> --- a/drivers/clk/ti/dpll.c
>>> +++ b/drivers/clk/ti/dpll.c
>>> @@ -290,7 +290,9 @@ static void __init of_ti_dpll_setup(struct device_node *node,
>>> struct clk_init_data *init = NULL;
>>> const char **parent_names = NULL;
>>> struct dpll_data *dd = NULL;
>>> + int ssc_clk_index;
>>> u8 dpll_mode = 0;
>>> + u32 min_div;
>>>
>>> dd = kmemdup(ddt, sizeof(*dd), GFP_KERNEL);
>>> clk_hw = kzalloc(sizeof(*clk_hw), GFP_KERNEL);
>>> @@ -345,6 +347,27 @@ static void __init of_ti_dpll_setup(struct device_node *node,
>>> if (dd->autoidle_mask) {
>>> if (ti_clk_get_reg_addr(node, 3, &dd->autoidle_reg))
>>> goto cleanup;
>>> +
>>> + ssc_clk_index = 4;
>>> + } else {
>>> + ssc_clk_index = 3;
>>> + }
>>> +
>>> + if (dd->ssc_deltam_int_mask && dd->ssc_deltam_frac_mask &&
>>> + dd->ssc_modfreq_mant_mask && dd->ssc_modfreq_exp_mask) {
>>> + if (ti_clk_get_reg_addr(node, ssc_clk_index++,
>>> + &dd->ssc_deltam_reg))
>>> + goto cleanup;
>>> +
>>> + if (ti_clk_get_reg_addr(node, ssc_clk_index++,
>>> + &dd->ssc_modfreq_reg))
>>> + goto cleanup;
>>> +
>>> + of_property_read_u32(node, "ti,ssc-modfreq", &dd->ssc_modfreq);
>>> + of_property_read_u32(node, "ti,ssc-deltam", &dd->ssc_deltam);
>>> + if (of_property_read_bool(node, "ti,ssc-downspread"))
>>> + dd->ssc_downspread = 1;
>>
>> New bindings.
>
> I added the bindings documentation in another patch of the series.
>

oh. sorry I've not received it for some reasons :(

--
Best regards,
grygorii