aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGregory CLEMENT <gregory.clement@free-electrons.com>2014-09-02 04:15:16 -0400
committerJason Cooper <jason@lakedaemon.net>2014-09-09 11:14:47 -0400
commit15917b16022427c53755abff4dc7051f3076dd7a (patch)
tree8912ac27e630e208a38e5c0bf2873db0c6522efb
parent7d1311b93e58ed55f3a31cc8f94c4b8fe988a2b9 (diff)
clk: mvebu: Fix clk frequency value if SSCG is enabled
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(). The fix is done with the other new optional function related to each SoC: fix_sscg_deviation. If one these functions are not present then no correction is done on the clock frequency. Signed-off-by: Gregory CLEMENT <gregory.clement@free-electrons.com> Tested-by: Leigh Brown <leigh@solinno.co.uk> Link: https://lkml.kernel.org/r/1409645719-20003-2-git-send-email-gregory.clement@free-electrons.com Signed-off-by: Jason Cooper <jason@lakedaemon.net>
-rw-r--r--drivers/clk/mvebu/common.c82
-rw-r--r--drivers/clk/mvebu/common.h7
2 files changed, 89 insertions, 0 deletions
diff --git a/drivers/clk/mvebu/common.c b/drivers/clk/mvebu/common.c
index 25ceccf939ad..354bbadb3bce 100644
--- a/drivers/clk/mvebu/common.c
+++ b/drivers/clk/mvebu/common.c
@@ -26,8 +26,85 @@
26 * Core Clocks 26 * Core Clocks
27 */ 27 */
28 28
29#define SSCG_CONF_MODE(reg) (((reg) >> 16) & 0x3)
30#define SSCG_SPREAD_DOWN 0x0
31#define SSCG_SPREAD_UP 0x1
32#define SSCG_SPREAD_CENTRAL 0x2
33#define SSCG_CONF_LOW(reg) (((reg) >> 8) & 0xFF)
34#define SSCG_CONF_HIGH(reg) ((reg) & 0xFF)
35
29static struct clk_onecell_data clk_data; 36static struct clk_onecell_data clk_data;
30 37
38/*
39 * This function can be used by the Kirkwood, the Armada 370, the
40 * Armada XP and the Armada 375 SoC. The name of the function was
41 * chosen following the dt convention: using the first known SoC
42 * compatible with it.
43 */
44u32 kirkwood_fix_sscg_deviation(struct device_node *np, u32 system_clk)
45{
46 struct device_node *sscg_np = NULL;
47 void __iomem *sscg_map;
48 u32 sscg_reg;
49 s32 low_bound, high_bound;
50 u64 freq_swing_half;
51
52 sscg_np = of_find_node_by_name(np, "sscg");
53 if (sscg_np == NULL) {
54 pr_err("cannot get SSCG register node\n");
55 return system_clk;
56 }
57
58 sscg_map = of_iomap(sscg_np, 0);
59 if (sscg_map == NULL) {
60 pr_err("cannot map SSCG register\n");
61 goto out;
62 }
63
64 sscg_reg = readl(sscg_map);
65 high_bound = SSCG_CONF_HIGH(sscg_reg);
66 low_bound = SSCG_CONF_LOW(sscg_reg);
67
68 if ((high_bound - low_bound) <= 0)
69 goto out;
70 /*
71 * From Marvell engineer we got the following formula (when
72 * this code was written, the datasheet was erroneous)
73 * Spread percentage = 1/96 * (H - L) / H
74 * H = SSCG_High_Boundary
75 * L = SSCG_Low_Boundary
76 *
77 * As the deviation is half of spread then it lead to the
78 * following formula in the code.
79 *
80 * To avoid an overflow and not lose any significant digit in
81 * the same time we have to use a 64 bit integer.
82 */
83
84 freq_swing_half = (((u64)high_bound - (u64)low_bound)
85 * (u64)system_clk);
86 do_div(freq_swing_half, (2 * 96 * high_bound));
87
88 switch (SSCG_CONF_MODE(sscg_reg)) {
89 case SSCG_SPREAD_DOWN:
90 system_clk -= freq_swing_half;
91 break;
92 case SSCG_SPREAD_UP:
93 system_clk += freq_swing_half;
94 break;
95 case SSCG_SPREAD_CENTRAL:
96 default:
97 break;
98 }
99
100 iounmap(sscg_map);
101
102out:
103 of_node_put(sscg_np);
104
105 return system_clk;
106}
107
31void __init mvebu_coreclk_setup(struct device_node *np, 108void __init mvebu_coreclk_setup(struct device_node *np,
32 const struct coreclk_soc_desc *desc) 109 const struct coreclk_soc_desc *desc)
33{ 110{
@@ -62,6 +139,11 @@ void __init mvebu_coreclk_setup(struct device_node *np,
62 of_property_read_string_index(np, "clock-output-names", 1, 139 of_property_read_string_index(np, "clock-output-names", 1,
63 &cpuclk_name); 140 &cpuclk_name);
64 rate = desc->get_cpu_freq(base); 141 rate = desc->get_cpu_freq(base);
142
143 if (desc->is_sscg_enabled && desc->fix_sscg_deviation
144 && desc->is_sscg_enabled(base))
145 rate = desc->fix_sscg_deviation(np, rate);
146
65 clk_data.clks[1] = clk_register_fixed_rate(NULL, cpuclk_name, NULL, 147 clk_data.clks[1] = clk_register_fixed_rate(NULL, cpuclk_name, NULL,
66 CLK_IS_ROOT, rate); 148 CLK_IS_ROOT, rate);
67 WARN_ON(IS_ERR(clk_data.clks[1])); 149 WARN_ON(IS_ERR(clk_data.clks[1]));
diff --git a/drivers/clk/mvebu/common.h b/drivers/clk/mvebu/common.h
index f968b4d9df92..59efaa850bde 100644
--- a/drivers/clk/mvebu/common.h
+++ b/drivers/clk/mvebu/common.h
@@ -28,6 +28,8 @@ struct coreclk_soc_desc {
28 u32 (*get_tclk_freq)(void __iomem *sar); 28 u32 (*get_tclk_freq)(void __iomem *sar);
29 u32 (*get_cpu_freq)(void __iomem *sar); 29 u32 (*get_cpu_freq)(void __iomem *sar);
30 void (*get_clk_ratio)(void __iomem *sar, int id, int *mult, int *div); 30 void (*get_clk_ratio)(void __iomem *sar, int id, int *mult, int *div);
31 bool (*is_sscg_enabled)(void __iomem *sar);
32 u32 (*fix_sscg_deviation)(struct device_node *np, u32 system_clk);
31 const struct coreclk_ratio *ratios; 33 const struct coreclk_ratio *ratios;
32 int num_ratios; 34 int num_ratios;
33}; 35};
@@ -45,4 +47,9 @@ void __init mvebu_coreclk_setup(struct device_node *np,
45void __init mvebu_clk_gating_setup(struct device_node *np, 47void __init mvebu_clk_gating_setup(struct device_node *np,
46 const struct clk_gating_soc_desc *desc); 48 const struct clk_gating_soc_desc *desc);
47 49
50/*
51 * This function is shared among the Kirkwood, Armada 370, Armada XP
52 * and Armada 375 SoC
53 */
54u32 kirkwood_fix_sscg_deviation(struct device_node *np, u32 system_clk);
48#endif 55#endif