summaryrefslogtreecommitdiffstats
path: root/drivers/clk/mvebu
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/clk/mvebu')
-rw-r--r--drivers/clk/mvebu/armada-370.c8
-rw-r--r--drivers/clk/mvebu/armada-375.c4
-rw-r--r--drivers/clk/mvebu/common.c91
-rw-r--r--drivers/clk/mvebu/common.h9
-rw-r--r--drivers/clk/mvebu/kirkwood.c102
5 files changed, 207 insertions, 7 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 25ceccf939ad..b7fcb469c87a 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(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(NULL, "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(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]));
@@ -89,8 +171,10 @@ void __init mvebu_coreclk_setup(struct device_node *np,
* Clock Gating Control
*/
+DEFINE_SPINLOCK(ctrl_gating_lock);
+
struct clk_gating_ctrl {
- spinlock_t lock;
+ spinlock_t *lock;
struct clk **gates;
int num_gates;
};
@@ -138,7 +222,8 @@ void __init mvebu_clk_gating_setup(struct device_node *np,
if (WARN_ON(!ctrl))
goto ctrl_out;
- spin_lock_init(&ctrl->lock);
+ /* lock must already be initialized */
+ ctrl->lock = &ctrl_gating_lock;
/* Count, allocate, and register clock gates */
for (n = 0; desc[n].name;)
@@ -155,7 +240,7 @@ void __init mvebu_clk_gating_setup(struct device_node *np,
(desc[n].parent) ? desc[n].parent : default_parent;
ctrl->gates[n] = clk_register_gate(NULL, desc[n].name, parent,
desc[n].flags, base, desc[n].bit_idx,
- 0, &ctrl->lock);
+ 0, ctrl->lock);
WARN_ON(IS_ERR(ctrl->gates[n]));
}
diff --git a/drivers/clk/mvebu/common.h b/drivers/clk/mvebu/common.h
index f968b4d9df92..783b5631a453 100644
--- a/drivers/clk/mvebu/common.h
+++ b/drivers/clk/mvebu/common.h
@@ -17,6 +17,8 @@
#include <linux/kernel.h>
+extern spinlock_t ctrl_gating_lock;
+
struct device_node;
struct coreclk_ratio {
@@ -28,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)(u32 system_clk);
const struct coreclk_ratio *ratios;
int num_ratios;
};
@@ -45,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(u32 system_clk);
#endif
diff --git a/drivers/clk/mvebu/kirkwood.c b/drivers/clk/mvebu/kirkwood.c
index ddb666a86500..99550f25975e 100644
--- a/drivers/clk/mvebu/kirkwood.c
+++ b/drivers/clk/mvebu/kirkwood.c
@@ -13,9 +13,11 @@
*/
#include <linux/kernel.h>
+#include <linux/slab.h>
#include <linux/clk-provider.h>
#include <linux/io.h>
#include <linux/of.h>
+#include <linux/of_address.h>
#include "common.h"
/*
@@ -214,7 +216,6 @@ static const struct clk_gating_soc_desc kirkwood_gating_desc[] __initconst = {
{ "runit", NULL, 7, 0 },
{ "xor0", NULL, 8, 0 },
{ "audio", NULL, 9, 0 },
- { "powersave", "cpuclk", 11, 0 },
{ "sata0", NULL, 14, 0 },
{ "sata1", NULL, 15, 0 },
{ "xor1", NULL, 16, 0 },
@@ -225,6 +226,101 @@ static const struct clk_gating_soc_desc kirkwood_gating_desc[] __initconst = {
{ }
};
+
+/*
+ * Clock Muxing Control
+ */
+
+struct clk_muxing_soc_desc {
+ const char *name;
+ const char **parents;
+ int num_parents;
+ int shift;
+ int width;
+ unsigned long flags;
+};
+
+struct clk_muxing_ctrl {
+ spinlock_t *lock;
+ struct clk **muxes;
+ int num_muxes;
+};
+
+static const char *powersave_parents[] = {
+ "cpuclk",
+ "ddrclk",
+};
+
+static const struct clk_muxing_soc_desc kirkwood_mux_desc[] __initconst = {
+ { "powersave", powersave_parents, ARRAY_SIZE(powersave_parents),
+ 11, 1, 0 },
+};
+
+#define to_clk_mux(_hw) container_of(_hw, struct clk_mux, hw)
+
+static struct clk *clk_muxing_get_src(
+ struct of_phandle_args *clkspec, void *data)
+{
+ struct clk_muxing_ctrl *ctrl = (struct clk_muxing_ctrl *)data;
+ int n;
+
+ if (clkspec->args_count < 1)
+ return ERR_PTR(-EINVAL);
+
+ for (n = 0; n < ctrl->num_muxes; n++) {
+ struct clk_mux *mux =
+ to_clk_mux(__clk_get_hw(ctrl->muxes[n]));
+ if (clkspec->args[0] == mux->shift)
+ return ctrl->muxes[n];
+ }
+ return ERR_PTR(-ENODEV);
+}
+
+static void __init kirkwood_clk_muxing_setup(struct device_node *np,
+ const struct clk_muxing_soc_desc *desc)
+{
+ struct clk_muxing_ctrl *ctrl;
+ void __iomem *base;
+ int n;
+
+ base = of_iomap(np, 0);
+ if (WARN_ON(!base))
+ return;
+
+ ctrl = kzalloc(sizeof(*ctrl), GFP_KERNEL);
+ if (WARN_ON(!ctrl))
+ goto ctrl_out;
+
+ /* lock must already be initialized */
+ ctrl->lock = &ctrl_gating_lock;
+
+ /* Count, allocate, and register clock muxes */
+ for (n = 0; desc[n].name;)
+ n++;
+
+ ctrl->num_muxes = n;
+ ctrl->muxes = kcalloc(ctrl->num_muxes, sizeof(struct clk *),
+ GFP_KERNEL);
+ if (WARN_ON(!ctrl->muxes))
+ goto muxes_out;
+
+ for (n = 0; n < ctrl->num_muxes; n++) {
+ ctrl->muxes[n] = clk_register_mux(NULL, desc[n].name,
+ desc[n].parents, desc[n].num_parents,
+ desc[n].flags, base, desc[n].shift,
+ desc[n].width, desc[n].flags, ctrl->lock);
+ WARN_ON(IS_ERR(ctrl->muxes[n]));
+ }
+
+ of_clk_add_provider(np, clk_muxing_get_src, ctrl);
+
+ return;
+muxes_out:
+ kfree(ctrl);
+ctrl_out:
+ iounmap(base);
+}
+
static void __init kirkwood_clk_init(struct device_node *np)
{
struct device_node *cgnp =
@@ -236,8 +332,10 @@ static void __init kirkwood_clk_init(struct device_node *np)
else
mvebu_coreclk_setup(np, &kirkwood_coreclks);
- if (cgnp)
+ if (cgnp) {
mvebu_clk_gating_setup(cgnp, kirkwood_gating_desc);
+ kirkwood_clk_muxing_setup(cgnp, kirkwood_mux_desc);
+ }
}
CLK_OF_DECLARE(kirkwood_clk, "marvell,kirkwood-core-clock",
kirkwood_clk_init);