Linux-OMAP Archive on lore.kernel.org
 help / color / Atom feed
* [PATCH v5 0/5] clk: ti: add am33xx spread spectrum clock support
@ 2021-04-18 14:56 Dario Binacchi
  2021-04-18 14:56 ` [PATCH v5 1/5] clk: ti: fix typo in routine description Dario Binacchi
                   ` (3 more replies)
  0 siblings, 4 replies; 6+ messages in thread
From: Dario Binacchi @ 2021-04-18 14:56 UTC (permalink / raw)
  To: linux-kernel
  Cc: Dario Binacchi, Tero Kristo, Grygorii Strashko,
	Benoît Cousson, Lee Jones, Michael Turquette, Rob Herring,
	Stephen Boyd, Tony Lindgren, devicetree, linux-clk, linux-omap


As reported by the TI spruh73x/spruhl7x 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/am43xx PLLs for which it is supported. All these issues
have been fixed.


Previous versions of the series did not supported SSC for am43xx SOCs,
causing clock registration failure for DPLLs. Furthermore, for am33xx
SOCs, clock registration failed for DPLLs for which SSC is not supported.

Changes in v5:
- Remove ssc_ack_mask field from dpll_data structure. It was not used.
- Change ssc_downspread type from u8 to bool in dpll_data structure.

Changes in v4:
- Add Stephen Boyd review tag.
- Add Rob Herring review tag.
- Add SSC registers for CORE, DDR and PER PLLs.
- Update commit message.
- Update commit message.

Changes in v3:
- Add '-hz' suffix to "ti,ssc-modfreq" binding.
- Add Tony Lindgren acked tag.
- Use "ti,ssc-modfreq-hz" binding instead of "ti,ssc-modfreq".

Changes in v2:
- Remove SSC registers from dpll_core_ck@490 node (SSC is not supported)
- Add SSC registers to dpll_mpu_ck@488 node.
- Move the DT changes to the previous patch in the series.

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

 .../devicetree/bindings/clock/ti/dpll.txt     | 20 +++++
 arch/arm/boot/dts/am33xx-clocks.dtsi          | 10 +--
 arch/arm/boot/dts/am43xx-clocks.dtsi          | 12 +--
 drivers/clk/ti/dpll.c                         | 39 +++++++++
 drivers/clk/ti/dpll3xxx.c                     | 87 ++++++++++++++++++-
 include/linux/clk/ti.h                        | 22 +++++
 6 files changed, 178 insertions(+), 12 deletions(-)

-- 
2.17.1


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH v5 1/5] clk: ti: fix typo in routine description
  2021-04-18 14:56 [PATCH v5 0/5] clk: ti: add am33xx spread spectrum clock support Dario Binacchi
@ 2021-04-18 14:56 ` Dario Binacchi
  2021-04-18 14:56 ` [PATCH v5 3/5] ARM: dts: am33xx-clocks: add spread spectrum support Dario Binacchi
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 6+ messages in thread
From: Dario Binacchi @ 2021-04-18 14:56 UTC (permalink / raw)
  To: linux-kernel
  Cc: Dario Binacchi, Tero Kristo, Grygorii Strashko, Lee Jones,
	Michael Turquette, Stephen Boyd, linux-clk, linux-omap

Replace _omap3_noncore_dpll_program with omap3_noncore_dpll_program.

Signed-off-by: Dario Binacchi <dariobin@libero.it>
Reviewed-by: Stephen Boyd <sboyd@kernel.org>

---

(no changes since v4)

Changes in v4:
- Add Stephen Boyd review tag.

 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


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH v5 3/5] ARM: dts: am33xx-clocks: add spread spectrum support
  2021-04-18 14:56 [PATCH v5 0/5] clk: ti: add am33xx spread spectrum clock support Dario Binacchi
  2021-04-18 14:56 ` [PATCH v5 1/5] clk: ti: fix typo in routine description Dario Binacchi
@ 2021-04-18 14:56 ` Dario Binacchi
  2021-04-18 14:56 ` [PATCH v5 4/5] ARM: dts: am43xx-clocks: " Dario Binacchi
  2021-04-18 14:56 ` [PATCH v5 5/5] clk: ti: add am33xx/am43xx spread spectrum clock support Dario Binacchi
  3 siblings, 0 replies; 6+ messages in thread
From: Dario Binacchi @ 2021-04-18 14:56 UTC (permalink / raw)
  To: linux-kernel
  Cc: Dario Binacchi, Tero Kristo, Grygorii Strashko,
	Benoît Cousson, Rob Herring, Tony Lindgren, devicetree,
	linux-omap

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, but the CM_SSC_DELTAMSTEP_DPLL_XXX and
CM_SSC_MODFREQDIV_DPLL_XXX registers, as well as the enable field in the
CM_CLKMODE_DPLL_XXX registers are mapped for all PLLs (CORE, MPU, DDR,
PER, DISP).

