diff options
Diffstat (limited to 'drivers/clk/mvebu')
-rw-r--r-- | drivers/clk/mvebu/armada-370.c | 8 | ||||
-rw-r--r-- | drivers/clk/mvebu/armada-375.c | 4 | ||||
-rw-r--r-- | drivers/clk/mvebu/common.c | 82 | ||||
-rw-r--r-- | drivers/clk/mvebu/common.h | 7 |
4 files changed, 99 insertions, 2 deletions
diff --git a/drivers/clk/mvebu/armada-370.c b/drivers/clk/mvebu/armada-370.c index bef198a83863..756f0f39d6a3 100644 --- a/drivers/clk/mvebu/armada-370.c +++ b/drivers/clk/mvebu/armada-370.c @@ -23,6 +23,7 @@ */ #define SARL 0 /* Low part [0:31] */ +#define SARL_A370_SSCG_ENABLE BIT(10) #define SARL_A370_PCLK_FREQ_OPT 11 #define SARL_A370_PCLK_FREQ_OPT_MASK 0xF #define SARL_A370_FAB_FREQ_OPT 15 @@ -133,10 +134,17 @@ static void __init a370_get_clk_ratio( } } +static bool a370_is_sscg_enabled(void __iomem *sar) +{ + return !(readl(sar) & SARL_A370_SSCG_ENABLE); +} + static const struct coreclk_soc_desc a370_coreclks = { .get_tclk_freq = a370_get_tclk_freq, .get_cpu_freq = a370_get_cpu_freq, .get_clk_ratio = a370_get_clk_ratio, + .is_sscg_enabled = a370_is_sscg_enabled, + .fix_sscg_deviation = kirkwood_fix_sscg_deviation, .ratios = a370_coreclk_ratios, .num_ratios = ARRAY_SIZE(a370_coreclk_ratios), }; diff --git a/drivers/clk/mvebu/armada-375.c b/drivers/clk/mvebu/armada-375.c index c991a4d95e10..c7af2242b796 100644 --- a/drivers/clk/mvebu/armada-375.c +++ b/drivers/clk/mvebu/armada-375.c @@ -27,14 +27,14 @@ * all modified at the same time, and not separately as for the Armada * 370 or the Armada XP SoCs. * - * SAR0[21:17] : CPU frequency DDR frequency L2 frequency + * SAR1[21:17] : CPU frequency DDR frequency L2 frequency * 6 = 400 MHz 400 MHz 200 MHz * 15 = 600 MHz 600 MHz 300 MHz * 21 = 800 MHz 534 MHz 400 MHz * 25 = 1000 MHz 500 MHz 500 MHz * others reserved. * - * SAR0[22] : TCLK frequency + * SAR1[22] : TCLK frequency * 0 = 166 MHz * 1 = 200 MHz */ diff --git a/drivers/clk/mvebu/common.c b/drivers/clk/mvebu/common.c index 8145c4efc381..7f8a33ab265b 100644 --- a/drivers/clk/mvebu/common.c +++ b/drivers/clk/mvebu/common.c @@ -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])); diff --git a/drivers/clk/mvebu/common.h b/drivers/clk/mvebu/common.h index 8cd28e47471c..8f8db7eac3f6 100644 --- a/drivers/clk/mvebu/common.h +++ b/drivers/clk/mvebu/common.h @@ -30,6 +30,8 @@ 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); + u32 (*fix_sscg_deviation)(struct device_node *np, u32 system_clk); const struct coreclk_ratio *ratios; int num_ratios; }; @@ -47,4 +49,9 @@ void __init mvebu_coreclk_setup(struct device_node *np, void __init mvebu_clk_gating_setup(struct device_node *np, const struct clk_gating_soc_desc *desc); +/* + * This function is shared among the Kirkwood, Armada 370, Armada XP + * and Armada 375 SoC + */ +u32 kirkwood_fix_sscg_deviation(struct device_node *np, u32 system_clk); #endif |