clk: Vexpress-spc: Add clock driver
authorViresh Kumar <viresh.kumar@linaro.org>
Fri, 23 Nov 2012 19:21:07 +0000 (00:51 +0530)
committerJon Medhurst <tixy@linaro.org>
Mon, 1 Jul 2013 10:06:06 +0000 (11:06 +0100)
This patch adds spc clock controller. In Vexpress cpu cluster clock is controlled via
spc controller and so it must be present in clk framework.

vexpress_clk_[of_]register_spc() registers cluster clocks with and without DT.
These are added as root clocks without any parents and their names are
"cluster[0|1|..]".

Now, platform must add clocks of all the cpus below these clusters. cpufreq
driver would get cpu clock and will do clk_get[set]_rate() on cpu clock, which
will then pass it to cluster clocks. And finally spc will get programmed.

This patch doesn't add non-DT clocks for clusters and cpus as i don't see a user
of that for now.

Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>
drivers/clk/versatile/Makefile
drivers/clk/versatile/clk-vexpress-spc.c [new file with mode: 0644]
include/linux/vexpress.h

index c16ca787170a59fb60c7235752e2f762dea3300e..6e76bf87ca8763b4fc3a772057f7d9777c968956 100644 (file)
@@ -4,4 +4,4 @@ obj-$(CONFIG_ARCH_INTEGRATOR)   += clk-integrator.o
 obj-$(CONFIG_INTEGRATOR_IMPD1) += clk-impd1.o
 obj-$(CONFIG_ARCH_REALVIEW)    += clk-realview.o
 obj-$(CONFIG_ARCH_VEXPRESS)    += clk-vexpress.o clk-sp810.o
-obj-$(CONFIG_VEXPRESS_CONFIG)  += clk-vexpress-osc.o
+obj-$(CONFIG_VEXPRESS_CONFIG)  += clk-vexpress-osc.o clk-vexpress-spc.o
diff --git a/drivers/clk/versatile/clk-vexpress-spc.c b/drivers/clk/versatile/clk-vexpress-spc.c
new file mode 100644 (file)
index 0000000..b701ae4
--- /dev/null
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2012 ARM Limited
+ * Copyright (C) 2012 Linaro
+ *
+ * Author: Viresh Kumar <viresh.kumar@linaro.org>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+/* SPC clock programming interface for Vexpress cpus */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/vexpress.h>
+
+struct clk_spc {
+       struct clk_hw hw;
+       spinlock_t *lock;
+       int cluster;
+};
+
+#define to_clk_spc(spc) container_of(spc, struct clk_spc, hw)
+
+static unsigned long spc_recalc_rate(struct clk_hw *hw,
+               unsigned long parent_rate)
+{
+       struct clk_spc *spc = to_clk_spc(hw);
+       u32 freq;
+
+       if (vexpress_spc_get_performance(spc->cluster, &freq)) {
+               return -EIO;
+               pr_err("%s: Failed", __func__);
+       }
+
+       return freq * 1000;
+}
+
+static long spc_round_rate(struct clk_hw *hw, unsigned long drate,
+               unsigned long *parent_rate)
+{
+       return drate;
+}
+
+static int spc_set_rate(struct clk_hw *hw, unsigned long rate,
+               unsigned long parent_rate)
+{
+       struct clk_spc *spc = to_clk_spc(hw);
+
+       return vexpress_spc_set_performance(spc->cluster, rate / 1000);
+}
+
+static struct clk_ops clk_spc_ops = {
+       .recalc_rate = spc_recalc_rate,
+       .round_rate = spc_round_rate,
+       .set_rate = spc_set_rate,
+};
+
+struct clk *vexpress_clk_register_spc(const char *name, int cluster_id)
+{
+       struct clk_init_data init;
+       struct clk_spc *spc;
+       struct clk *clk;
+
+       if (!name) {
+               pr_err("Invalid name passed");
+               return ERR_PTR(-EINVAL);
+       }
+
+       spc = kzalloc(sizeof(*spc), GFP_KERNEL);
+       if (!spc) {
+               pr_err("could not allocate spc clk\n");
+               return ERR_PTR(-ENOMEM);
+       }
+
+       spc->hw.init = &init;
+       spc->cluster = cluster_id;
+
+       init.name = name;
+       init.ops = &clk_spc_ops;
+       init.flags = CLK_IS_ROOT | CLK_GET_RATE_NOCACHE;
+       init.num_parents = 0;
+
+       clk = clk_register(NULL, &spc->hw);
+       if (!IS_ERR_OR_NULL(clk))
+               return clk;
+
+       pr_err("clk register failed\n");
+       kfree(spc);
+
+       return NULL;
+}
+
+#if defined(CONFIG_OF)
+void __init vexpress_clk_of_register_spc(void)
+{
+       char name[14] = "cpu-cluster.";
+       struct device_node *node = NULL;
+       struct clk *clk;
+       const u32 *val;
+       int cluster_id = 0, len;
+
+       if (!of_find_compatible_node(NULL, NULL, "arm,spc")) {
+               pr_debug("%s: No SPC found, Exiting!!\n", __func__);
+               return;
+       }
+
+       while ((node = of_find_node_by_name(node, "cluster"))) {
+               val = of_get_property(node, "reg", &len);
+               if (val && len == 4)
+                       cluster_id = be32_to_cpup(val);
+
+               name[12] = cluster_id + '0';
+               clk = vexpress_clk_register_spc(name, cluster_id);
+               if (IS_ERR(clk))
+                       return;
+
+               pr_debug("Registered clock '%s'\n", name);
+               clk_register_clkdev(clk, NULL, name);
+       }
+}
+CLK_OF_DECLARE(spc, "arm,spc", vexpress_clk_of_register_spc);
+#endif
index ea7168a680810dba82505afd827ff4909bf5130e..6e7980de37f4ee37dee1de2d0b16edbb007f683b 100644 (file)
@@ -120,6 +120,9 @@ void vexpress_sysreg_of_early_init(void);
 struct clk *vexpress_osc_setup(struct device *dev);
 void vexpress_osc_of_setup(struct device_node *node);
 
+struct clk *vexpress_clk_register_spc(const char *name, int cluster_id);
+void vexpress_clk_of_register_spc(void);
+
 void vexpress_clk_init(void __iomem *sp810_base);
 void vexpress_clk_of_init(void);