Re: [PATCH v5 5/5] clk: ti: add am33xx/am43xx spread spectrum clock support

2021-04-19 Thread Tero Kristo

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 


Reviewed-by: Tero Kristo 



---

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 = 0x3,
+   .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 = 0x3,
+   .ssc_modfreq_mant_mask = 0x7f,
+   .ssc_modfreq_exp_mask = 0x7 << 8,
.max_multiplier = 2047,
.max_divider = 128,
 

[PATCH v5 5/5] clk: ti: add am33xx/am43xx spread spectrum clock support

2021-04-18 Thread Dario Binacchi
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 

---

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 = 0x3,
+   .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 = 0x3,
+   .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/