Signed-off-by: Dario Binacchi <dariobin@libero.it>
Acked-by: Tony Lindgren <tony@atomide.com>

---

(no changes since v4)

Changes in v4:
- Add SSC registers for CORE, DDR and PER PLLs.
- Update commit message.

Changes in v3:
- Add Tony Lindgren acked tag.

Changes in v2:
- Remove SSC registers from dpll_core_ck@490 node (SSC is not supported)
- Add SSC registers to dpll_mpu_ck@488 node.

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

diff --git a/arch/arm/boot/dts/am33xx-clocks.dtsi b/arch/arm/boot/dts/am33xx-clocks.dtsi
index dced92a8970e..b7b7106f2dee 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 {
@@ -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 {
@@ -220,7 +220,7 @@
 		#clock-cells = <0>;
 		compatible = "ti,am3-dpll-no-gate-clock";
 		clocks = <&sys_clkin_ck>, <&sys_clkin_ck>;
-		reg = <0x0494>, <0x0434>, <0x0440>;
+		reg = <0x0494>, <0x0434>, <0x0440>, <0x0438>, <0x043c>;
 	};
 
 	dpll_ddr_m2_ck: dpll_ddr_m2_ck@4a0 {
@@ -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 {
@@ -261,7 +261,7 @@
 		#clock-cells = <0>;
 		compatible = "ti,am3-dpll-no-gate-j-type-clock";
 		clocks = <&sys_clkin_ck>, <&sys_clkin_ck>;
-		reg = <0x048c>, <0x0470>, <0x049c>;
+		reg = <0x048c>, <0x0470>, <0x049c>, <0x0474>, <0x0478>;
 	};
 
 	dpll_per_m2_ck: dpll_per_m2_ck@4ac {
-- 
2.17.1


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH v5 4/5] ARM: dts: am43xx-clocks: add spread spectrum support
  2021-04-18 14:56 [PATCH v5 0/5] clk: ti: add am33xx spread spectrum clock support Dario Binacchi
  2021-04-18 14:56 ` [PATCH v5 1/5] clk: ti: fix typo in routine description Dario Binacchi
  2021-04-18 14:56 ` [PATCH v5 3/5] ARM: dts: am33xx-clocks: add spread spectrum support Dario Binacchi
@ 2021-04-18 14:56 ` Dario Binacchi
  2021-04-18 14:56 ` [PATCH v5 5/5] clk: ti: add am33xx/am43xx spread spectrum clock support Dario Binacchi
  3 siblings, 0 replies; 6+ messages in thread
From: Dario Binacchi @ 2021-04-18 14:56 UTC (permalink / raw)
  To: linux-kernel
  Cc: Dario Binacchi, Tero Kristo, Grygorii Strashko,
	Benoît Cousson, Rob Herring, Tony Lindgren, devicetree,
	linux-omap

Registers for adjusting the spread spectrum clocking (SSC) have been
added. As reported by the TI spruhl7x RM, SSC is supported only for LCD
and MPU PLLs, but the PRCM_CM_SSC_DELTAMSTEP_DPLL_XXX and
PRCM_CM_SSC_MODFREQDIV_DPLL_XXX registers, as well as the enable field
in the PRCM_CM_CLKMODE_DPLL_XXX registers are mapped for all PLLs (CORE,
MPU, DDR, PER, DISP, EXTDEV).

Signed-off-by: Dario Binacchi <dariobin@libero.it>
---

(no changes since v1)

 arch/arm/boot/dts/am43xx-clocks.dtsi | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/arch/arm/boot/dts/am43xx-clocks.dtsi b/arch/arm/boot/dts/am43xx-clocks.dtsi
index c726cd8dbdf1..314fc5975acb 100644
--- a/arch/arm/boot/dts/am43xx-clocks.dtsi
+++ b/arch/arm/boot/dts/am43xx-clocks.dtsi
@@ -204,7 +204,7 @@
 		#clock-cells = <0>;
 		compatible = "ti,am3-dpll-core-clock";
 		clocks = <&sys_clkin_ck>, <&sys_clkin_ck>;
-		reg = <0x2d20>, <0x2d24>, <0x2d2c>;
+		reg = <0x2d20>, <0x2d24>, <0x2d2c>, <0x2d48>, <0x2d4c>;
 	};
 
 	dpll_core_x2_ck: dpll_core_x2_ck {
@@ -250,7 +250,7 @@
 		#clock-cells = <0>;
 		compatible = "ti,am3-dpll-clock";
 		clocks = <&sys_clkin_ck>, <&sys_clkin_ck>;
-		reg = <0x2d60>, <0x2d64>, <0x2d6c>;
+		reg = <0x2d60>, <0x2d64>, <0x2d6c>, <0x2d88>, <0x2d8c>;
 	};
 
 	dpll_mpu_m2_ck: dpll_mpu_m2_ck@2d70 {
@@ -276,7 +276,7 @@
 		#clock-cells = <0>;
 		compatible = "ti,am3-dpll-clock";
 		clocks = <&sys_clkin_ck>, <&sys_clkin_ck>;
-		reg = <0x2da0>, <0x2da4>, <0x2dac>;
+		reg = <0x2da0>, <0x2da4>, <0x2dac>, <0x2dc8>, <0x2dcc>;
 	};
 
 	dpll_ddr_m2_ck: dpll_ddr_m2_ck@2db0 {
@@ -294,7 +294,7 @@
 		#clock-cells = <0>;
 		compatible = "ti,am3-dpll-clock";
 		clocks = <&sys_clkin_ck>, <&sys_clkin_ck>;
-		reg = <0x2e20>, <0x2e24>, <0x2e2c>;
+		reg = <0x2e20>, <0x2e24>, <0x2e2c>, <0x2e48>, <0x2e4c>;
 	};
 
 	dpll_disp_m2_ck: dpll_disp_m2_ck@2e30 {
@@ -313,7 +313,7 @@
 		#clock-cells = <0>;
 		compatible = "ti,am3-dpll-j-type-clock";
 		clocks = <&sys_clkin_ck>, <&sys_clkin_ck>;
-		reg = <0x2de0>, <0x2de4>, <0x2dec>;
+		reg = <0x2de0>, <0x2de4>, <0x2dec>, <0x2e08>, <0x2e0c>;
 	};
 
 	dpll_per_m2_ck: dpll_per_m2_ck@2df0 {
@@ -557,7 +557,7 @@
 		#clock-cells = <0>;
 		compatible = "ti,am3-dpll-clock";
 		clocks = <&sys_clkin_ck>, <&sys_clkin_ck>;
-		reg = <0x2e60>, <0x2e64>, <0x2e6c>;
+		reg = <0x2e60>, <0x2e64>, <0x2e6c>, <0x2e88>, <0x2e8c>;
 	};
 
 	dpll_extdev_m2_ck: dpll_extdev_m2_ck@2e70 {
-- 
2.17.1


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH v5 5/5] clk: ti: add am33xx/am43xx spread spectrum clock support
  2021-04-18 14:56 [PATCH v5 0/5] clk: ti: add am33xx spread spectrum clock support Dario Binacchi
                   ` (2 preceding siblings ...)
  2021-04-18 14:56 ` [PATCH v5 4/5] ARM: dts: am43xx-clocks: " Dario Binacchi
@ 2021-04-18 14:56 ` Dario Binacchi
  2021-04-19 11:26   ` Tero Kristo
  3 siblings, 1 reply; 6+ messages in thread
From: Dario Binacchi @ 2021-04-18 14:56 UTC (permalink / raw)
  To: linux-kernel
  Cc: Dario Binacchi, Tero Kristo, Grygorii Strashko, Lee Jones,
	Michael Turquette, Stephen Boyd, linux-clk, linux-omap

The patch enables spread spectrum clocking (SSC) for MPU and LCD PLLs.
As reported by the TI spruh73x/spruhl7x RM, SSC is only supported for
the DISP/LCD and MPU PLLs on am33xx/am43xx. 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 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 <dariobin@libero.it>

---

Changes in v5:
- Remove ssc_ack_mask field from dpll_data structure. It was not used.
- Change ssc_downspread type from u8 to bool in dpll_data structure.

Changes in v4:
- Update commit message.

Changes in v3:
- Use "ti,ssc-modfreq-hz" binding instead of "ti,ssc-modfreq".

Changes in v2:
- Move the DT changes to the previous patch in the series.

 drivers/clk/ti/dpll.c     | 39 ++++++++++++++++++
 drivers/clk/ti/dpll3xxx.c | 85 +++++++++++++++++++++++++++++++++++++++
 include/linux/clk/ti.h    | 22 ++++++++++
 3 files changed, 146 insertions(+)

diff --git a/drivers/clk/ti/dpll.c b/drivers/clk/ti/dpll.c
index d6f1ac5b53e1..e9f9aee936ae 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-hz",
+				     &dd->ssc_modfreq);
+		of_property_read_u32(node, "ti,ssc-deltam", &dd->ssc_deltam);
+		dd->ssc_downspread =
+			of_property_read_bool(node, "ti,ssc-downspread");
 	}
 
 	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,14 @@ 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_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 +678,14 @@ 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_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..3486f20a3753 100644
