|
@@ -26,8 +26,85 @@
|
|
|
* 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;
|
|
|
|
|
|
+/*
|
|
|
+ * This function can be used by the Kirkwood, the Armada 370, the
|
|
|
+ * Armada XP and the Armada 375 SoC. The name of the function was
|
|
|
+ * chosen following the dt convention: using the first known SoC
|
|
|
+ * compatible with it.
|
|
|
+ */
|
|
|
+u32 kirkwood_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 Marvell engineer we got the following formula (when
|
|
|
+ * this code was written, the datasheet was erroneous)
|
|
|
+ * 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 +139,11 @@ 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->fix_sscg_deviation
|
|
|
+ && desc->is_sscg_enabled(base))
|
|
|
+ rate = desc->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]));
|