From 07ae43edc3b2e692356671f9444832fc291ae093 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Sat, 24 Nov 2012 00:51:07 +0530 Subject: clk: Vexpress-spc: Add clock driver 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 --- drivers/clk/versatile/Makefile | 2 +- drivers/clk/versatile/clk-vexpress-spc.c | 131 +++++++++++++++++++++++++++++++ include/linux/vexpress.h | 3 + 3 files changed, 135 insertions(+), 1 deletion(-) create mode 100644 drivers/clk/versatile/clk-vexpress-spc.c diff --git a/drivers/clk/versatile/Makefile b/drivers/clk/versatile/Makefile index c16ca787170..6e76bf87ca8 100644 --- a/drivers/clk/versatile/Makefile +++ b/drivers/clk/versatile/Makefile @@ -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 index 00000000000..b701ae41475 --- /dev/null +++ b/drivers/clk/versatile/clk-vexpress-spc.c @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2012 ARM Limited + * Copyright (C) 2012 Linaro + * + * Author: Viresh Kumar + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +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 diff --git a/include/linux/vexpress.h b/include/linux/vexpress.h index ea7168a6808..6e7980de37f 100644 --- a/include/linux/vexpress.h +++ b/include/linux/vexpress.h @@ -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); -- cgit v1.2.3