* [PATCH 1/4] clk: mvebu: Fix clk frequency value if SSCG is enabled
@ 2014-08-29 11:43 ` Gregory CLEMENT
0 siblings, 0 replies; 24+ messages in thread
From: Gregory CLEMENT @ 2014-08-29 11:43 UTC (permalink / raw)
To: Mike Turquette, linux-kernel
Cc: Jason Cooper, Andrew Lunn, Sebastian Hesselbarth, Gregory CLEMENT,
Thomas Petazzoni, Ezequiel Garcia, linux-arm-kernel, Lior Amsalem,
Tawfik Bayouk, Nadav Haklai, Raphael Rigo, Arnaud Ebalard,
Simon Boulay
When the SSCG (Spread Spectrum Clock Generator) is enabled, it shifts
the frequency of the clock. The percentage is no more than 1% but when
the clock is used for a timer it leads to a clock drift.
This patch allows to correct the affected clock when the SSCG is
enabled. The check is done in an new optional function related to each
SoC: is_sscg_enabled(). If this function is not present then no
correction is done on the clock frequency.
Signed-off-by: Gregory CLEMENT <gregory.clement@free-electrons.com>
---
drivers/clk/mvebu/common.c | 74 ++++++++++++++++++++++++++++++++++++++++++++++
drivers/clk/mvebu/common.h | 1 +
2 files changed, 75 insertions(+)
diff --git a/drivers/clk/mvebu/common.c b/drivers/clk/mvebu/common.c
index 25ceccf939ad..834d36cf79b0 100644
--- a/drivers/clk/mvebu/common.c
+++ b/drivers/clk/mvebu/common.c
@@ -26,8 +26,78 @@
* Core Clocks
*/
+#define SSCG_CONF_MODE(reg) (((reg) >> 16) & 0x3)
+#define SSCG_SPREAD_DOWN 0x0
+#define SSCG_SPREAD_UP 0x1
+#define SSCG_SPREAD_CENTRAL 0x2
+#define SSCG_CONF_LOW(reg) (((reg) >> 8) & 0xFF)
+#define SSCG_CONF_HIGH(reg) ((reg) & 0xFF)
+
static struct clk_onecell_data clk_data;
+static u32 fix_sscg_deviation(struct device_node *np, u32 system_clk)
+{
+ struct device_node *sscg_np = NULL;
+ void __iomem *sscg_map;
+ u32 sscg_reg;
+ s32 low_bound, high_bound;
+ u64 freq_swing_half;
+
+ sscg_np = of_find_node_by_name(np, "sscg");
+ if (sscg_np == NULL) {
+ pr_err("cannot get SSCG register node\n");
+ return system_clk;
+ }
+
+ sscg_map = of_iomap(sscg_np, 0);
+ if (sscg_map == NULL) {
+ pr_err("cannot map SSCG register\n");
+ goto out;
+ }
+
+ sscg_reg = readl(sscg_map);
+ high_bound = SSCG_CONF_HIGH(sscg_reg);
+ low_bound = SSCG_CONF_LOW(sscg_reg);
+
+ if ((high_bound - low_bound) <= 0)
+ goto out;
+ /*
+ * From the datasheet we got the following formula
+ * Spread percentage = 1/96 * (H - L) / H
+ * H = SSCG_High_Boundary
+ * L = SSCG_Low_Boundary
+ *
+ * As the deviation is half of spread then it lead to the
+ * following formula in the code.
+ *
+ * To avoid an overflow and not lose any significant digit in
+ * the same time we have to use a 64 bit integer.
+ */
+
+ freq_swing_half = (((u64)high_bound - (u64)low_bound)
+ * (u64)system_clk);
+ do_div(freq_swing_half, (2 * 96 * high_bound));
+
+ switch (SSCG_CONF_MODE(sscg_reg)) {
+ case SSCG_SPREAD_DOWN:
+ system_clk -= freq_swing_half;
+ break;
+ case SSCG_SPREAD_UP:
+ system_clk += freq_swing_half;
+ break;
+ case SSCG_SPREAD_CENTRAL:
+ default:
+ break;
+ }
+
+ iounmap(sscg_map);
+
+out:
+ of_node_put(sscg_np);
+
+ return system_clk;
+}
+
void __init mvebu_coreclk_setup(struct device_node *np,
const struct coreclk_soc_desc *desc)
{
@@ -62,6 +132,10 @@ void __init mvebu_coreclk_setup(struct device_node *np,
of_property_read_string_index(np, "clock-output-names", 1,
&cpuclk_name);
rate = desc->get_cpu_freq(base);
+
+ if (desc->is_sscg_enabled && desc->is_sscg_enabled(base))
+ rate = fix_sscg_deviation(np, rate);
+
clk_data.clks[1] = clk_register_fixed_rate(NULL, cpuclk_name, NULL,
CLK_IS_ROOT, rate);
WARN_ON(IS_ERR(clk_data.clks[1]));
diff --git a/drivers/clk/mvebu/common.h b/drivers/clk/mvebu/common.h
index f968b4d9df92..495f94ff4c90 100644
--- a/drivers/clk/mvebu/common.h
+++ b/drivers/clk/mvebu/common.h
@@ -28,6 +28,7 @@ struct coreclk_soc_desc {
u32 (*get_tclk_freq)(void __iomem *sar);
u32 (*get_cpu_freq)(void __iomem *sar);
void (*get_clk_ratio)(void __iomem *sar, int id, int *mult, int *div);
+ bool (*is_sscg_enabled)(void __iomem *sar);
const struct coreclk_ratio *ratios;
int num_ratios;
};
--
1.9.1
^ permalink raw reply related [flat|nested] 24+ messages in thread* [PATCH 1/4] clk: mvebu: Fix clk frequency value if SSCG is enabled
2014-08-29 11:43 ` Gregory CLEMENT
@ 2014-08-29 12:48 ` Sebastian Hesselbarth
-1 siblings, 0 replies; 24+ messages in thread
From: Sebastian Hesselbarth @ 2014-08-29 12:48 UTC (permalink / raw)
To: linux-arm-kernel
On 08/29/2014 01:43 PM, Gregory CLEMENT wrote:
> When the SSCG (Spread Spectrum Clock Generator) is enabled, it shifts
> the frequency of the clock. The percentage is no more than 1% but when
> the clock is used for a timer it leads to a clock drift.
>
> This patch allows to correct the affected clock when the SSCG is
> enabled. The check is done in an new optional function related to each
> SoC: is_sscg_enabled(). If this function is not present then no
> correction is done on the clock frequency.
Gregory,
I think computing the SSCG inside the common clk part is a no-go. I
flipped through KW, Dove, Armada 370, and Armada XP FS looking at
SSCG config register layout. Guess what: Dove is different.
How about we have a .get_sscg_deviation() callback instead and move
the one below to some common place where KW, Armada 370, and XP can
set it for their callback struct?
Sebastian
> Signed-off-by: Gregory CLEMENT <gregory.clement@free-electrons.com>
> ---
> drivers/clk/mvebu/common.c | 74 ++++++++++++++++++++++++++++++++++++++++++++++
> drivers/clk/mvebu/common.h | 1 +
> 2 files changed, 75 insertions(+)
>
> diff --git a/drivers/clk/mvebu/common.c b/drivers/clk/mvebu/common.c
> index 25ceccf939ad..834d36cf79b0 100644
> --- a/drivers/clk/mvebu/common.c
> +++ b/drivers/clk/mvebu/common.c
> @@ -26,8 +26,78 @@
> * Core Clocks
> */
>
> +#define SSCG_CONF_MODE(reg) (((reg) >> 16) & 0x3)
> +#define SSCG_SPREAD_DOWN 0x0
> +#define SSCG_SPREAD_UP 0x1
> +#define SSCG_SPREAD_CENTRAL 0x2
> +#define SSCG_CONF_LOW(reg) (((reg) >> 8) & 0xFF)
> +#define SSCG_CONF_HIGH(reg) ((reg) & 0xFF)
> +
> static struct clk_onecell_data clk_data;
>
> +static u32 fix_sscg_deviation(struct device_node *np, u32 system_clk)
> +{
> + struct device_node *sscg_np = NULL;
> + void __iomem *sscg_map;
> + u32 sscg_reg;
> + s32 low_bound, high_bound;
> + u64 freq_swing_half;
> +
> + sscg_np = of_find_node_by_name(np, "sscg");
> + if (sscg_np == NULL) {
> + pr_err("cannot get SSCG register node\n");
> + return system_clk;
> + }
> +
> + sscg_map = of_iomap(sscg_np, 0);
> + if (sscg_map == NULL) {
> + pr_err("cannot map SSCG register\n");
> + goto out;
> + }
> +
> + sscg_reg = readl(sscg_map);
> + high_bound = SSCG_CONF_HIGH(sscg_reg);
> + low_bound = SSCG_CONF_LOW(sscg_reg);
> +
> + if ((high_bound - low_bound) <= 0)
> + goto out;
> + /*
> + * From the datasheet we got the following formula
> + * Spread percentage = 1/96 * (H - L) / H
> + * H = SSCG_High_Boundary
> + * L = SSCG_Low_Boundary
> + *
> + * As the deviation is half of spread then it lead to the
> + * following formula in the code.
> + *
> + * To avoid an overflow and not lose any significant digit in
> + * the same time we have to use a 64 bit integer.
> + */
> +
> + freq_swing_half = (((u64)high_bound - (u64)low_bound)
> + * (u64)system_clk);
> + do_div(freq_swing_half, (2 * 96 * high_bound));
> +
> + switch (SSCG_CONF_MODE(sscg_reg)) {
> + case SSCG_SPREAD_DOWN:
> + system_clk -= freq_swing_half;
> + break;
> + case SSCG_SPREAD_UP:
> + system_clk += freq_swing_half;
> + break;
> + case SSCG_SPREAD_CENTRAL:
> + default:
> + break;
> + }
> +
> + iounmap(sscg_map);
> +
> +out:
> + of_node_put(sscg_np);
> +
> + return system_clk;
> +}
> +
> void __init mvebu_coreclk_setup(struct device_node *np,
> const struct coreclk_soc_desc *desc)
> {
> @@ -62,6 +132,10 @@ void __init mvebu_coreclk_setup(struct device_node *np,
> of_property_read_string_index(np, "clock-output-names", 1,
> &cpuclk_name);
> rate = desc->get_cpu_freq(base);
> +
> + if (desc->is_sscg_enabled && desc->is_sscg_enabled(base))
> + rate = fix_sscg_deviation(np, rate);
> +
> clk_data.clks[1] = clk_register_fixed_rate(NULL, cpuclk_name, NULL,
> CLK_IS_ROOT, rate);
> WARN_ON(IS_ERR(clk_data.clks[1]));
> diff --git a/drivers/clk/mvebu/common.h b/drivers/clk/mvebu/common.h
> index f968b4d9df92..495f94ff4c90 100644
> --- a/drivers/clk/mvebu/common.h
> +++ b/drivers/clk/mvebu/common.h
> @@ -28,6 +28,7 @@ struct coreclk_soc_desc {
> u32 (*get_tclk_freq)(void __iomem *sar);
> u32 (*get_cpu_freq)(void __iomem *sar);
> void (*get_clk_ratio)(void __iomem *sar, int id, int *mult, int *div);
> + bool (*is_sscg_enabled)(void __iomem *sar);
> const struct coreclk_ratio *ratios;
> int num_ratios;
> };
>
^ permalink raw reply [flat|nested] 24+ messages in thread* Re: [PATCH 1/4] clk: mvebu: Fix clk frequency value if SSCG is enabled
@ 2014-08-29 12:48 ` Sebastian Hesselbarth
0 siblings, 0 replies; 24+ messages in thread
From: Sebastian Hesselbarth @ 2014-08-29 12:48 UTC (permalink / raw)
To: Gregory CLEMENT, Mike Turquette, linux-kernel
Cc: Jason Cooper, Andrew Lunn, Thomas Petazzoni, Ezequiel Garcia,
linux-arm-kernel, Lior Amsalem, Tawfik Bayouk, Nadav Haklai,
Raphael Rigo, Arnaud Ebalard, Simon Boulay
On 08/29/2014 01:43 PM, Gregory CLEMENT wrote:
> When the SSCG (Spread Spectrum Clock Generator) is enabled, it shifts
> the frequency of the clock. The percentage is no more than 1% but when
> the clock is used for a timer it leads to a clock drift.
>
> This patch allows to correct the affected clock when the SSCG is
> enabled. The check is done in an new optional function related to each
> SoC: is_sscg_enabled(). If this function is not present then no
> correction is done on the clock frequency.
Gregory,
I think computing the SSCG inside the common clk part is a no-go. I
flipped through KW, Dove, Armada 370, and Armada XP FS looking at
SSCG config register layout. Guess what: Dove is different.
How about we have a .get_sscg_deviation() callback instead and move
the one below to some common place where KW, Armada 370, and XP can
set it for their callback struct?
Sebastian
> Signed-off-by: Gregory CLEMENT <gregory.clement@free-electrons.com>
> ---
> drivers/clk/mvebu/common.c | 74 ++++++++++++++++++++++++++++++++++++++++++++++
> drivers/clk/mvebu/common.h | 1 +
> 2 files changed, 75 insertions(+)
>
> diff --git a/drivers/clk/mvebu/common.c b/drivers/clk/mvebu/common.c
> index 25ceccf939ad..834d36cf79b0 100644
> --- a/drivers/clk/mvebu/common.c
> +++ b/drivers/clk/mvebu/common.c
> @@ -26,8 +26,78 @@
> * Core Clocks
> */
>
> +#define SSCG_CONF_MODE(reg) (((reg) >> 16) & 0x3)
> +#define SSCG_SPREAD_DOWN 0x0
> +#define SSCG_SPREAD_UP 0x1
> +#define SSCG_SPREAD_CENTRAL 0x2
> +#define SSCG_CONF_LOW(reg) (((reg) >> 8) & 0xFF)
> +#define SSCG_CONF_HIGH(reg) ((reg) & 0xFF)
> +
> static struct clk_onecell_data clk_data;
>
> +static u32 fix_sscg_deviation(struct device_node *np, u32 system_clk)
> +{
> + struct device_node *sscg_np = NULL;
> + void __iomem *sscg_map;
> + u32 sscg_reg;
> + s32 low_bound, high_bound;
> + u64 freq_swing_half;
> +
> + sscg_np = of_find_node_by_name(np, "sscg");
> + if (sscg_np == NULL) {
> + pr_err("cannot get SSCG register node\n");
> + return system_clk;
> + }
> +
> + sscg_map = of_iomap(sscg_np, 0);
> + if (sscg_map == NULL) {
> + pr_err("cannot map SSCG register\n");
> + goto out;
> + }
> +
> + sscg_reg = readl(sscg_map);
> + high_bound = SSCG_CONF_HIGH(sscg_reg);
> + low_bound = SSCG_CONF_LOW(sscg_reg);
> +
> + if ((high_bound - low_bound) <= 0)
> + goto out;
> + /*
> + * From the datasheet we got the following formula
> + * Spread percentage = 1/96 * (H - L) / H
> + * H = SSCG_High_Boundary
> + * L = SSCG_Low_Boundary
> + *
> + * As the deviation is half of spread then it lead to the
> + * following formula in the code.
> + *
> + * To avoid an overflow and not lose any significant digit in
> + * the same time we have to use a 64 bit integer.
> + */
> +
> + freq_swing_half = (((u64)high_bound - (u64)low_bound)
> + * (u64)system_clk);
> + do_div(freq_swing_half, (2 * 96 * high_bound));
> +
> + switch (SSCG_CONF_MODE(sscg_reg)) {
> + case SSCG_SPREAD_DOWN:
> + system_clk -= freq_swing_half;
> + break;
> + case SSCG_SPREAD_UP:
> + system_clk += freq_swing_half;
> + break;
> + case SSCG_SPREAD_CENTRAL:
> + default:
> + break;
> + }
> +
> + iounmap(sscg_map);
> +
> +out:
> + of_node_put(sscg_np);
> +
> + return system_clk;
> +}
> +
> void __init mvebu_coreclk_setup(struct device_node *np,
> const struct coreclk_soc_desc *desc)
> {
> @@ -62,6 +132,10 @@ void __init mvebu_coreclk_setup(struct device_node *np,
> of_property_read_string_index(np, "clock-output-names", 1,
> &cpuclk_name);
> rate = desc->get_cpu_freq(base);
> +
> + if (desc->is_sscg_enabled && desc->is_sscg_enabled(base))
> + rate = fix_sscg_deviation(np, rate);
> +
> clk_data.clks[1] = clk_register_fixed_rate(NULL, cpuclk_name, NULL,
> CLK_IS_ROOT, rate);
> WARN_ON(IS_ERR(clk_data.clks[1]));
> diff --git a/drivers/clk/mvebu/common.h b/drivers/clk/mvebu/common.h
> index f968b4d9df92..495f94ff4c90 100644
> --- a/drivers/clk/mvebu/common.h
> +++ b/drivers/clk/mvebu/common.h
> @@ -28,6 +28,7 @@ struct coreclk_soc_desc {
> u32 (*get_tclk_freq)(void __iomem *sar);
> u32 (*get_cpu_freq)(void __iomem *sar);
> void (*get_clk_ratio)(void __iomem *sar, int id, int *mult, int *div);
> + bool (*is_sscg_enabled)(void __iomem *sar);
> const struct coreclk_ratio *ratios;
> int num_ratios;
> };
>
^ permalink raw reply [flat|nested] 24+ messages in thread* [PATCH 1/4] clk: mvebu: Fix clk frequency value if SSCG is enabled
2014-08-29 12:48 ` Sebastian Hesselbarth
@ 2014-08-29 13:35 ` Gregory CLEMENT
-1 siblings, 0 replies; 24+ messages in thread
From: Gregory CLEMENT @ 2014-08-29 13:35 UTC (permalink / raw)
To: linux-arm-kernel
Hi Sebastian,
On 29/08/2014 14:48, Sebastian Hesselbarth wrote:
> On 08/29/2014 01:43 PM, Gregory CLEMENT wrote:
>> When the SSCG (Spread Spectrum Clock Generator) is enabled, it shifts
>> the frequency of the clock. The percentage is no more than 1% but when
>> the clock is used for a timer it leads to a clock drift.
>>
>> This patch allows to correct the affected clock when the SSCG is
>> enabled. The check is done in an new optional function related to each
>> SoC: is_sscg_enabled(). If this function is not present then no
>> correction is done on the clock frequency.
>
> Gregory,
>
> I think computing the SSCG inside the common clk part is a no-go. I
> flipped through KW, Dove, Armada 370, and Armada XP FS looking at
> SSCG config register layout. Guess what: Dove is different.
argh I checked all the other SoC excepting Dove! Sometime I wonder if
the Dove really belongs to the mvebu family
>
> How about we have a .get_sscg_deviation() callback instead and move
> the one below to some common place where KW, Armada 370, and XP can
> set it for their callback struct?
I agree. Given the name of thie file the common place will be here.
Thanks,
Gregory
>
> Sebastian
>
>> Signed-off-by: Gregory CLEMENT <gregory.clement@free-electrons.com>
>> ---
>> drivers/clk/mvebu/common.c | 74 ++++++++++++++++++++++++++++++++++++++++++++++
>> drivers/clk/mvebu/common.h | 1 +
>> 2 files changed, 75 insertions(+)
>>
>> diff --git a/drivers/clk/mvebu/common.c b/drivers/clk/mvebu/common.c
>> index 25ceccf939ad..834d36cf79b0 100644
>> --- a/drivers/clk/mvebu/common.c
>> +++ b/drivers/clk/mvebu/common.c
>> @@ -26,8 +26,78 @@
>> * Core Clocks
>> */
>>
>> +#define SSCG_CONF_MODE(reg) (((reg) >> 16) & 0x3)
>> +#define SSCG_SPREAD_DOWN 0x0
>> +#define SSCG_SPREAD_UP 0x1
>> +#define SSCG_SPREAD_CENTRAL 0x2
>> +#define SSCG_CONF_LOW(reg) (((reg) >> 8) & 0xFF)
>> +#define SSCG_CONF_HIGH(reg) ((reg) & 0xFF)
>> +
>> static struct clk_onecell_data clk_data;
>>
>> +static u32 fix_sscg_deviation(struct device_node *np, u32 system_clk)
>> +{
>> + struct device_node *sscg_np = NULL;
>> + void __iomem *sscg_map;
>> + u32 sscg_reg;
>> + s32 low_bound, high_bound;
>> + u64 freq_swing_half;
>> +
>> + sscg_np = of_find_node_by_name(np, "sscg");
>> + if (sscg_np == NULL) {
>> + pr_err("cannot get SSCG register node\n");
>> + return system_clk;
>> + }
>> +
>> + sscg_map = of_iomap(sscg_np, 0);
>> + if (sscg_map == NULL) {
>> + pr_err("cannot map SSCG register\n");
>> + goto out;
>> + }
>> +
>> + sscg_reg = readl(sscg_map);
>> + high_bound = SSCG_CONF_HIGH(sscg_reg);
>> + low_bound = SSCG_CONF_LOW(sscg_reg);
>> +
>> + if ((high_bound - low_bound) <= 0)
>> + goto out;
>> + /*
>> + * From the datasheet we got the following formula
>> + * Spread percentage = 1/96 * (H - L) / H
>> + * H = SSCG_High_Boundary
>> + * L = SSCG_Low_Boundary
>> + *
>> + * As the deviation is half of spread then it lead to the
>> + * following formula in the code.
>> + *
>> + * To avoid an overflow and not lose any significant digit in
>> + * the same time we have to use a 64 bit integer.
>> + */
>> +
>> + freq_swing_half = (((u64)high_bound - (u64)low_bound)
>> + * (u64)system_clk);
>> + do_div(freq_swing_half, (2 * 96 * high_bound));
>> +
>> + switch (SSCG_CONF_MODE(sscg_reg)) {
>> + case SSCG_SPREAD_DOWN:
>> + system_clk -= freq_swing_half;
>> + break;
>> + case SSCG_SPREAD_UP:
>> + system_clk += freq_swing_half;
>> + break;
>> + case SSCG_SPREAD_CENTRAL:
>> + default:
>> + break;
>> + }
>> +
>> + iounmap(sscg_map);
>> +
>> +out:
>> + of_node_put(sscg_np);
>> +
>> + return system_clk;
>> +}
>> +
>> void __init mvebu_coreclk_setup(struct device_node *np,
>> const struct coreclk_soc_desc *desc)
>> {
>> @@ -62,6 +132,10 @@ void __init mvebu_coreclk_setup(struct device_node *np,
>> of_property_read_string_index(np, "clock-output-names", 1,
>> &cpuclk_name);
>> rate = desc->get_cpu_freq(base);
>> +
>> + if (desc->is_sscg_enabled && desc->is_sscg_enabled(base))
>> + rate = fix_sscg_deviation(np, rate);
>> +
>> clk_data.clks[1] = clk_register_fixed_rate(NULL, cpuclk_name, NULL,
>> CLK_IS_ROOT, rate);
>> WARN_ON(IS_ERR(clk_data.clks[1]));
>> diff --git a/drivers/clk/mvebu/common.h b/drivers/clk/mvebu/common.h
>> index f968b4d9df92..495f94ff4c90 100644
>> --- a/drivers/clk/mvebu/common.h
>> +++ b/drivers/clk/mvebu/common.h
>> @@ -28,6 +28,7 @@ struct coreclk_soc_desc {
>> u32 (*get_tclk_freq)(void __iomem *sar);
>> u32 (*get_cpu_freq)(void __iomem *sar);
>> void (*get_clk_ratio)(void __iomem *sar, int id, int *mult, int *div);
>> + bool (*is_sscg_enabled)(void __iomem *sar);
>> const struct coreclk_ratio *ratios;
>> int num_ratios;
>> };
>>
>
>
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
>
--
Gregory Clement, Free Electrons
Kernel, drivers, real-time and embedded Linux
development, consulting, training and support.
http://free-electrons.com
^ permalink raw reply [flat|nested] 24+ messages in thread* Re: [PATCH 1/4] clk: mvebu: Fix clk frequency value if SSCG is enabled
@ 2014-08-29 13:35 ` Gregory CLEMENT
0 siblings, 0 replies; 24+ messages in thread
From: Gregory CLEMENT @ 2014-08-29 13:35 UTC (permalink / raw)
To: Sebastian Hesselbarth
Cc: Mike Turquette, linux-kernel, Thomas Petazzoni, Andrew Lunn,
Jason Cooper, Tawfik Bayouk, Simon Boulay, Arnaud Ebalard,
Nadav Haklai, Lior Amsalem, Ezequiel Garcia, Raphael Rigo,
linux-arm-kernel
Hi Sebastian,
On 29/08/2014 14:48, Sebastian Hesselbarth wrote:
> On 08/29/2014 01:43 PM, Gregory CLEMENT wrote:
>> When the SSCG (Spread Spectrum Clock Generator) is enabled, it shifts
>> the frequency of the clock. The percentage is no more than 1% but when
>> the clock is used for a timer it leads to a clock drift.
>>
>> This patch allows to correct the affected clock when the SSCG is
>> enabled. The check is done in an new optional function related to each
>> SoC: is_sscg_enabled(). If this function is not present then no
>> correction is done on the clock frequency.
>
> Gregory,
>
> I think computing the SSCG inside the common clk part is a no-go. I
> flipped through KW, Dove, Armada 370, and Armada XP FS looking at
> SSCG config register layout. Guess what: Dove is different.
argh I checked all the other SoC excepting Dove! Sometime I wonder if
the Dove really belongs to the mvebu family
>
> How about we have a .get_sscg_deviation() callback instead and move
> the one below to some common place where KW, Armada 370, and XP can
> set it for their callback struct?
I agree. Given the name of thie file the common place will be here.
Thanks,
Gregory
>
> Sebastian
>
>> Signed-off-by: Gregory CLEMENT <gregory.clement@free-electrons.com>
>> ---
>> drivers/clk/mvebu/common.c | 74 ++++++++++++++++++++++++++++++++++++++++++++++
>> drivers/clk/mvebu/common.h | 1 +
>> 2 files changed, 75 insertions(+)
>>
>> diff --git a/drivers/clk/mvebu/common.c b/drivers/clk/mvebu/common.c
>> index 25ceccf939ad..834d36cf79b0 100644
>> --- a/drivers/clk/mvebu/common.c
>> +++ b/drivers/clk/mvebu/common.c
>> @@ -26,8 +26,78 @@
>> * Core Clocks
>> */
>>
>> +#define SSCG_CONF_MODE(reg) (((reg) >> 16) & 0x3)
>> +#define SSCG_SPREAD_DOWN 0x0
>> +#define SSCG_SPREAD_UP 0x1
>> +#define SSCG_SPREAD_CENTRAL 0x2
>> +#define SSCG_CONF_LOW(reg) (((reg) >> 8) & 0xFF)
>> +#define SSCG_CONF_HIGH(reg) ((reg) & 0xFF)
>> +
>> static struct clk_onecell_data clk_data;
>>
>> +static u32 fix_sscg_deviation(struct device_node *np, u32 system_clk)
>> +{
>> + struct device_node *sscg_np = NULL;
>> + void __iomem *sscg_map;
>> + u32 sscg_reg;
>> + s32 low_bound, high_bound;
>> + u64 freq_swing_half;
>> +
>> + sscg_np = of_find_node_by_name(np, "sscg");
>> + if (sscg_np == NULL) {
>> + pr_err("cannot get SSCG register node\n");
>> + return system_clk;
>> + }
>> +
>> + sscg_map = of_iomap(sscg_np, 0);
>> + if (sscg_map == NULL) {
>> + pr_err("cannot map SSCG register\n");
>> + goto out;
>> + }
>> +
>> + sscg_reg = readl(sscg_map);
>> + high_bound = SSCG_CONF_HIGH(sscg_reg);
>> + low_bound = SSCG_CONF_LOW(sscg_reg);
>> +
>> + if ((high_bound - low_bound) <= 0)
>> + goto out;
>> + /*
>> + * From the datasheet we got the following formula
>> + * Spread percentage = 1/96 * (H - L) / H
>> + * H = SSCG_High_Boundary
>> + * L = SSCG_Low_Boundary
>> + *
>> + * As the deviation is half of spread then it lead to the
>> + * following formula in the code.
>> + *
>> + * To avoid an overflow and not lose any significant digit in
>> + * the same time we have to use a 64 bit integer.
>> + */
>> +
>> + freq_swing_half = (((u64)high_bound - (u64)low_bound)
>> + * (u64)system_clk);
>> + do_div(freq_swing_half, (2 * 96 * high_bound));
>> +
>> + switch (SSCG_CONF_MODE(sscg_reg)) {
>> + case SSCG_SPREAD_DOWN:
>> + system_clk -= freq_swing_half;
>> + break;
>> + case SSCG_SPREAD_UP:
>> + system_clk += freq_swing_half;
>> + break;
>> + case SSCG_SPREAD_CENTRAL:
>> + default:
>> + break;
>> + }
>> +
>> + iounmap(sscg_map);
>> +
>> +out:
>> + of_node_put(sscg_np);
>> +
>> + return system_clk;
>> +}
>> +
>> void __init mvebu_coreclk_setup(struct device_node *np,
>> const struct coreclk_soc_desc *desc)
>> {
>> @@ -62,6 +132,10 @@ void __init mvebu_coreclk_setup(struct device_node *np,
>> of_property_read_string_index(np, "clock-output-names", 1,
>> &cpuclk_name);
>> rate = desc->get_cpu_freq(base);
>> +
>> + if (desc->is_sscg_enabled && desc->is_sscg_enabled(base))
>> + rate = fix_sscg_deviation(np, rate);
>> +
>> clk_data.clks[1] = clk_register_fixed_rate(NULL, cpuclk_name, NULL,
>> CLK_IS_ROOT, rate);
>> WARN_ON(IS_ERR(clk_data.clks[1]));
>> diff --git a/drivers/clk/mvebu/common.h b/drivers/clk/mvebu/common.h
>> index f968b4d9df92..495f94ff4c90 100644
>> --- a/drivers/clk/mvebu/common.h
>> +++ b/drivers/clk/mvebu/common.h
>> @@ -28,6 +28,7 @@ struct coreclk_soc_desc {
>> u32 (*get_tclk_freq)(void __iomem *sar);
>> u32 (*get_cpu_freq)(void __iomem *sar);
>> void (*get_clk_ratio)(void __iomem *sar, int id, int *mult, int *div);
>> + bool (*is_sscg_enabled)(void __iomem *sar);
>> const struct coreclk_ratio *ratios;
>> int num_ratios;
>> };
>>
>
>
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
>
--
Gregory Clement, Free Electrons
Kernel, drivers, real-time and embedded Linux
development, consulting, training and support.
http://free-electrons.com
^ permalink raw reply [flat|nested] 24+ messages in thread
* [PATCH 1/4] clk: mvebu: Fix clk frequency value if SSCG is enabled
2014-08-29 11:43 ` Gregory CLEMENT
@ 2014-08-31 22:25 ` Leigh Brown
-1 siblings, 0 replies; 24+ messages in thread
From: Leigh Brown @ 2014-08-31 22:25 UTC (permalink / raw)
To: linux-arm-kernel
Hi Gregory,
On 2014-08-29 12:43, Gregory CLEMENT wrote:
> When the SSCG (Spread Spectrum Clock Generator) is enabled, it shifts
> the frequency of the clock. The percentage is no more than 1% but when
> the clock is used for a timer it leads to a clock drift.
Thank you so much for this series. I'm running 3.16 and without these
patches ntpd is all over the place, so it is a huge improvement I do
have a comment further down though..
> This patch allows to correct the affected clock when the SSCG is
> enabled. The check is done in an new optional function related to each
> SoC: is_sscg_enabled(). If this function is not present then no
> correction is done on the clock frequency.
>
> Signed-off-by: Gregory CLEMENT <gregory.clement@free-electrons.com>
> ---
> drivers/clk/mvebu/common.c | 74
> ++++++++++++++++++++++++++++++++++++++++++++++
> drivers/clk/mvebu/common.h | 1 +
> 2 files changed, 75 insertions(+)
>
> diff --git a/drivers/clk/mvebu/common.c b/drivers/clk/mvebu/common.c
> index 25ceccf939ad..834d36cf79b0 100644
> --- a/drivers/clk/mvebu/common.c
> +++ b/drivers/clk/mvebu/common.c
> @@ -26,8 +26,78 @@
> * Core Clocks
> */
>
> +#define SSCG_CONF_MODE(reg) (((reg) >> 16) & 0x3)
> +#define SSCG_SPREAD_DOWN 0x0
> +#define SSCG_SPREAD_UP 0x1
> +#define SSCG_SPREAD_CENTRAL 0x2
> +#define SSCG_CONF_LOW(reg) (((reg) >> 8) & 0xFF)
> +#define SSCG_CONF_HIGH(reg) ((reg) & 0xFF)
> +
> static struct clk_onecell_data clk_data;
>
> +static u32 fix_sscg_deviation(struct device_node *np, u32 system_clk)
> +{
> + struct device_node *sscg_np = NULL;
> + void __iomem *sscg_map;
> + u32 sscg_reg;
> + s32 low_bound, high_bound;
> + u64 freq_swing_half;
> +
> + sscg_np = of_find_node_by_name(np, "sscg");
> + if (sscg_np == NULL) {
> + pr_err("cannot get SSCG register node\n");
> + return system_clk;
> + }
> +
> + sscg_map = of_iomap(sscg_np, 0);
> + if (sscg_map == NULL) {
> + pr_err("cannot map SSCG register\n");
> + goto out;
> + }
> +
> + sscg_reg = readl(sscg_map);
> + high_bound = SSCG_CONF_HIGH(sscg_reg);
> + low_bound = SSCG_CONF_LOW(sscg_reg);
> +
> + if ((high_bound - low_bound) <= 0)
> + goto out;
> + /*
> + * From the datasheet we got the following formula
> + * Spread percentage = 1/96 * (H - L) / H
In the datasheet it says the percentage is 0.96 * (H - L) / H. 1/96 is
close but not the same as 0.0096.
> + * H = SSCG_High_Boundary
> + * L = SSCG_Low_Boundary
> + *
> + * As the deviation is half of spread then it lead to the
> + * following formula in the code.
> + *
> + * To avoid an overflow and not lose any significant digit in
> + * the same time we have to use a 64 bit integer.
> + */
> +
> + freq_swing_half = (((u64)high_bound - (u64)low_bound)
> + * (u64)system_clk);
> + do_div(freq_swing_half, (2 * 96 * high_bound));
So this would be become :-
/* NB: 0.96% = 0.0048 or 3/625 */
freq_swing_half = (((u64)high_bound - (u64)low_bound)
* (u64)system_clk * 3);
do_div(freq_swing_half, (625 * high_bound));
> +
> + switch (SSCG_CONF_MODE(sscg_reg)) {
> + case SSCG_SPREAD_DOWN:
> + system_clk -= freq_swing_half;
> + break;
> + case SSCG_SPREAD_UP:
> + system_clk += freq_swing_half;
> + break;
> + case SSCG_SPREAD_CENTRAL:
> + default:
> + break;
> + }
> +
> + iounmap(sscg_map);
> +
> +out:
> + of_node_put(sscg_np);
> +
> + return system_clk;
> +}
> +
> void __init mvebu_coreclk_setup(struct device_node *np,
> const struct coreclk_soc_desc *desc)
> {
> @@ -62,6 +132,10 @@ void __init mvebu_coreclk_setup(struct device_node
> *np,
> of_property_read_string_index(np, "clock-output-names", 1,
> &cpuclk_name);
> rate = desc->get_cpu_freq(base);
> +
> + if (desc->is_sscg_enabled && desc->is_sscg_enabled(base))
> + rate = fix_sscg_deviation(np, rate);
> +
> clk_data.clks[1] = clk_register_fixed_rate(NULL, cpuclk_name, NULL,
> CLK_IS_ROOT, rate);
> WARN_ON(IS_ERR(clk_data.clks[1]));
> diff --git a/drivers/clk/mvebu/common.h b/drivers/clk/mvebu/common.h
> index f968b4d9df92..495f94ff4c90 100644
> --- a/drivers/clk/mvebu/common.h
> +++ b/drivers/clk/mvebu/common.h
> @@ -28,6 +28,7 @@ struct coreclk_soc_desc {
> u32 (*get_tclk_freq)(void __iomem *sar);
> u32 (*get_cpu_freq)(void __iomem *sar);
> void (*get_clk_ratio)(void __iomem *sar, int id, int *mult, int
> *div);
> + bool (*is_sscg_enabled)(void __iomem *sar);
> const struct coreclk_ratio *ratios;
> int num_ratios;
> };
I don't have a good way to measure the difference but my small
modification seems correct as per the datasheet.
Regards,
Leigh.
^ permalink raw reply [flat|nested] 24+ messages in thread* Re: [PATCH 1/4] clk: mvebu: Fix clk frequency value if SSCG is enabled
@ 2014-08-31 22:25 ` Leigh Brown
0 siblings, 0 replies; 24+ messages in thread
From: Leigh Brown @ 2014-08-31 22:25 UTC (permalink / raw)
To: Gregory CLEMENT
Cc: Mike Turquette, linux-kernel, Thomas Petazzoni, Andrew Lunn,
Jason Cooper, Tawfik Bayouk, Simon Boulay, Arnaud Ebalard,
Nadav Haklai, Lior Amsalem, Ezequiel Garcia, Raphael Rigo,
linux-arm-kernel, Sebastian Hesselbarth
Hi Gregory,
On 2014-08-29 12:43, Gregory CLEMENT wrote:
> When the SSCG (Spread Spectrum Clock Generator) is enabled, it shifts
> the frequency of the clock. The percentage is no more than 1% but when
> the clock is used for a timer it leads to a clock drift.
Thank you so much for this series. I'm running 3.16 and without these
patches ntpd is all over the place, so it is a huge improvement I do
have a comment further down though..
> This patch allows to correct the affected clock when the SSCG is
> enabled. The check is done in an new optional function related to each
> SoC: is_sscg_enabled(). If this function is not present then no
> correction is done on the clock frequency.
>
> Signed-off-by: Gregory CLEMENT <gregory.clement@free-electrons.com>
> ---
> drivers/clk/mvebu/common.c | 74
> ++++++++++++++++++++++++++++++++++++++++++++++
> drivers/clk/mvebu/common.h | 1 +
> 2 files changed, 75 insertions(+)
>
> diff --git a/drivers/clk/mvebu/common.c b/drivers/clk/mvebu/common.c
> index 25ceccf939ad..834d36cf79b0 100644
> --- a/drivers/clk/mvebu/common.c
> +++ b/drivers/clk/mvebu/common.c
> @@ -26,8 +26,78 @@
> * Core Clocks
> */
>
> +#define SSCG_CONF_MODE(reg) (((reg) >> 16) & 0x3)
> +#define SSCG_SPREAD_DOWN 0x0
> +#define SSCG_SPREAD_UP 0x1
> +#define SSCG_SPREAD_CENTRAL 0x2
> +#define SSCG_CONF_LOW(reg) (((reg) >> 8) & 0xFF)
> +#define SSCG_CONF_HIGH(reg) ((reg) & 0xFF)
> +
> static struct clk_onecell_data clk_data;
>
> +static u32 fix_sscg_deviation(struct device_node *np, u32 system_clk)
> +{
> + struct device_node *sscg_np = NULL;
> + void __iomem *sscg_map;
> + u32 sscg_reg;
> + s32 low_bound, high_bound;
> + u64 freq_swing_half;
> +
> + sscg_np = of_find_node_by_name(np, "sscg");
> + if (sscg_np == NULL) {
> + pr_err("cannot get SSCG register node\n");
> + return system_clk;
> + }
> +
> + sscg_map = of_iomap(sscg_np, 0);
> + if (sscg_map == NULL) {
> + pr_err("cannot map SSCG register\n");
> + goto out;
> + }
> +
> + sscg_reg = readl(sscg_map);
> + high_bound = SSCG_CONF_HIGH(sscg_reg);
> + low_bound = SSCG_CONF_LOW(sscg_reg);
> +
> + if ((high_bound - low_bound) <= 0)
> + goto out;
> + /*
> + * From the datasheet we got the following formula
> + * Spread percentage = 1/96 * (H - L) / H
In the datasheet it says the percentage is 0.96 * (H - L) / H. 1/96 is
close but not the same as 0.0096.
> + * H = SSCG_High_Boundary
> + * L = SSCG_Low_Boundary
> + *
> + * As the deviation is half of spread then it lead to the
> + * following formula in the code.
> + *
> + * To avoid an overflow and not lose any significant digit in
> + * the same time we have to use a 64 bit integer.
> + */
> +
> + freq_swing_half = (((u64)high_bound - (u64)low_bound)
> + * (u64)system_clk);
> + do_div(freq_swing_half, (2 * 96 * high_bound));
So this would be become :-
/* NB: 0.96% = 0.0048 or 3/625 */
freq_swing_half = (((u64)high_bound - (u64)low_bound)
* (u64)system_clk * 3);
do_div(freq_swing_half, (625 * high_bound));
> +
> + switch (SSCG_CONF_MODE(sscg_reg)) {
> + case SSCG_SPREAD_DOWN:
> + system_clk -= freq_swing_half;
> + break;
> + case SSCG_SPREAD_UP:
> + system_clk += freq_swing_half;
> + break;
> + case SSCG_SPREAD_CENTRAL:
> + default:
> + break;
> + }
> +
> + iounmap(sscg_map);
> +
> +out:
> + of_node_put(sscg_np);
> +
> + return system_clk;
> +}
> +
> void __init mvebu_coreclk_setup(struct device_node *np,
> const struct coreclk_soc_desc *desc)
> {
> @@ -62,6 +132,10 @@ void __init mvebu_coreclk_setup(struct device_node
> *np,
> of_property_read_string_index(np, "clock-output-names", 1,
> &cpuclk_name);
> rate = desc->get_cpu_freq(base);
> +
> + if (desc->is_sscg_enabled && desc->is_sscg_enabled(base))
> + rate = fix_sscg_deviation(np, rate);
> +
> clk_data.clks[1] = clk_register_fixed_rate(NULL, cpuclk_name, NULL,
> CLK_IS_ROOT, rate);
> WARN_ON(IS_ERR(clk_data.clks[1]));
> diff --git a/drivers/clk/mvebu/common.h b/drivers/clk/mvebu/common.h
> index f968b4d9df92..495f94ff4c90 100644
> --- a/drivers/clk/mvebu/common.h
> +++ b/drivers/clk/mvebu/common.h
> @@ -28,6 +28,7 @@ struct coreclk_soc_desc {
> u32 (*get_tclk_freq)(void __iomem *sar);
> u32 (*get_cpu_freq)(void __iomem *sar);
> void (*get_clk_ratio)(void __iomem *sar, int id, int *mult, int
> *div);
> + bool (*is_sscg_enabled)(void __iomem *sar);
> const struct coreclk_ratio *ratios;
> int num_ratios;
> };
I don't have a good way to measure the difference but my small
modification seems correct as per the datasheet.
Regards,
Leigh.
^ permalink raw reply [flat|nested] 24+ messages in thread* [PATCH 1/4] clk: mvebu: Fix clk frequency value if SSCG is enabled
2014-08-31 22:25 ` Leigh Brown
@ 2014-08-31 22:30 ` Leigh Brown
-1 siblings, 0 replies; 24+ messages in thread
From: Leigh Brown @ 2014-08-31 22:30 UTC (permalink / raw)
To: linux-arm-kernel
On 2014-08-31 23:25, Leigh Brown wrote:
[...]
> /* NB: 0.96% = 0.0048 or 3/625 */
> freq_swing_half = (((u64)high_bound - (u64)low_bound)
> * (u64)system_clk * 3);
> do_div(freq_swing_half, (625 * high_bound));
Sigh. The comment should be:
/* NB: half of 0.96% is 0.0048 or 3/625 */
The code should be right :-)
Regards,
Leigh.
^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH 1/4] clk: mvebu: Fix clk frequency value if SSCG is enabled
@ 2014-08-31 22:30 ` Leigh Brown
0 siblings, 0 replies; 24+ messages in thread
From: Leigh Brown @ 2014-08-31 22:30 UTC (permalink / raw)
To: Gregory CLEMENT
Cc: Mike Turquette, linux-kernel, Thomas Petazzoni, Andrew Lunn,
Jason Cooper, Tawfik Bayouk, Simon Boulay, Arnaud Ebalard,
Nadav Haklai, Lior Amsalem, Ezequiel Garcia, Raphael Rigo,
linux-arm-kernel, Sebastian Hesselbarth
On 2014-08-31 23:25, Leigh Brown wrote:
[...]
> /* NB: 0.96% = 0.0048 or 3/625 */
> freq_swing_half = (((u64)high_bound - (u64)low_bound)
> * (u64)system_clk * 3);
> do_div(freq_swing_half, (625 * high_bound));
Sigh. The comment should be:
/* NB: half of 0.96% is 0.0048 or 3/625 */
The code should be right :-)
Regards,
Leigh.
^ permalink raw reply [flat|nested] 24+ messages in thread
* [PATCH 1/4] clk: mvebu: Fix clk frequency value if SSCG is enabled
2014-08-31 22:25 ` Leigh Brown
@ 2014-09-01 7:17 ` Gregory CLEMENT
-1 siblings, 0 replies; 24+ messages in thread
From: Gregory CLEMENT @ 2014-09-01 7:17 UTC (permalink / raw)
To: linux-arm-kernel
Hi Leigh,
On 01/09/2014 00:25, Leigh Brown wrote:
> Hi Gregory,
>
> On 2014-08-29 12:43, Gregory CLEMENT wrote:
>> When the SSCG (Spread Spectrum Clock Generator) is enabled, it shifts
>> the frequency of the clock. The percentage is no more than 1% but when
>> the clock is used for a timer it leads to a clock drift.
>
> Thank you so much for this series. I'm running 3.16 and without these
> patches ntpd is all over the place, so it is a huge improvement I do
> have a comment further down though..
>
>> This patch allows to correct the affected clock when the SSCG is
>> enabled. The check is done in an new optional function related to each
>> SoC: is_sscg_enabled(). If this function is not present then no
>> correction is done on the clock frequency.
>>
>> Signed-off-by: Gregory CLEMENT <gregory.clement@free-electrons.com>
>> ---
>> drivers/clk/mvebu/common.c | 74
>> ++++++++++++++++++++++++++++++++++++++++++++++
>> drivers/clk/mvebu/common.h | 1 +
>> 2 files changed, 75 insertions(+)
>>
>> diff --git a/drivers/clk/mvebu/common.c b/drivers/clk/mvebu/common.c
>> index 25ceccf939ad..834d36cf79b0 100644
>> --- a/drivers/clk/mvebu/common.c
>> +++ b/drivers/clk/mvebu/common.c
>> @@ -26,8 +26,78 @@
>> * Core Clocks
>> */
>>
>> +#define SSCG_CONF_MODE(reg) (((reg) >> 16) & 0x3)
>> +#define SSCG_SPREAD_DOWN 0x0
>> +#define SSCG_SPREAD_UP 0x1
>> +#define SSCG_SPREAD_CENTRAL 0x2
>> +#define SSCG_CONF_LOW(reg) (((reg) >> 8) & 0xFF)
>> +#define SSCG_CONF_HIGH(reg) ((reg) & 0xFF)
>> +
>> static struct clk_onecell_data clk_data;
>>
>> +static u32 fix_sscg_deviation(struct device_node *np, u32 system_clk)
>> +{
>> + struct device_node *sscg_np = NULL;
>> + void __iomem *sscg_map;
>> + u32 sscg_reg;
>> + s32 low_bound, high_bound;
>> + u64 freq_swing_half;
>> +
>> + sscg_np = of_find_node_by_name(np, "sscg");
>> + if (sscg_np == NULL) {
>> + pr_err("cannot get SSCG register node\n");
>> + return system_clk;
>> + }
>> +
>> + sscg_map = of_iomap(sscg_np, 0);
>> + if (sscg_map == NULL) {
>> + pr_err("cannot map SSCG register\n");
>> + goto out;
>> + }
>> +
>> + sscg_reg = readl(sscg_map);
>> + high_bound = SSCG_CONF_HIGH(sscg_reg);
>> + low_bound = SSCG_CONF_LOW(sscg_reg);
>> +
>> + if ((high_bound - low_bound) <= 0)
>> + goto out;
>> + /*
>> + * From the datasheet we got the following formula
>> + * Spread percentage = 1/96 * (H - L) / H
>
> In the datasheet it says the percentage is 0.96 * (H - L) / H. 1/96 is
> close but not the same as 0.0096.
Actually the public datasheet is wrong (even the NDA datasheet).
>
>> + * H = SSCG_High_Boundary
>> + * L = SSCG_Low_Boundary
>> + *
>> + * As the deviation is half of spread then it lead to the
>> + * following formula in the code.
>> + *
>> + * To avoid an overflow and not lose any significant digit in
>> + * the same time we have to use a 64 bit integer.
>> + */
>> +
>> + freq_swing_half = (((u64)high_bound - (u64)low_bound)
>> + * (u64)system_clk);
>> + do_div(freq_swing_half, (2 * 96 * high_bound));
>
> So this would be become :-
>
> /* NB: 0.96% = 0.0048 or 3/625 */
> freq_swing_half = (((u64)high_bound - (u64)low_bound)
> * (u64)system_clk * 3);
> do_div(freq_swing_half, (625 * high_bound));
It was the first implementation I tried, but with this one
the drift was about 200ppm instead of 50ppm. After having
checked this with the Marvell engineers, they confirmed me
that the datasheet was erroneous.
Thanks for your feedback: I will add a comment in the code
about the fact that the datasheet is erroneous.
--
Gregory Clement, Free Electrons
Kernel, drivers, real-time and embedded Linux
development, consulting, training and support.
http://free-electrons.com
^ permalink raw reply [flat|nested] 24+ messages in thread* Re: [PATCH 1/4] clk: mvebu: Fix clk frequency value if SSCG is enabled
@ 2014-09-01 7:17 ` Gregory CLEMENT
0 siblings, 0 replies; 24+ messages in thread
From: Gregory CLEMENT @ 2014-09-01 7:17 UTC (permalink / raw)
To: Leigh Brown
Cc: Mike Turquette, linux-kernel, Thomas Petazzoni, Andrew Lunn,
Jason Cooper, Tawfik Bayouk, Simon Boulay, Arnaud Ebalard,
Nadav Haklai, Lior Amsalem, Ezequiel Garcia, Raphael Rigo,
linux-arm-kernel, Sebastian Hesselbarth
Hi Leigh,
On 01/09/2014 00:25, Leigh Brown wrote:
> Hi Gregory,
>
> On 2014-08-29 12:43, Gregory CLEMENT wrote:
>> When the SSCG (Spread Spectrum Clock Generator) is enabled, it shifts
>> the frequency of the clock. The percentage is no more than 1% but when
>> the clock is used for a timer it leads to a clock drift.
>
> Thank you so much for this series. I'm running 3.16 and without these
> patches ntpd is all over the place, so it is a huge improvement I do
> have a comment further down though..
>
>> This patch allows to correct the affected clock when the SSCG is
>> enabled. The check is done in an new optional function related to each
>> SoC: is_sscg_enabled(). If this function is not present then no
>> correction is done on the clock frequency.
>>
>> Signed-off-by: Gregory CLEMENT <gregory.clement@free-electrons.com>
>> ---
>> drivers/clk/mvebu/common.c | 74
>> ++++++++++++++++++++++++++++++++++++++++++++++
>> drivers/clk/mvebu/common.h | 1 +
>> 2 files changed, 75 insertions(+)
>>
>> diff --git a/drivers/clk/mvebu/common.c b/drivers/clk/mvebu/common.c
>> index 25ceccf939ad..834d36cf79b0 100644
>> --- a/drivers/clk/mvebu/common.c
>> +++ b/drivers/clk/mvebu/common.c
>> @@ -26,8 +26,78 @@
>> * Core Clocks
>> */
>>
>> +#define SSCG_CONF_MODE(reg) (((reg) >> 16) & 0x3)
>> +#define SSCG_SPREAD_DOWN 0x0
>> +#define SSCG_SPREAD_UP 0x1
>> +#define SSCG_SPREAD_CENTRAL 0x2
>> +#define SSCG_CONF_LOW(reg) (((reg) >> 8) & 0xFF)
>> +#define SSCG_CONF_HIGH(reg) ((reg) & 0xFF)
>> +
>> static struct clk_onecell_data clk_data;
>>
>> +static u32 fix_sscg_deviation(struct device_node *np, u32 system_clk)
>> +{
>> + struct device_node *sscg_np = NULL;
>> + void __iomem *sscg_map;
>> + u32 sscg_reg;
>> + s32 low_bound, high_bound;
>> + u64 freq_swing_half;
>> +
>> + sscg_np = of_find_node_by_name(np, "sscg");
>> + if (sscg_np == NULL) {
>> + pr_err("cannot get SSCG register node\n");
>> + return system_clk;
>> + }
>> +
>> + sscg_map = of_iomap(sscg_np, 0);
>> + if (sscg_map == NULL) {
>> + pr_err("cannot map SSCG register\n");
>> + goto out;
>> + }
>> +
>> + sscg_reg = readl(sscg_map);
>> + high_bound = SSCG_CONF_HIGH(sscg_reg);
>> + low_bound = SSCG_CONF_LOW(sscg_reg);
>> +
>> + if ((high_bound - low_bound) <= 0)
>> + goto out;
>> + /*
>> + * From the datasheet we got the following formula
>> + * Spread percentage = 1/96 * (H - L) / H
>
> In the datasheet it says the percentage is 0.96 * (H - L) / H. 1/96 is
> close but not the same as 0.0096.
Actually the public datasheet is wrong (even the NDA datasheet).
>
>> + * H = SSCG_High_Boundary
>> + * L = SSCG_Low_Boundary
>> + *
>> + * As the deviation is half of spread then it lead to the
>> + * following formula in the code.
>> + *
>> + * To avoid an overflow and not lose any significant digit in
>> + * the same time we have to use a 64 bit integer.
>> + */
>> +
>> + freq_swing_half = (((u64)high_bound - (u64)low_bound)
>> + * (u64)system_clk);
>> + do_div(freq_swing_half, (2 * 96 * high_bound));
>
> So this would be become :-
>
> /* NB: 0.96% = 0.0048 or 3/625 */
> freq_swing_half = (((u64)high_bound - (u64)low_bound)
> * (u64)system_clk * 3);
> do_div(freq_swing_half, (625 * high_bound));
It was the first implementation I tried, but with this one
the drift was about 200ppm instead of 50ppm. After having
checked this with the Marvell engineers, they confirmed me
that the datasheet was erroneous.
Thanks for your feedback: I will add a comment in the code
about the fact that the datasheet is erroneous.
--
Gregory Clement, Free Electrons
Kernel, drivers, real-time and embedded Linux
development, consulting, training and support.
http://free-electrons.com
^ permalink raw reply [flat|nested] 24+ messages in thread