--- a/include/linux/clk/ti.h
+++ b/include/linux/clk/ti.h
@@ -63,6 +63,17 @@ 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_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 +121,17 @@ 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_downspread_mask;
+	u32                     ssc_modfreq;
+	u32                     ssc_deltam;
+	bool                    ssc_downspread;
 	u8			flags;
 };
 
-- 
2.17.1


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH v5 5/5] clk: ti: add am33xx/am43xx spread spectrum clock support
  2021-04-18 14:56 ` [PATCH v5 5/5] clk: ti: add am33xx/am43xx spread spectrum clock support Dario Binacchi
@ 2021-04-19 11:26   ` Tero Kristo
  0 siblings, 0 replies; 6+ messages in thread
From: Tero Kristo @ 2021-04-19 11:26 UTC (permalink / raw)
  To: Dario Binacchi, linux-kernel
  Cc: Grygorii Strashko, Lee Jones, Michael Turquette, Stephen Boyd,
	linux-clk, linux-omap

On 18/04/2021 17:56, Dario Binacchi wrote:
> The patch enables spread spectrum clocking (SSC) for MPU and LCD PLLs.
> As reported by the TI spruh73x/spruhl7x RM, SSC is only supported for
> the DISP/LCD and MPU PLLs on am33xx/am43xx. 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 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 <dariobin@libero.it>

Reviewed-by: Tero Kristo <kristo@kernel.org>

> 
> ---
> 
> Changes in v5:
> - Remove ssc_ack_mask field from dpll_data structure. It was not used.
> - Change ssc_downspread type from u8 to bool in dpll_data structure.
> 
> Changes in v4:
> - Update commit message.
> 
> Changes in v3:
> - Use "ti,ssc-modfreq-hz" binding instead of "ti,ssc-modfreq".
> 
> Changes in v2:
> - Move the DT changes to the previous patch in the series.
> 
>   drivers/clk/ti/dpll.c     | 39 ++++++++++++++++++
>   drivers/clk/ti/dpll3xxx.c | 85 +++++++++++++++++++++++++++++++++++++++
>   include/linux/clk/ti.h    | 22 ++++++++++
>   3 files changed, 146 insertions(+)
> 
> diff --git a/drivers/clk/ti/dpll.c b/drivers/clk/ti/dpll.c
> index d6f1ac5b53e1..e9f9aee936ae 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-hz",
> +				     &dd->ssc_modfreq);
> +		of_property_read_u32(node, "ti,ssc-deltam", &dd->ssc_deltam);
> +		dd->ssc_downspread =
> +			of_property_read_bool(node, "ti,ssc-downspread");
>   	}
>   
>   	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,14 @@ 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_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 +678,14 @@ 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_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..3486f20a3753 100644
> --- a/include/linux/clk/ti.h
> +++ b/include/linux/clk/ti.h
> @@ -63,6 +63,17 @@ 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_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 +121,17 @@ 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_downspread_mask;
> +	u32                     ssc_modfreq;
> +	u32                     ssc_deltam;
> +	bool                    ssc_downspread;
>   	u8			flags;
>   };
>   
> 


^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, back to index

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-04-18 14:56 [PATCH v5 0/5] clk: ti: add am33xx spread spectrum clock support Dario Binacchi
2021-04-18 14:56 ` [PATCH v5 1/5] clk: ti: fix typo in routine description Dario Binacchi
2021-04-18 14:56 ` [PATCH v5 3/5] ARM: dts: am33xx-clocks: add spread spectrum support Dario Binacchi
2021-04-18 14:56 ` [PATCH v5 4/5] ARM: dts: am43xx-clocks: " Dario Binacchi
2021-04-18 14:56 ` [PATCH v5 5/5] clk: ti: add am33xx/am43xx spread spectrum clock support Dario Binacchi
2021-04-19 11:26   ` Tero Kristo

Linux-OMAP Archive on lore.kernel.org

Archives are clonable:
	git clone --mirror https://lore.kernel.org/linux-omap/0 linux-omap/git/0.git

	# If you have public-inbox 1.1+ installed, you may
	# initialize and index your mirror using the following commands:
	public-inbox-init -V2 linux-omap linux-omap/ https://lore.kernel.org/linux-omap \
		linux-omap@vger.kernel.org
	public-inbox-index linux-omap

Example config snippet for mirrors

Newsgroup available over NNTP:
	nntp://nntp.lore.kernel.org/org.kernel.vger.linux-omap


AGPL code for this site: git clone https://public-inbox.org/public-inbox.git