From ccb975c3c2afc932b30cb52e7924f9db286a962d 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 | 126 +++++++++++++++++++++++++++++++ include/linux/vexpress.h | 3 + 3 files changed, 130 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 ec3b88fe3e6..d1359f44043 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 -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..8620f54eded --- /dev/null +++ b/drivers/clk/versatile/clk-vexpress-spc.c @@ -0,0 +1,126 @@ +/* + * 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[9] = "cluster"; + struct device_node *node = NULL; + struct clk *clk; + const u32 *val; + int cluster_id = 0, len; + + 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[7] = 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, name, NULL); + } +} +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 75818744ab5..698ed67e17b 100644 --- a/include/linux/vexpress.h +++ b/include/linux/vexpress.h @@ -123,6 +123,9 @@ void vexpress_restart(char str, const char *cmd); 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 From 264f35a56f2dd96c0626f28fca3d6b08c25e686e Mon Sep 17 00:00:00 2001 From: Mark Hambleton Date: Thu, 3 Jan 2013 15:32:11 +0000 Subject: clk: vexpress-spc: Register clock only when spc is found clk-spc depends on spc and when spc is not present in DT (ex: on fast models), we must not register clk-spc. Signed-off-by: Mark Hambleton Signed-off-by: Viresh Kumar --- drivers/clk/versatile/clk-vexpress-spc.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/clk/versatile/clk-vexpress-spc.c b/drivers/clk/versatile/clk-vexpress-spc.c index 8620f54eded..d3f8fb44cca 100644 --- a/drivers/clk/versatile/clk-vexpress-spc.c +++ b/drivers/clk/versatile/clk-vexpress-spc.c @@ -108,6 +108,11 @@ void __init vexpress_clk_of_register_spc(void) 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) -- cgit v1.2.3 From e7e3e43dab481b2dcf8735715eea0e4fd2cd3704 Mon Sep 17 00:00:00 2001 From: Sudeep KarkadaNagesha Date: Fri, 13 Jul 2012 17:05:30 +0100 Subject: ARM: Select ARCH_HAS_CPUFREQ for ARCH_VEXPRESS This patch enables ARCH_HAS_CPUFREQ for Versatile Express platforms in order to support CPU frequency scaling. Signed-off-by: Sudeep KarkadaNagesha --- arch/arm/mach-vexpress/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/mach-vexpress/Kconfig b/arch/arm/mach-vexpress/Kconfig index 52d315b792c..4edf23f333f 100644 --- a/arch/arm/mach-vexpress/Kconfig +++ b/arch/arm/mach-vexpress/Kconfig @@ -1,5 +1,6 @@ config ARCH_VEXPRESS bool "ARM Ltd. Versatile Express family" if ARCH_MULTI_V7 + select ARCH_HAS_CPUFREQ select ARCH_REQUIRE_GPIOLIB select ARM_AMBA select ARM_GIC -- cgit v1.2.3 From e341ddcc08930576b22eb33525a4c1ef1e9ced36 Mon Sep 17 00:00:00 2001 From: Sudeep KarkadaNagesha Date: Fri, 13 Jul 2012 17:30:25 +0100 Subject: cpufreq: vexpress: add support for big.LITTLE frequency scaling This patch adds support for CPU frequency scaling on ARM vexpress based big.LITTLE platforms. The driver reads the frequencies from the device tree. This driver depends on Serial Power Controller(SPC) for setting operating performance points(OPPs). Ensure that SPC driver is built to avoid run-time errors. To support big.LITTLE topology, the frequencies are read from FDT and registered seperately per-cluster. To achieve this the CPU topology is used to set the affected/related CPUs in terms of their OPP dependencies. Signed-off-by: Sudeep KarkadaNagesha --- drivers/cpufreq/Kconfig.arm | 9 ++ drivers/cpufreq/Makefile | 1 + drivers/cpufreq/vexpress_bL_cpufreq.c | 276 ++++++++++++++++++++++++++++++++++ 3 files changed, 286 insertions(+) create mode 100644 drivers/cpufreq/vexpress_bL_cpufreq.c diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm index 030ddf6dd3f..4476f3a7901 100644 --- a/drivers/cpufreq/Kconfig.arm +++ b/drivers/cpufreq/Kconfig.arm @@ -113,3 +113,12 @@ config ARM_HIGHBANK_CPUFREQ based boards. If in doubt, say N. + +config ARM_VEXPRESS_BL_CPUFREQ + tristate "CPUfreq driver for ARM Vexpress big.LITTLE CPUs" + depends on ARCH_VEXPRESS && CPU_FREQ + help + This enables the CPUfreq driver for ARM Vexpress big.LITTLE + platform. + + If in doubt, say N. diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile index 863fd1865d4..039024c99e4 100644 --- a/drivers/cpufreq/Makefile +++ b/drivers/cpufreq/Makefile @@ -57,6 +57,7 @@ obj-$(CONFIG_ARM_OMAP2PLUS_CPUFREQ) += omap-cpufreq.o obj-$(CONFIG_ARM_SPEAR_CPUFREQ) += spear-cpufreq.o obj-$(CONFIG_ARM_HIGHBANK_CPUFREQ) += highbank-cpufreq.o obj-$(CONFIG_ARM_IMX6Q_CPUFREQ) += imx6q-cpufreq.o +obj-$(CONFIG_ARM_VEXPRESS_BL_CPUFREQ) += vexpress_bL_cpufreq.o ################################################################################## # PowerPC platform drivers diff --git a/drivers/cpufreq/vexpress_bL_cpufreq.c b/drivers/cpufreq/vexpress_bL_cpufreq.c new file mode 100644 index 00000000000..88ac95ef85c --- /dev/null +++ b/drivers/cpufreq/vexpress_bL_cpufreq.c @@ -0,0 +1,276 @@ +/* + * Vexpress big.LITTLE CPUFreq support + * Based on mach-integrator + * + * Copyright (C) 2012 ARM Ltd. + * Author: Sudeep KarkadaNagesha + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define VEXPRESS_MAX_CLUSTER 2 + +static struct cpufreq_frequency_table *freq_table[VEXPRESS_MAX_CLUSTER]; +static atomic_t freq_table_users = ATOMIC_INIT(0); + +/* Cached current cluster for each CPU to save on IPIs */ +static DEFINE_PER_CPU(unsigned int, cpu_cur_cluster); + +/* + * Functions to get the current status. + * + * Beware that the cluster for another CPU may change unexpectedly. + */ + +static unsigned int get_local_cluster(void) +{ + unsigned int mpidr; + asm ("mrc\tp15, 0, %0, c0, c0, 5" : "=r" (mpidr)); + return (mpidr >> 8) & 0xf; +} + +static void __get_current_cluster(void *_data) +{ + unsigned int *_cluster = _data; + *_cluster = get_local_cluster(); +} + +static int get_current_cluster(unsigned int cpu) +{ + unsigned int cluster = 0; + smp_call_function_single(cpu, __get_current_cluster, &cluster, 1); + return cluster; +} + +static int get_current_cached_cluster(unsigned int cpu) +{ + return per_cpu(cpu_cur_cluster, cpu); +} + +/* Validate policy frequency range */ +static int vexpress_cpufreq_verify_policy(struct cpufreq_policy *policy) +{ + uint32_t cur_cluster = get_current_cached_cluster(policy->cpu); + + /* This call takes care of it all using freq_table */ + return cpufreq_frequency_table_verify(policy, freq_table[cur_cluster]); +} + +/* Set clock frequency */ +static int vexpress_cpufreq_set_target(struct cpufreq_policy *policy, + unsigned int target_freq, unsigned int relation) +{ + uint32_t cpu = policy->cpu; + struct cpufreq_freqs freqs; + uint32_t freq_tab_idx; + uint32_t cur_cluster; + int ret = 0; + + /* Read current clock rate */ + cur_cluster = get_current_cached_cluster(cpu); + + if (vexpress_spc_get_performance(cur_cluster, &freq_tab_idx)) + return -EIO; + + freqs.old = freq_table[cur_cluster][freq_tab_idx].frequency; + + /* Make sure that target_freq is within supported range */ + if (target_freq > policy->max) + target_freq = policy->max; + if (target_freq < policy->min) + target_freq = policy->min; + + /* Determine valid target frequency using freq_table */ + cpufreq_frequency_table_target(policy, freq_table[cur_cluster], + target_freq, relation, &freq_tab_idx); + freqs.new = freq_table[cur_cluster][freq_tab_idx].frequency; + + freqs.cpu = policy->cpu; + + if (freqs.old == freqs.new) + return 0; + + pr_debug("Requested Freq %d cpu %d\n", freqs.new, cpu); + + for_each_cpu(freqs.cpu, policy->cpus) + cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); + + ret = vexpress_spc_set_performance(cur_cluster, freq_tab_idx); + if (ret) { + pr_err("Error %d while setting required OPP\n", ret); + return ret; + } + + policy->cur = freqs.new; + + for_each_cpu(freqs.cpu, policy->cpus) + cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); + + return ret; +} + +/* Get current clock frequency */ +static unsigned int vexpress_cpufreq_get(unsigned int cpu) +{ + uint32_t freq_tab_idx = 0; + uint32_t cur_cluster = get_current_cached_cluster(cpu); + + /* + * Read current clock rate with vexpress_spc call + */ + if (vexpress_spc_get_performance(cur_cluster, &freq_tab_idx)) + return -EIO; + + return freq_table[cur_cluster][freq_tab_idx].frequency; +} + +/* translate the integer array into cpufreq_frequency_table entries */ +static inline void _cpufreq_copy_table_from_array(uint32_t *table, + struct cpufreq_frequency_table *freq_table, int size) +{ + int i; + for (i = 0; i < size; i++) { + freq_table[i].index = i; + freq_table[i].frequency = table[i] / 1000; /* in kHZ */ + } + freq_table[i].index = size; + freq_table[i].frequency = CPUFREQ_TABLE_END; +} + +static int vexpress_cpufreq_of_init(void) +{ + uint32_t cpu_opp_num; + struct cpufreq_frequency_table *freqtable[VEXPRESS_MAX_CLUSTER]; + uint32_t *cpu_freqs; + int ret = 0, cluster_id = 0, len; + struct device_node *cluster = NULL; + const struct property *pp; + const u32 *hwid; + + while ((cluster = of_find_node_by_name(cluster, "cluster"))) { + hwid = of_get_property(cluster, "reg", &len); + if (hwid && len == 4) + cluster_id = be32_to_cpup(hwid); + + pp = of_find_property(cluster, "freqs", NULL); + if (!pp) + return -EINVAL; + cpu_opp_num = pp->length / sizeof(u32); + if (!cpu_opp_num) + return -ENODATA; + + cpu_freqs = kzalloc(sizeof(uint32_t) * cpu_opp_num, GFP_KERNEL); + freqtable[cluster_id] = + kzalloc(sizeof(struct cpufreq_frequency_table) * + (cpu_opp_num + 1), GFP_KERNEL); + if (!cpu_freqs || !freqtable[cluster_id]) { + ret = -ENOMEM; + goto free_mem; + } + of_property_read_u32_array(cluster, "freqs", + cpu_freqs, cpu_opp_num); + _cpufreq_copy_table_from_array(cpu_freqs, + freqtable[cluster_id], cpu_opp_num); + freq_table[cluster_id] = freqtable[cluster_id]; + + kfree(cpu_freqs); + } + return ret; +free_mem: + while (cluster_id >= 0) + kfree(freqtable[cluster_id--]); + kfree(cpu_freqs); + return ret; +} + +/* Per-CPU initialization */ +static int vexpress_cpufreq_init(struct cpufreq_policy *policy) +{ + int result = 0; + uint32_t cur_cluster = get_current_cluster(policy->cpu); + + if (atomic_inc_return(&freq_table_users) == 1) + result = vexpress_cpufreq_of_init(); + + if (result) { + atomic_dec_return(&freq_table_users); + pr_err("CPUFreq - CPU %d failed to initialize\n", policy->cpu); + return result; + } + + result = + cpufreq_frequency_table_cpuinfo(policy, freq_table[cur_cluster]); + if (result) + return result; + + cpufreq_frequency_table_get_attr(freq_table[cur_cluster], policy->cpu); + + per_cpu(cpu_cur_cluster, policy->cpu) = cur_cluster; + + /* set default policy and cpuinfo */ + policy->min = policy->cpuinfo.min_freq; + policy->max = policy->cpuinfo.max_freq; + + policy->cpuinfo.transition_latency = 1000000; /* 1 ms assumed */ + policy->cur = vexpress_cpufreq_get(policy->cpu); + + cpumask_copy(policy->cpus, topology_core_cpumask(policy->cpu)); + cpumask_copy(policy->related_cpus, policy->cpus); + + pr_info("CPUFreq for CPU %d initialized\n", policy->cpu); + return result; +} + +/* Export freq_table to sysfs */ +static struct freq_attr *vexpress_cpufreq_attr[] = { + &cpufreq_freq_attr_scaling_available_freqs, + NULL, +}; + +static struct cpufreq_driver vexpress_cpufreq_driver = { + .flags = CPUFREQ_STICKY, + .verify = vexpress_cpufreq_verify_policy, + .target = vexpress_cpufreq_set_target, + .get = vexpress_cpufreq_get, + .init = vexpress_cpufreq_init, + .name = "cpufreq_vexpress", + .attr = vexpress_cpufreq_attr, +}; + +static int __init vexpress_cpufreq_modinit(void) +{ + return cpufreq_register_driver(&vexpress_cpufreq_driver); +} + +static void __exit vexpress_cpufreq_modexit(void) +{ + cpufreq_unregister_driver(&vexpress_cpufreq_driver); +} + +MODULE_DESCRIPTION("cpufreq driver for ARM vexpress big.LITTLE platform"); +MODULE_LICENSE("GPL"); + +module_init(vexpress_cpufreq_modinit); +module_exit(vexpress_cpufreq_modexit); -- cgit v1.2.3 From ea8df6bcc2219a4854d38e33bfb085f532b9c88d Mon Sep 17 00:00:00 2001 From: Jon Medhurst Date: Fri, 20 Jul 2012 11:53:56 +0100 Subject: cpufreq: vexpress: Safely handle missing CPU frequencies If there are no CPU frequencies in the device tree for the current cluster, then vexpress_cpufreq_of_init() passes a null pointer to cpufreq_frequency_table_cpuinfo(), which it then dereferences. This patch tests for this situation and cleanly returns and error instead. Signed-off-by: Jon Medhurst --- drivers/cpufreq/vexpress_bL_cpufreq.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/cpufreq/vexpress_bL_cpufreq.c b/drivers/cpufreq/vexpress_bL_cpufreq.c index 88ac95ef85c..1fffaef366a 100644 --- a/drivers/cpufreq/vexpress_bL_cpufreq.c +++ b/drivers/cpufreq/vexpress_bL_cpufreq.c @@ -214,6 +214,9 @@ static int vexpress_cpufreq_init(struct cpufreq_policy *policy) if (atomic_inc_return(&freq_table_users) == 1) result = vexpress_cpufreq_of_init(); + if (freq_table[cur_cluster] == NULL) + result = -ENODATA; + if (result) { atomic_dec_return(&freq_table_users); pr_err("CPUFreq - CPU %d failed to initialize\n", policy->cpu); -- cgit v1.2.3 From 6192413a24d15cf475f34da8bab9f476a461de9b Mon Sep 17 00:00:00 2001 From: Jon Medhurst Date: Wed, 25 Jul 2012 15:08:04 +0100 Subject: cpufreq: vexpress: Check for presence of SPC driver The cpufreq driver requires SPC hardware, so check for its presence before initialising the driver. This enables the cpufreq driver to safely exist in kernels run on hardware without SPC support. Signed-off-by: Jon Medhurst --- drivers/cpufreq/vexpress_bL_cpufreq.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/cpufreq/vexpress_bL_cpufreq.c b/drivers/cpufreq/vexpress_bL_cpufreq.c index 1fffaef366a..2c71b2480d7 100644 --- a/drivers/cpufreq/vexpress_bL_cpufreq.c +++ b/drivers/cpufreq/vexpress_bL_cpufreq.c @@ -264,6 +264,11 @@ static struct cpufreq_driver vexpress_cpufreq_driver = { static int __init vexpress_cpufreq_modinit(void) { + if (!vexpress_spc_check_loaded()) { + pr_info("vexpress cpufreq not initialised because no SPC found\n"); + return -ENODEV; + } + return cpufreq_register_driver(&vexpress_cpufreq_driver); } -- cgit v1.2.3 From 3615b1187865c8c2bbd764e34bcb383318b4adf0 Mon Sep 17 00:00:00 2001 From: Jon Medhurst Date: Thu, 11 Oct 2012 14:47:42 +0100 Subject: cpufreq: vexpress: Rename driver to fit within size limits The maximum size of a cpu_freq driver is CPUFREQ_NAME_LEN (16) and the previous name was longer than this, leading to incorrect output with: $ cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_driver Reported-by: Viresh Kumar Signed-off-by: Jon Medhurst --- drivers/cpufreq/vexpress_bL_cpufreq.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/cpufreq/vexpress_bL_cpufreq.c b/drivers/cpufreq/vexpress_bL_cpufreq.c index 2c71b2480d7..228542a21d5 100644 --- a/drivers/cpufreq/vexpress_bL_cpufreq.c +++ b/drivers/cpufreq/vexpress_bL_cpufreq.c @@ -258,7 +258,7 @@ static struct cpufreq_driver vexpress_cpufreq_driver = { .target = vexpress_cpufreq_set_target, .get = vexpress_cpufreq_get, .init = vexpress_cpufreq_init, - .name = "cpufreq_vexpress", + .name = "vexpress-spc", .attr = vexpress_cpufreq_attr, }; -- cgit v1.2.3 From bbdd94fe02e9bc54b5c8d873e7dfc0ef3c592aaa Mon Sep 17 00:00:00 2001 From: Sudeep KarkadaNagesha Date: Fri, 30 Nov 2012 18:45:47 +0000 Subject: cpufreq: vexpress: update parameters to spc get/set perf routines As the SPC driver now take frequency as the input parameter for get/set performance routines, change the cpufreq driver to reflect the same. With this change Linux no longer rely on DT for freq -> perf index mapping. Signed-off-by: Sudeep KarkadaNagesha --- drivers/cpufreq/vexpress_bL_cpufreq.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/drivers/cpufreq/vexpress_bL_cpufreq.c b/drivers/cpufreq/vexpress_bL_cpufreq.c index 228542a21d5..5718bbecd98 100644 --- a/drivers/cpufreq/vexpress_bL_cpufreq.c +++ b/drivers/cpufreq/vexpress_bL_cpufreq.c @@ -91,11 +91,9 @@ static int vexpress_cpufreq_set_target(struct cpufreq_policy *policy, /* Read current clock rate */ cur_cluster = get_current_cached_cluster(cpu); - if (vexpress_spc_get_performance(cur_cluster, &freq_tab_idx)) + if (vexpress_spc_get_performance(cur_cluster, &freqs.old)) return -EIO; - freqs.old = freq_table[cur_cluster][freq_tab_idx].frequency; - /* Make sure that target_freq is within supported range */ if (target_freq > policy->max) target_freq = policy->max; @@ -117,7 +115,7 @@ static int vexpress_cpufreq_set_target(struct cpufreq_policy *policy, for_each_cpu(freqs.cpu, policy->cpus) cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); - ret = vexpress_spc_set_performance(cur_cluster, freq_tab_idx); + ret = vexpress_spc_set_performance(cur_cluster, freqs.new); if (ret) { pr_err("Error %d while setting required OPP\n", ret); return ret; @@ -134,16 +132,16 @@ static int vexpress_cpufreq_set_target(struct cpufreq_policy *policy, /* Get current clock frequency */ static unsigned int vexpress_cpufreq_get(unsigned int cpu) { - uint32_t freq_tab_idx = 0; + uint32_t freq = 0; uint32_t cur_cluster = get_current_cached_cluster(cpu); /* * Read current clock rate with vexpress_spc call */ - if (vexpress_spc_get_performance(cur_cluster, &freq_tab_idx)) + if (vexpress_spc_get_performance(cur_cluster, &freq)) return -EIO; - return freq_table[cur_cluster][freq_tab_idx].frequency; + return freq; } /* translate the integer array into cpufreq_frequency_table entries */ -- cgit v1.2.3 From 66756546bd6f86dfe11a1d69559c29b201e8bdf8 Mon Sep 17 00:00:00 2001 From: Dave Martin Date: Fri, 23 Nov 2012 12:38:44 +0000 Subject: cpufreq: vexpress: Use CPU topology for mapping cpus to clusters Signed-off-by: Dave Martin --- drivers/cpufreq/Kconfig.arm | 1 + drivers/cpufreq/vexpress_bL_cpufreq.c | 42 +++++++++-------------------------- 2 files changed, 11 insertions(+), 32 deletions(-) diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm index 4476f3a7901..2d4983aa23b 100644 --- a/drivers/cpufreq/Kconfig.arm +++ b/drivers/cpufreq/Kconfig.arm @@ -117,6 +117,7 @@ config ARM_HIGHBANK_CPUFREQ config ARM_VEXPRESS_BL_CPUFREQ tristate "CPUfreq driver for ARM Vexpress big.LITTLE CPUs" depends on ARCH_VEXPRESS && CPU_FREQ + depends on ARM_CPU_TOPOLOGY help This enables the CPUfreq driver for ARM Vexpress big.LITTLE platform. diff --git a/drivers/cpufreq/vexpress_bL_cpufreq.c b/drivers/cpufreq/vexpress_bL_cpufreq.c index 5718bbecd98..39825354b47 100644 --- a/drivers/cpufreq/vexpress_bL_cpufreq.c +++ b/drivers/cpufreq/vexpress_bL_cpufreq.c @@ -30,49 +30,27 @@ #include +#include + #define VEXPRESS_MAX_CLUSTER 2 static struct cpufreq_frequency_table *freq_table[VEXPRESS_MAX_CLUSTER]; static atomic_t freq_table_users = ATOMIC_INIT(0); -/* Cached current cluster for each CPU to save on IPIs */ -static DEFINE_PER_CPU(unsigned int, cpu_cur_cluster); - /* * Functions to get the current status. * * Beware that the cluster for another CPU may change unexpectedly. */ - -static unsigned int get_local_cluster(void) -{ - unsigned int mpidr; - asm ("mrc\tp15, 0, %0, c0, c0, 5" : "=r" (mpidr)); - return (mpidr >> 8) & 0xf; -} - -static void __get_current_cluster(void *_data) -{ - unsigned int *_cluster = _data; - *_cluster = get_local_cluster(); -} - -static int get_current_cluster(unsigned int cpu) +static int cpu_to_cluster(int cpu) { - unsigned int cluster = 0; - smp_call_function_single(cpu, __get_current_cluster, &cluster, 1); - return cluster; -} - -static int get_current_cached_cluster(unsigned int cpu) -{ - return per_cpu(cpu_cur_cluster, cpu); + return topology_physical_package_id(cpu); } /* Validate policy frequency range */ static int vexpress_cpufreq_verify_policy(struct cpufreq_policy *policy) { - uint32_t cur_cluster = get_current_cached_cluster(policy->cpu); + uint32_t cur_cluster = cpu_to_cluster(policy->cpu); /* This call takes care of it all using freq_table */ return cpufreq_frequency_table_verify(policy, freq_table[cur_cluster]); @@ -89,7 +67,9 @@ static int vexpress_cpufreq_set_target(struct cpufreq_policy *policy, int ret = 0; /* Read current clock rate */ - cur_cluster = get_current_cached_cluster(cpu); + + /* ASSUMPTION: The cpu can't be hotplugged in this function */ + cur_cluster = cpu_to_cluster(policy->cpu); if (vexpress_spc_get_performance(cur_cluster, &freqs.old)) return -EIO; @@ -133,7 +113,7 @@ static int vexpress_cpufreq_set_target(struct cpufreq_policy *policy, static unsigned int vexpress_cpufreq_get(unsigned int cpu) { uint32_t freq = 0; - uint32_t cur_cluster = get_current_cached_cluster(cpu); + uint32_t cur_cluster = cpu_to_cluster(cpu); /* * Read current clock rate with vexpress_spc call @@ -207,7 +187,7 @@ free_mem: static int vexpress_cpufreq_init(struct cpufreq_policy *policy) { int result = 0; - uint32_t cur_cluster = get_current_cluster(policy->cpu); + uint32_t cur_cluster = cpu_to_cluster(policy->cpu); if (atomic_inc_return(&freq_table_users) == 1) result = vexpress_cpufreq_of_init(); @@ -228,8 +208,6 @@ static int vexpress_cpufreq_init(struct cpufreq_policy *policy) cpufreq_frequency_table_get_attr(freq_table[cur_cluster], policy->cpu); - per_cpu(cpu_cur_cluster, policy->cpu) = cur_cluster; - /* set default policy and cpuinfo */ policy->min = policy->cpuinfo.min_freq; policy->max = policy->cpuinfo.max_freq; -- cgit v1.2.3 From fa55a999a51295d95ac1bf7d416da59c266d1d87 Mon Sep 17 00:00:00 2001 From: Dave Martin Date: Fri, 23 Nov 2012 12:41:34 +0000 Subject: cpufreq: vexpress: Remove duplicate frequency bounds check __cpufreq_driver_target() already validates the requested frequency against the min and max bounds specified in the policy, so there is to need to check it again. This patch removes the bounds check from vexpress_cpufreq_set_target(). Signed-off-by: Dave Martin --- drivers/cpufreq/vexpress_bL_cpufreq.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/cpufreq/vexpress_bL_cpufreq.c b/drivers/cpufreq/vexpress_bL_cpufreq.c index 39825354b47..253d59f9635 100644 --- a/drivers/cpufreq/vexpress_bL_cpufreq.c +++ b/drivers/cpufreq/vexpress_bL_cpufreq.c @@ -74,12 +74,6 @@ static int vexpress_cpufreq_set_target(struct cpufreq_policy *policy, if (vexpress_spc_get_performance(cur_cluster, &freqs.old)) return -EIO; - /* Make sure that target_freq is within supported range */ - if (target_freq > policy->max) - target_freq = policy->max; - if (target_freq < policy->min) - target_freq = policy->min; - /* Determine valid target frequency using freq_table */ cpufreq_frequency_table_target(policy, freq_table[cur_cluster], target_freq, relation, &freq_tab_idx); -- cgit v1.2.3 From cb8e2124fbd1c7f4b57455f777cf0230c53d05f7 Mon Sep 17 00:00:00 2001 From: Riku Voipio Date: Wed, 5 Dec 2012 11:08:29 +0200 Subject: fix building vexpress_bL_cpufreq as module cpu_topology symbol is needed if we build vexpress_bL_cpufreq as an module Signed-off-by: Riku Voipio --- arch/arm/kernel/topology.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/arm/kernel/topology.c b/arch/arm/kernel/topology.c index 79282ebcd93..606b95383d9 100644 --- a/arch/arm/kernel/topology.c +++ b/arch/arm/kernel/topology.c @@ -13,6 +13,7 @@ #include #include +#include #include #include #include @@ -200,6 +201,7 @@ static inline void update_cpu_power(unsigned int cpuid, unsigned int mpidr) {} * cpu topology table */ struct cputopo_arm cpu_topology[NR_CPUS]; +EXPORT_SYMBOL_GPL(cpu_topology); const struct cpumask *cpu_coregroup_mask(int cpu) { -- cgit v1.2.3 From 1f8d4aeb041757bf66d96b60922c753b42995a6a Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Sat, 1 Dec 2012 16:27:26 +0530 Subject: cpufreq: arm_big_little: Make Vexpress bL as generic big LITTLE cpufreq driver This driver shouldn't have any dependency on Vexpress Platform and so can be converted into generic big LITTLE platform's cpufreq driver. All dependencies which are currently there would be removed by other patches. Signed-off-by: Viresh Kumar --- drivers/cpufreq/Kconfig.arm | 9 +- drivers/cpufreq/Makefile | 2 +- drivers/cpufreq/arm_big_little.c | 245 ++++++++++++++++++++++++++++++++ drivers/cpufreq/vexpress_bL_cpufreq.c | 254 ---------------------------------- 4 files changed, 249 insertions(+), 261 deletions(-) create mode 100644 drivers/cpufreq/arm_big_little.c delete mode 100644 drivers/cpufreq/vexpress_bL_cpufreq.c diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm index 2d4983aa23b..62944f40dca 100644 --- a/drivers/cpufreq/Kconfig.arm +++ b/drivers/cpufreq/Kconfig.arm @@ -114,12 +114,9 @@ config ARM_HIGHBANK_CPUFREQ If in doubt, say N. -config ARM_VEXPRESS_BL_CPUFREQ - tristate "CPUfreq driver for ARM Vexpress big.LITTLE CPUs" - depends on ARCH_VEXPRESS && CPU_FREQ +config ARM_BIG_LITTLE_CPUFREQ + tristate "CPUfreq driver for ARM big LITTLE CPUs" depends on ARM_CPU_TOPOLOGY help - This enables the CPUfreq driver for ARM Vexpress big.LITTLE - platform. - + This enables the CPUfreq driver for ARM big.LITTLE platforms. If in doubt, say N. diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile index 039024c99e4..7071d4ec666 100644 --- a/drivers/cpufreq/Makefile +++ b/drivers/cpufreq/Makefile @@ -57,7 +57,7 @@ obj-$(CONFIG_ARM_OMAP2PLUS_CPUFREQ) += omap-cpufreq.o obj-$(CONFIG_ARM_SPEAR_CPUFREQ) += spear-cpufreq.o obj-$(CONFIG_ARM_HIGHBANK_CPUFREQ) += highbank-cpufreq.o obj-$(CONFIG_ARM_IMX6Q_CPUFREQ) += imx6q-cpufreq.o -obj-$(CONFIG_ARM_VEXPRESS_BL_CPUFREQ) += vexpress_bL_cpufreq.o +obj-$(CONFIG_ARM_BIG_LITTLE_CPUFREQ) += arm_big_little.o ################################################################################## # PowerPC platform drivers diff --git a/drivers/cpufreq/arm_big_little.c b/drivers/cpufreq/arm_big_little.c new file mode 100644 index 00000000000..a89f4021456 --- /dev/null +++ b/drivers/cpufreq/arm_big_little.c @@ -0,0 +1,245 @@ +/* + * ARM big.LITTLE Platforms CPUFreq support + * + * Copyright (C) 2012 ARM Ltd. + * Author: Sudeep KarkadaNagesha + * + * Copyright (C) 2012 Linaro. + * Viresh Kumar + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include + +#define MAX_CLUSTERS 2 + +static struct cpufreq_frequency_table *freq_table[MAX_CLUSTERS]; +static atomic_t freq_table_users = ATOMIC_INIT(0); + +/* + * Functions to get the current status. + * + * Beware that the cluster for another CPU may change unexpectedly. + */ +static int cpu_to_cluster(int cpu) +{ + return topology_physical_package_id(cpu); +} + +/* Validate policy frequency range */ +static int bl_cpufreq_verify_policy(struct cpufreq_policy *policy) +{ + u32 cur_cluster = cpu_to_cluster(policy->cpu); + + /* This call takes care of it all using freq_table */ + return cpufreq_frequency_table_verify(policy, freq_table[cur_cluster]); +} + +/* Set clock frequency */ +static int bl_cpufreq_set_target(struct cpufreq_policy *policy, + unsigned int target_freq, unsigned int relation) +{ + struct cpufreq_freqs freqs; + u32 cpu = policy->cpu, freq_tab_idx, cur_cluster; + int ret = 0; + + /* ASSUMPTION: The cpu can't be hotplugged in this function */ + cur_cluster = cpu_to_cluster(policy->cpu); + + if (vexpress_spc_get_performance(cur_cluster, &freqs.old)) + return -EIO; + + /* Determine valid target frequency using freq_table */ + cpufreq_frequency_table_target(policy, freq_table[cur_cluster], + target_freq, relation, &freq_tab_idx); + freqs.new = freq_table[cur_cluster][freq_tab_idx].frequency; + + freqs.cpu = policy->cpu; + + if (freqs.old == freqs.new) + return 0; + + pr_debug("Requested Freq %d cpu %d\n", freqs.new, cpu); + + for_each_cpu(freqs.cpu, policy->cpus) + cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); + + ret = vexpress_spc_set_performance(cur_cluster, freqs.new); + if (ret) { + pr_err("Error %d while setting required OPP\n", ret); + return ret; + } + + policy->cur = freqs.new; + + for_each_cpu(freqs.cpu, policy->cpus) + cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); + + return ret; +} + +/* Get current clock frequency */ +static unsigned int bl_cpufreq_get(unsigned int cpu) +{ + u32 freq = 0; + u32 cur_cluster = cpu_to_cluster(cpu); + + /* + * Read current clock rate with vexpress_spc call + */ + if (vexpress_spc_get_performance(cur_cluster, &freq)) + return -EIO; + + return freq; +} + +/* translate the integer array into cpufreq_frequency_table entries */ +static inline void _cpufreq_copy_table_from_array(u32 *table, + struct cpufreq_frequency_table *freq_table, int size) +{ + int i; + for (i = 0; i < size; i++) { + freq_table[i].index = i; + freq_table[i].frequency = table[i] / 1000; /* in kHZ */ + } + freq_table[i].index = size; + freq_table[i].frequency = CPUFREQ_TABLE_END; +} + +static int bl_cpufreq_of_init(void) +{ + u32 cpu_opp_num; + struct cpufreq_frequency_table *freqtable[MAX_CLUSTERS]; + u32 *cpu_freqs; + int ret = 0, cluster_id = 0, len; + struct device_node *cluster = NULL; + const struct property *pp; + const u32 *hwid; + + while ((cluster = of_find_node_by_name(cluster, "cluster"))) { + hwid = of_get_property(cluster, "reg", &len); + if (hwid && len == 4) + cluster_id = be32_to_cpup(hwid); + + pp = of_find_property(cluster, "freqs", NULL); + if (!pp) + return -EINVAL; + cpu_opp_num = pp->length / sizeof(u32); + if (!cpu_opp_num) + return -ENODATA; + + cpu_freqs = kzalloc(sizeof(u32) * cpu_opp_num, GFP_KERNEL); + freqtable[cluster_id] = + kzalloc(sizeof(struct cpufreq_frequency_table) * + (cpu_opp_num + 1), GFP_KERNEL); + if (!cpu_freqs || !freqtable[cluster_id]) { + ret = -ENOMEM; + goto free_mem; + } + of_property_read_u32_array(cluster, "freqs", + cpu_freqs, cpu_opp_num); + _cpufreq_copy_table_from_array(cpu_freqs, + freqtable[cluster_id], cpu_opp_num); + freq_table[cluster_id] = freqtable[cluster_id]; + + kfree(cpu_freqs); + } + return ret; +free_mem: + while (cluster_id >= 0) + kfree(freqtable[cluster_id--]); + kfree(cpu_freqs); + return ret; +} + +/* Per-CPU initialization */ +static int bl_cpufreq_init(struct cpufreq_policy *policy) +{ + int result = 0; + u32 cur_cluster = cpu_to_cluster(policy->cpu); + + if (atomic_inc_return(&freq_table_users) == 1) + result = bl_cpufreq_of_init(); + + if (freq_table[cur_cluster] == NULL) + result = -ENODATA; + + if (result) { + atomic_dec_return(&freq_table_users); + pr_err("CPUFreq - CPU %d failed to initialize\n", policy->cpu); + return result; + } + + result = cpufreq_frequency_table_cpuinfo(policy, + freq_table[cur_cluster]); + if (result) + return result; + + cpufreq_frequency_table_get_attr(freq_table[cur_cluster], policy->cpu); + + /* set default policy and cpuinfo */ + policy->min = policy->cpuinfo.min_freq; + policy->max = policy->cpuinfo.max_freq; + + policy->cpuinfo.transition_latency = 1000000; /* 1 ms assumed */ + policy->cur = bl_cpufreq_get(policy->cpu); + + cpumask_copy(policy->cpus, topology_core_cpumask(policy->cpu)); + cpumask_copy(policy->related_cpus, policy->cpus); + + pr_info("CPUFreq for CPU %d initialized\n", policy->cpu); + return result; +} + +/* Export freq_table to sysfs */ +static struct freq_attr *bl_cpufreq_attr[] = { + &cpufreq_freq_attr_scaling_available_freqs, + NULL, +}; + +static struct cpufreq_driver bl_cpufreq_driver = { + .name = "arm-big-little", + .flags = CPUFREQ_STICKY, + .verify = bl_cpufreq_verify_policy, + .target = bl_cpufreq_set_target, + .get = bl_cpufreq_get, + .init = bl_cpufreq_init, + .attr = bl_cpufreq_attr, +}; + +static int __init bl_cpufreq_modinit(void) +{ + if (!vexpress_spc_check_loaded()) { + pr_info("vexpress cpufreq not initialised because no SPC found\n"); + return -ENODEV; + } + + return cpufreq_register_driver(&bl_cpufreq_driver); +} +module_init(bl_cpufreq_modinit); + +static void __exit bl_cpufreq_modexit(void) +{ + cpufreq_unregister_driver(&bl_cpufreq_driver); +} +module_exit(bl_cpufreq_modexit); + +MODULE_DESCRIPTION("ARM big LITTLE platforms cpufreq driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/cpufreq/vexpress_bL_cpufreq.c b/drivers/cpufreq/vexpress_bL_cpufreq.c deleted file mode 100644 index 253d59f9635..00000000000 --- a/drivers/cpufreq/vexpress_bL_cpufreq.c +++ /dev/null @@ -1,254 +0,0 @@ -/* - * Vexpress big.LITTLE CPUFreq support - * Based on mach-integrator - * - * Copyright (C) 2012 ARM Ltd. - * Author: Sudeep KarkadaNagesha - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include - -#define VEXPRESS_MAX_CLUSTER 2 - -static struct cpufreq_frequency_table *freq_table[VEXPRESS_MAX_CLUSTER]; -static atomic_t freq_table_users = ATOMIC_INIT(0); - -/* - * Functions to get the current status. - * - * Beware that the cluster for another CPU may change unexpectedly. - */ -static int cpu_to_cluster(int cpu) -{ - return topology_physical_package_id(cpu); -} - -/* Validate policy frequency range */ -static int vexpress_cpufreq_verify_policy(struct cpufreq_policy *policy) -{ - uint32_t cur_cluster = cpu_to_cluster(policy->cpu); - - /* This call takes care of it all using freq_table */ - return cpufreq_frequency_table_verify(policy, freq_table[cur_cluster]); -} - -/* Set clock frequency */ -static int vexpress_cpufreq_set_target(struct cpufreq_policy *policy, - unsigned int target_freq, unsigned int relation) -{ - uint32_t cpu = policy->cpu; - struct cpufreq_freqs freqs; - uint32_t freq_tab_idx; - uint32_t cur_cluster; - int ret = 0; - - /* Read current clock rate */ - - /* ASSUMPTION: The cpu can't be hotplugged in this function */ - cur_cluster = cpu_to_cluster(policy->cpu); - - if (vexpress_spc_get_performance(cur_cluster, &freqs.old)) - return -EIO; - - /* Determine valid target frequency using freq_table */ - cpufreq_frequency_table_target(policy, freq_table[cur_cluster], - target_freq, relation, &freq_tab_idx); - freqs.new = freq_table[cur_cluster][freq_tab_idx].frequency; - - freqs.cpu = policy->cpu; - - if (freqs.old == freqs.new) - return 0; - - pr_debug("Requested Freq %d cpu %d\n", freqs.new, cpu); - - for_each_cpu(freqs.cpu, policy->cpus) - cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); - - ret = vexpress_spc_set_performance(cur_cluster, freqs.new); - if (ret) { - pr_err("Error %d while setting required OPP\n", ret); - return ret; - } - - policy->cur = freqs.new; - - for_each_cpu(freqs.cpu, policy->cpus) - cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); - - return ret; -} - -/* Get current clock frequency */ -static unsigned int vexpress_cpufreq_get(unsigned int cpu) -{ - uint32_t freq = 0; - uint32_t cur_cluster = cpu_to_cluster(cpu); - - /* - * Read current clock rate with vexpress_spc call - */ - if (vexpress_spc_get_performance(cur_cluster, &freq)) - return -EIO; - - return freq; -} - -/* translate the integer array into cpufreq_frequency_table entries */ -static inline void _cpufreq_copy_table_from_array(uint32_t *table, - struct cpufreq_frequency_table *freq_table, int size) -{ - int i; - for (i = 0; i < size; i++) { - freq_table[i].index = i; - freq_table[i].frequency = table[i] / 1000; /* in kHZ */ - } - freq_table[i].index = size; - freq_table[i].frequency = CPUFREQ_TABLE_END; -} - -static int vexpress_cpufreq_of_init(void) -{ - uint32_t cpu_opp_num; - struct cpufreq_frequency_table *freqtable[VEXPRESS_MAX_CLUSTER]; - uint32_t *cpu_freqs; - int ret = 0, cluster_id = 0, len; - struct device_node *cluster = NULL; - const struct property *pp; - const u32 *hwid; - - while ((cluster = of_find_node_by_name(cluster, "cluster"))) { - hwid = of_get_property(cluster, "reg", &len); - if (hwid && len == 4) - cluster_id = be32_to_cpup(hwid); - - pp = of_find_property(cluster, "freqs", NULL); - if (!pp) - return -EINVAL; - cpu_opp_num = pp->length / sizeof(u32); - if (!cpu_opp_num) - return -ENODATA; - - cpu_freqs = kzalloc(sizeof(uint32_t) * cpu_opp_num, GFP_KERNEL); - freqtable[cluster_id] = - kzalloc(sizeof(struct cpufreq_frequency_table) * - (cpu_opp_num + 1), GFP_KERNEL); - if (!cpu_freqs || !freqtable[cluster_id]) { - ret = -ENOMEM; - goto free_mem; - } - of_property_read_u32_array(cluster, "freqs", - cpu_freqs, cpu_opp_num); - _cpufreq_copy_table_from_array(cpu_freqs, - freqtable[cluster_id], cpu_opp_num); - freq_table[cluster_id] = freqtable[cluster_id]; - - kfree(cpu_freqs); - } - return ret; -free_mem: - while (cluster_id >= 0) - kfree(freqtable[cluster_id--]); - kfree(cpu_freqs); - return ret; -} - -/* Per-CPU initialization */ -static int vexpress_cpufreq_init(struct cpufreq_policy *policy) -{ - int result = 0; - uint32_t cur_cluster = cpu_to_cluster(policy->cpu); - - if (atomic_inc_return(&freq_table_users) == 1) - result = vexpress_cpufreq_of_init(); - - if (freq_table[cur_cluster] == NULL) - result = -ENODATA; - - if (result) { - atomic_dec_return(&freq_table_users); - pr_err("CPUFreq - CPU %d failed to initialize\n", policy->cpu); - return result; - } - - result = - cpufreq_frequency_table_cpuinfo(policy, freq_table[cur_cluster]); - if (result) - return result; - - cpufreq_frequency_table_get_attr(freq_table[cur_cluster], policy->cpu); - - /* set default policy and cpuinfo */ - policy->min = policy->cpuinfo.min_freq; - policy->max = policy->cpuinfo.max_freq; - - policy->cpuinfo.transition_latency = 1000000; /* 1 ms assumed */ - policy->cur = vexpress_cpufreq_get(policy->cpu); - - cpumask_copy(policy->cpus, topology_core_cpumask(policy->cpu)); - cpumask_copy(policy->related_cpus, policy->cpus); - - pr_info("CPUFreq for CPU %d initialized\n", policy->cpu); - return result; -} - -/* Export freq_table to sysfs */ -static struct freq_attr *vexpress_cpufreq_attr[] = { - &cpufreq_freq_attr_scaling_available_freqs, - NULL, -}; - -static struct cpufreq_driver vexpress_cpufreq_driver = { - .flags = CPUFREQ_STICKY, - .verify = vexpress_cpufreq_verify_policy, - .target = vexpress_cpufreq_set_target, - .get = vexpress_cpufreq_get, - .init = vexpress_cpufreq_init, - .name = "vexpress-spc", - .attr = vexpress_cpufreq_attr, -}; - -static int __init vexpress_cpufreq_modinit(void) -{ - if (!vexpress_spc_check_loaded()) { - pr_info("vexpress cpufreq not initialised because no SPC found\n"); - return -ENODEV; - } - - return cpufreq_register_driver(&vexpress_cpufreq_driver); -} - -static void __exit vexpress_cpufreq_modexit(void) -{ - cpufreq_unregister_driver(&vexpress_cpufreq_driver); -} - -MODULE_DESCRIPTION("cpufreq driver for ARM vexpress big.LITTLE platform"); -MODULE_LICENSE("GPL"); - -module_init(vexpress_cpufreq_modinit); -module_exit(vexpress_cpufreq_modexit); -- cgit v1.2.3 From 5969835edfb72d6a848899ec9b0126cdb3f9124c Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Sat, 1 Dec 2012 17:16:02 +0530 Subject: cpufreq: arm_big_little: Get freq table from platform specific driver Every platform have different way to get available frequencies from boot loaders. Vexpress platform gets this information from SPC controller. This patch separates out platform specific part for getting cpufreq table from arm big LITTLE cpufreq driver. Every platform can pass this information from their own stubs to this driver. Signed-off-by: Viresh Kumar --- drivers/cpufreq/Kconfig.arm | 9 +- drivers/cpufreq/Makefile | 1 + drivers/cpufreq/arm_big_little.c | 150 +++++++++++++++++++--------------- drivers/cpufreq/arm_big_little.h | 43 ++++++++++ drivers/cpufreq/vexpress_big_little.c | 63 ++++++++++++++ 5 files changed, 198 insertions(+), 68 deletions(-) create mode 100644 drivers/cpufreq/arm_big_little.h create mode 100644 drivers/cpufreq/vexpress_big_little.c diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm index 62944f40dca..55c53bd742e 100644 --- a/drivers/cpufreq/Kconfig.arm +++ b/drivers/cpufreq/Kconfig.arm @@ -115,8 +115,13 @@ config ARM_HIGHBANK_CPUFREQ If in doubt, say N. config ARM_BIG_LITTLE_CPUFREQ - tristate "CPUfreq driver for ARM big LITTLE CPUs" + tristate depends on ARM_CPU_TOPOLOGY + +config ARM_VEXPRESS_BL_CPUFREQ + tristate "ARM Vexpress big LITTLE CPUfreq driver" + select ARM_BIG_LITTLE_CPUFREQ + depends on ARM_SPC help - This enables the CPUfreq driver for ARM big.LITTLE platforms. + This enables the CPUfreq driver for ARM Vexpress big.LITTLE platform. If in doubt, say N. diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile index 7071d4ec666..f26fd28849e 100644 --- a/drivers/cpufreq/Makefile +++ b/drivers/cpufreq/Makefile @@ -58,6 +58,7 @@ obj-$(CONFIG_ARM_SPEAR_CPUFREQ) += spear-cpufreq.o obj-$(CONFIG_ARM_HIGHBANK_CPUFREQ) += highbank-cpufreq.o obj-$(CONFIG_ARM_IMX6Q_CPUFREQ) += imx6q-cpufreq.o obj-$(CONFIG_ARM_BIG_LITTLE_CPUFREQ) += arm_big_little.o +obj-$(CONFIG_ARM_VEXPRESS_BL_CPUFREQ) += vexpress_big_little.o ################################################################################## # PowerPC platform drivers diff --git a/drivers/cpufreq/arm_big_little.c b/drivers/cpufreq/arm_big_little.c index a89f4021456..35265affd3d 100644 --- a/drivers/cpufreq/arm_big_little.c +++ b/drivers/cpufreq/arm_big_little.c @@ -21,17 +21,20 @@ #include #include +#include #include #include #include #include #include #include +#include "arm_big_little.h" #define MAX_CLUSTERS 2 +static struct cpufreq_arm_bl_ops *arm_bl_ops; static struct cpufreq_frequency_table *freq_table[MAX_CLUSTERS]; -static atomic_t freq_table_users = ATOMIC_INIT(0); +static atomic_t cluster_usage[MAX_CLUSTERS] = {ATOMIC_INIT(0), ATOMIC_INIT(0)}; /* * Functions to get the current status. @@ -111,86 +114,54 @@ static unsigned int bl_cpufreq_get(unsigned int cpu) } /* translate the integer array into cpufreq_frequency_table entries */ -static inline void _cpufreq_copy_table_from_array(u32 *table, - struct cpufreq_frequency_table *freq_table, int size) +struct cpufreq_frequency_table * +arm_bl_copy_table_from_array(unsigned int *table, int count) { int i; - for (i = 0; i < size; i++) { + + struct cpufreq_frequency_table *freq_table; + + freq_table = kmalloc(sizeof(*freq_table) * (count + 1), GFP_KERNEL); + if (!freq_table) + return NULL; + + for (i = 0; i < count; i++) { freq_table[i].index = i; - freq_table[i].frequency = table[i] / 1000; /* in kHZ */ + freq_table[i].frequency = table[i]; /* in kHZ */ } - freq_table[i].index = size; + + freq_table[i].index = count; freq_table[i].frequency = CPUFREQ_TABLE_END; + + return freq_table; } -static int bl_cpufreq_of_init(void) +void arm_bl_free_freq_table(u32 cluster) { - u32 cpu_opp_num; - struct cpufreq_frequency_table *freqtable[MAX_CLUSTERS]; - u32 *cpu_freqs; - int ret = 0, cluster_id = 0, len; - struct device_node *cluster = NULL; - const struct property *pp; - const u32 *hwid; - - while ((cluster = of_find_node_by_name(cluster, "cluster"))) { - hwid = of_get_property(cluster, "reg", &len); - if (hwid && len == 4) - cluster_id = be32_to_cpup(hwid); - - pp = of_find_property(cluster, "freqs", NULL); - if (!pp) - return -EINVAL; - cpu_opp_num = pp->length / sizeof(u32); - if (!cpu_opp_num) - return -ENODATA; - - cpu_freqs = kzalloc(sizeof(u32) * cpu_opp_num, GFP_KERNEL); - freqtable[cluster_id] = - kzalloc(sizeof(struct cpufreq_frequency_table) * - (cpu_opp_num + 1), GFP_KERNEL); - if (!cpu_freqs || !freqtable[cluster_id]) { - ret = -ENOMEM; - goto free_mem; - } - of_property_read_u32_array(cluster, "freqs", - cpu_freqs, cpu_opp_num); - _cpufreq_copy_table_from_array(cpu_freqs, - freqtable[cluster_id], cpu_opp_num); - freq_table[cluster_id] = freqtable[cluster_id]; - - kfree(cpu_freqs); - } - return ret; -free_mem: - while (cluster_id >= 0) - kfree(freqtable[cluster_id--]); - kfree(cpu_freqs); - return ret; + kfree(freq_table[cluster]); } /* Per-CPU initialization */ static int bl_cpufreq_init(struct cpufreq_policy *policy) { - int result = 0; u32 cur_cluster = cpu_to_cluster(policy->cpu); - - if (atomic_inc_return(&freq_table_users) == 1) - result = bl_cpufreq_of_init(); - - if (freq_table[cur_cluster] == NULL) - result = -ENODATA; - - if (result) { - atomic_dec_return(&freq_table_users); - pr_err("CPUFreq - CPU %d failed to initialize\n", policy->cpu); - return result; + int result = -ENODATA; + int count; + + if (atomic_inc_return(&cluster_usage[cur_cluster]) == 1) { + freq_table[cur_cluster] = arm_bl_ops->get_freq_tbl(cur_cluster, + &count); + if (!freq_table[cur_cluster]) + goto atomic_dec; } result = cpufreq_frequency_table_cpuinfo(policy, freq_table[cur_cluster]); - if (result) - return result; + if (result) { + if (atomic_read(&cluster_usage[cur_cluster]) == 1) + goto put_table; + goto atomic_dec; + } cpufreq_frequency_table_get_attr(freq_table[cur_cluster], policy->cpu); @@ -204,10 +175,29 @@ static int bl_cpufreq_init(struct cpufreq_policy *policy) cpumask_copy(policy->cpus, topology_core_cpumask(policy->cpu)); cpumask_copy(policy->related_cpus, policy->cpus); - pr_info("CPUFreq for CPU %d initialized\n", policy->cpu); + pr_info("CPU %d initialized\n", policy->cpu); + return 0; + +put_table: + arm_bl_ops->put_freq_tbl(cur_cluster); +atomic_dec: + atomic_dec_return(&cluster_usage[cur_cluster]); + pr_err("CPU %d failed to initialize\n", policy->cpu); return result; } +static int bl_cpufreq_exit(struct cpufreq_policy *policy) +{ + u32 cur_cluster = cpu_to_cluster(policy->cpu); + + if (!atomic_dec_return(&cluster_usage[cur_cluster])) { + arm_bl_ops->put_freq_tbl(cur_cluster); + freq_table[cur_cluster] = NULL; + } + + return 0; +} + /* Export freq_table to sysfs */ static struct freq_attr *bl_cpufreq_attr[] = { &cpufreq_freq_attr_scaling_available_freqs, @@ -221,16 +211,43 @@ static struct cpufreq_driver bl_cpufreq_driver = { .target = bl_cpufreq_set_target, .get = bl_cpufreq_get, .init = bl_cpufreq_init, + .exit = bl_cpufreq_exit, .attr = bl_cpufreq_attr, }; +/* All platforms using this driver must have their entry here */ +static struct of_device_id bl_of_match[] = { + { + .compatible = "arm,vexpress,v2p-ca15,tc2", + .data = vexpress_bl_get_ops + }, + {}, +}; + static int __init bl_cpufreq_modinit(void) { - if (!vexpress_spc_check_loaded()) { - pr_info("vexpress cpufreq not initialised because no SPC found\n"); + int i, size = ARRAY_SIZE(bl_of_match); + struct cpufreq_arm_bl_ops *(*bl_get_ops)(void) = NULL; + + for (i = 0; i < size; i++) { + if (of_machine_is_compatible(bl_of_match[i].compatible)) { + bl_get_ops = bl_of_match[i].data; + break; + } + } + + if (!bl_get_ops) { + pr_err("No platform matched, exiting\n"); return -ENODEV; } + arm_bl_ops = bl_get_ops(); + if (!arm_bl_ops || !arm_bl_ops->get_freq_tbl) { + pr_err("Invalid ops returned by platform: %s\n", + bl_of_match[i].compatible); + return -EINVAL; + } + return cpufreq_register_driver(&bl_cpufreq_driver); } module_init(bl_cpufreq_modinit); @@ -238,6 +255,7 @@ module_init(bl_cpufreq_modinit); static void __exit bl_cpufreq_modexit(void) { cpufreq_unregister_driver(&bl_cpufreq_driver); + arm_bl_ops = NULL; } module_exit(bl_cpufreq_modexit); diff --git a/drivers/cpufreq/arm_big_little.h b/drivers/cpufreq/arm_big_little.h new file mode 100644 index 00000000000..050aef9c076 --- /dev/null +++ b/drivers/cpufreq/arm_big_little.h @@ -0,0 +1,43 @@ +/* + * ARM big.LITTLE platform's CPUFreq header file + * + * Copyright (C) 2012 ARM Ltd. + * Author: Sudeep KarkadaNagesha + * + * Copyright (C) 2012 ARM Ltd. + * Viresh Kumar + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#ifndef CPUFREQ_ARM_BIG_LITTLE_H +#define CPUFREQ_ARM_BIG_LITTLE_H + +#include +#include + +struct cpufreq_arm_bl_ops { + struct cpufreq_frequency_table *(*get_freq_tbl)(u32 cluster, int *count); + void (*put_freq_tbl)(u32 cluster); +}; + +struct cpufreq_frequency_table * +arm_bl_copy_table_from_array(unsigned int *table, int count); +void arm_bl_free_freq_table(u32 cluster); + +#if defined(CONFIG_ARM_VEXPRESS_BL_CPUFREQ) || defined(CONFIG_ARM_VEXPRESS_BL_CPUFREQ_MODULE) +struct cpufreq_arm_bl_ops *vexpress_bl_get_ops(void); +#else +static struct cpufreq_arm_bl_ops *vexpress_bl_get_ops(void) +{ + return NULL; +} +#endif + +#endif /* CPUFREQ_ARM_BIG_LITTLE_H */ diff --git a/drivers/cpufreq/vexpress_big_little.c b/drivers/cpufreq/vexpress_big_little.c new file mode 100644 index 00000000000..1b44ae2ad36 --- /dev/null +++ b/drivers/cpufreq/vexpress_big_little.c @@ -0,0 +1,63 @@ +/* + * Vexpress big.LITTLE CPUFreq Interface driver + * + * It provides necessary ops to arm_big_little cpufreq driver and gets + * information from spc controller. + * + * Copyright (C) 2012 ARM Ltd. + * Author: Sudeep KarkadaNagesha + * + * Copyright (C) 2012 Linaro. + * Viresh Kumar + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include "arm_big_little.h" + +static struct cpufreq_frequency_table *vexpress_get_freq_tbl(u32 cluster, + int *count) +{ + unsigned int *table = vexpress_spc_get_freq_table(cluster, count); + + if (!table || !*count) { + pr_err("SPC controller returned invalid freq table"); + return NULL; + } + + return arm_bl_copy_table_from_array(table, *count); +} + +static void vexpress_put_freq_tbl(u32 cluster) +{ + arm_bl_free_freq_table(cluster); +} + +static struct cpufreq_arm_bl_ops vexpress_bl_ops = { + .get_freq_tbl = vexpress_get_freq_tbl, + .put_freq_tbl = vexpress_put_freq_tbl, +}; + +struct cpufreq_arm_bl_ops *vexpress_bl_get_ops(void) +{ + if (!vexpress_spc_check_loaded()) { + pr_info("No SPC found\n"); + return NULL; + } + + return &vexpress_bl_ops; +} +EXPORT_SYMBOL_GPL(vexpress_bl_get_ops); -- cgit v1.2.3 From 9fd812267aa6f7e9f1e5501d1cbe47c79d843b04 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Sat, 1 Dec 2012 19:40:14 +0530 Subject: cpufreq: arm_big_little: Use clock framework calls to get/set clk We already have clock framework support for vexpress spc controller. So, no longer need to call spc specific routines from arm_big_little driver. Finally this driver is generic now, with zero dependency on vexpress. Signed-off-by: Viresh Kumar --- drivers/cpufreq/arm_big_little.c | 108 +++++++++++++++++++++++---------------- 1 file changed, 64 insertions(+), 44 deletions(-) diff --git a/drivers/cpufreq/arm_big_little.c b/drivers/cpufreq/arm_big_little.c index 35265affd3d..49f87d1ee24 100644 --- a/drivers/cpufreq/arm_big_little.c +++ b/drivers/cpufreq/arm_big_little.c @@ -19,6 +19,7 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include #include #include #include @@ -26,13 +27,13 @@ #include #include #include -#include #include #include "arm_big_little.h" #define MAX_CLUSTERS 2 static struct cpufreq_arm_bl_ops *arm_bl_ops; +static struct clk *clk[MAX_CLUSTERS]; static struct cpufreq_frequency_table *freq_table[MAX_CLUSTERS]; static atomic_t cluster_usage[MAX_CLUSTERS] = {ATOMIC_INIT(0), ATOMIC_INIT(0)}; @@ -46,6 +47,13 @@ static int cpu_to_cluster(int cpu) return topology_physical_package_id(cpu); } +static unsigned int bl_cpufreq_get(unsigned int cpu) +{ + u32 cur_cluster = cpu_to_cluster(cpu); + + return clk_get_rate(clk[cur_cluster]) / 1000; +} + /* Validate policy frequency range */ static int bl_cpufreq_verify_policy(struct cpufreq_policy *policy) { @@ -66,8 +74,7 @@ static int bl_cpufreq_set_target(struct cpufreq_policy *policy, /* ASSUMPTION: The cpu can't be hotplugged in this function */ cur_cluster = cpu_to_cluster(policy->cpu); - if (vexpress_spc_get_performance(cur_cluster, &freqs.old)) - return -EIO; + freqs.old = bl_cpufreq_get(policy->cpu); /* Determine valid target frequency using freq_table */ cpufreq_frequency_table_target(policy, freq_table[cur_cluster], @@ -84,9 +91,9 @@ static int bl_cpufreq_set_target(struct cpufreq_policy *policy, for_each_cpu(freqs.cpu, policy->cpus) cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); - ret = vexpress_spc_set_performance(cur_cluster, freqs.new); + ret = clk_set_rate(clk[cur_cluster], freqs.new * 1000); if (ret) { - pr_err("Error %d while setting required OPP\n", ret); + pr_err("clk_set_rate failed: %d\n", ret); return ret; } @@ -98,21 +105,6 @@ static int bl_cpufreq_set_target(struct cpufreq_policy *policy, return ret; } -/* Get current clock frequency */ -static unsigned int bl_cpufreq_get(unsigned int cpu) -{ - u32 freq = 0; - u32 cur_cluster = cpu_to_cluster(cpu); - - /* - * Read current clock rate with vexpress_spc call - */ - if (vexpress_spc_get_performance(cur_cluster, &freq)) - return -EIO; - - return freq; -} - /* translate the integer array into cpufreq_frequency_table entries */ struct cpufreq_frequency_table * arm_bl_copy_table_from_array(unsigned int *table, int count) @@ -135,32 +127,71 @@ arm_bl_copy_table_from_array(unsigned int *table, int count) return freq_table; } +EXPORT_SYMBOL_GPL(arm_bl_copy_table_from_array); void arm_bl_free_freq_table(u32 cluster) { kfree(freq_table[cluster]); } +EXPORT_SYMBOL_GPL(arm_bl_free_freq_table); + +static void put_cluster_clk_and_freq_table(u32 cluster) +{ + if (!atomic_dec_return(&cluster_usage[cluster])) { + clk_put(clk[cluster]); + clk[cluster] = NULL; + arm_bl_ops->put_freq_tbl(cluster); + freq_table[cluster] = NULL; + pr_debug("%s: cluster: %d\n", __func__, cluster); + } +} + +static int get_cluster_clk_and_freq_table(u32 cluster) +{ + char name[9] = "cluster"; + int count; + + if (atomic_inc_return(&cluster_usage[cluster]) != 1) + return 0; + + freq_table[cluster] = arm_bl_ops->get_freq_tbl(cluster, &count); + if (!freq_table[cluster]) + goto atomic_dec; + + name[7] = cluster + '0'; + clk[cluster] = clk_get(NULL, name); + if (!IS_ERR_OR_NULL(clk[cluster])) { + pr_debug("%s: clk: %p & freq table: %p, cluster: %d\n", + __func__, clk[cluster], freq_table[cluster], + cluster); + return 0; + } + + arm_bl_ops->put_freq_tbl(cluster); + +atomic_dec: + atomic_dec(&cluster_usage[cluster]); + pr_err("%s: Failed to get data for cluster: %d\n", __func__, cluster); + return -ENODATA; +} /* Per-CPU initialization */ static int bl_cpufreq_init(struct cpufreq_policy *policy) { u32 cur_cluster = cpu_to_cluster(policy->cpu); - int result = -ENODATA; - int count; + int result; - if (atomic_inc_return(&cluster_usage[cur_cluster]) == 1) { - freq_table[cur_cluster] = arm_bl_ops->get_freq_tbl(cur_cluster, - &count); - if (!freq_table[cur_cluster]) - goto atomic_dec; - } + result = get_cluster_clk_and_freq_table(cur_cluster); + if (result) + return result; result = cpufreq_frequency_table_cpuinfo(policy, freq_table[cur_cluster]); if (result) { - if (atomic_read(&cluster_usage[cur_cluster]) == 1) - goto put_table; - goto atomic_dec; + pr_err("CPU %d, cluster: %d invalid freq table\n", policy->cpu, + cur_cluster); + put_cluster_clk_and_freq_table(cur_cluster); + return result; } cpufreq_frequency_table_get_attr(freq_table[cur_cluster], policy->cpu); @@ -177,23 +208,12 @@ static int bl_cpufreq_init(struct cpufreq_policy *policy) pr_info("CPU %d initialized\n", policy->cpu); return 0; - -put_table: - arm_bl_ops->put_freq_tbl(cur_cluster); -atomic_dec: - atomic_dec_return(&cluster_usage[cur_cluster]); - pr_err("CPU %d failed to initialize\n", policy->cpu); - return result; } static int bl_cpufreq_exit(struct cpufreq_policy *policy) { - u32 cur_cluster = cpu_to_cluster(policy->cpu); - - if (!atomic_dec_return(&cluster_usage[cur_cluster])) { - arm_bl_ops->put_freq_tbl(cur_cluster); - freq_table[cur_cluster] = NULL; - } + put_cluster_clk_and_freq_table(cpu_to_cluster(policy->cpu)); + pr_debug("%s: Exited, cpu: %d\n", __func__, policy->cpu); return 0; } -- cgit v1.2.3 From ed0ed40aa2c88dd8eef102bc4a466a8a7ca6f346 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 13 Dec 2012 15:05:21 +0530 Subject: cpufreq: arm_big_little: Add debugging info This adds pr_debugs at various places in driver to understand its behavior Signed-off-by: Viresh Kumar --- drivers/cpufreq/arm_big_little.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/cpufreq/arm_big_little.c b/drivers/cpufreq/arm_big_little.c index 49f87d1ee24..5edaba26034 100644 --- a/drivers/cpufreq/arm_big_little.c +++ b/drivers/cpufreq/arm_big_little.c @@ -83,11 +83,13 @@ static int bl_cpufreq_set_target(struct cpufreq_policy *policy, freqs.cpu = policy->cpu; + pr_debug("%s: cpu: %d, cluster: %d, oldfreq: %d, target freq: %d, new freq: %d\n", + __func__, cpu, cur_cluster, freqs.old, target_freq, + freqs.new); + if (freqs.old == freqs.new) return 0; - pr_debug("Requested Freq %d cpu %d\n", freqs.new, cpu); - for_each_cpu(freqs.cpu, policy->cpus) cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); @@ -113,11 +115,14 @@ arm_bl_copy_table_from_array(unsigned int *table, int count) struct cpufreq_frequency_table *freq_table; + pr_debug("%s: table: %p, count: %d\n", __func__, table, count); + freq_table = kmalloc(sizeof(*freq_table) * (count + 1), GFP_KERNEL); if (!freq_table) return NULL; for (i = 0; i < count; i++) { + pr_debug("%s: index: %d, freq: %d\n", __func__, i, table[i]); freq_table[i].index = i; freq_table[i].frequency = table[i]; /* in kHZ */ } @@ -131,6 +136,8 @@ EXPORT_SYMBOL_GPL(arm_bl_copy_table_from_array); void arm_bl_free_freq_table(u32 cluster) { + pr_debug("%s: free freq table\n", __func__); + kfree(freq_table[cluster]); } EXPORT_SYMBOL_GPL(arm_bl_free_freq_table); -- cgit v1.2.3 From 81ac613e2140039350f4224cba73c3935e6ea509 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Fri, 21 Dec 2012 12:25:22 +0530 Subject: cpufreq: arm_big_little: Remove all platform drivers dependencies Currently, we need to add entry for any new platform using this driver in compatible list of arm_big_little.c driver. Which is not good. Lets make this driver independent of slave drivers. Now, slave drivers would be calling register routine of arm_big_little.c driver and would pass ops as parameter. Signed-off-by: Viresh Kumar --- drivers/cpufreq/arm_big_little.c | 63 +++++++++++++++++------------------ drivers/cpufreq/arm_big_little.h | 11 ++---- drivers/cpufreq/vexpress_big_little.c | 21 +++++++++--- 3 files changed, 49 insertions(+), 46 deletions(-) diff --git a/drivers/cpufreq/arm_big_little.c b/drivers/cpufreq/arm_big_little.c index 5edaba26034..8455ac77153 100644 --- a/drivers/cpufreq/arm_big_little.c +++ b/drivers/cpufreq/arm_big_little.c @@ -23,7 +23,6 @@ #include #include #include -#include #include #include #include @@ -242,49 +241,47 @@ static struct cpufreq_driver bl_cpufreq_driver = { .attr = bl_cpufreq_attr, }; -/* All platforms using this driver must have their entry here */ -static struct of_device_id bl_of_match[] = { - { - .compatible = "arm,vexpress,v2p-ca15,tc2", - .data = vexpress_bl_get_ops - }, - {}, -}; - -static int __init bl_cpufreq_modinit(void) +int bl_cpufreq_register(struct cpufreq_arm_bl_ops *ops) { - int i, size = ARRAY_SIZE(bl_of_match); - struct cpufreq_arm_bl_ops *(*bl_get_ops)(void) = NULL; - - for (i = 0; i < size; i++) { - if (of_machine_is_compatible(bl_of_match[i].compatible)) { - bl_get_ops = bl_of_match[i].data; - break; - } + int ret; + + if (arm_bl_ops) { + pr_debug("%s: Already registered: %s, exiting\n", __func__, + arm_bl_ops->name); + return -EBUSY; } - if (!bl_get_ops) { - pr_err("No platform matched, exiting\n"); + if (!ops || !strlen(ops->name) || !ops->get_freq_tbl) { + pr_err("%s: Invalid arm_bl_ops, exiting\n", __func__); return -ENODEV; } - arm_bl_ops = bl_get_ops(); - if (!arm_bl_ops || !arm_bl_ops->get_freq_tbl) { - pr_err("Invalid ops returned by platform: %s\n", - bl_of_match[i].compatible); - return -EINVAL; + arm_bl_ops = ops; + + ret = cpufreq_register_driver(&bl_cpufreq_driver); + if (ret) { + pr_info("%s: Failed registering platform driver: %s, err: %d\n", + __func__, ops->name, ret); + arm_bl_ops = NULL; + } else { + pr_info("%s: Registered platform driver: %s\n", __func__, + ops->name); } - return cpufreq_register_driver(&bl_cpufreq_driver); + return ret; } -module_init(bl_cpufreq_modinit); +EXPORT_SYMBOL_GPL(bl_cpufreq_register); -static void __exit bl_cpufreq_modexit(void) +void bl_cpufreq_unregister(struct cpufreq_arm_bl_ops *ops) { + if (arm_bl_ops != ops) { + pr_info("%s: Registered with: %s, can't unregister, exiting\n", + __func__, arm_bl_ops->name); + } + cpufreq_unregister_driver(&bl_cpufreq_driver); + pr_info("%s: Un-registered platform driver: %s\n", __func__, + arm_bl_ops->name); arm_bl_ops = NULL; } -module_exit(bl_cpufreq_modexit); - -MODULE_DESCRIPTION("ARM big LITTLE platforms cpufreq driver"); -MODULE_LICENSE("GPL"); +EXPORT_SYMBOL_GPL(bl_cpufreq_unregister); diff --git a/drivers/cpufreq/arm_big_little.h b/drivers/cpufreq/arm_big_little.h index 050aef9c076..5b50fc72abc 100644 --- a/drivers/cpufreq/arm_big_little.h +++ b/drivers/cpufreq/arm_big_little.h @@ -23,6 +23,7 @@ #include struct cpufreq_arm_bl_ops { + char name[CPUFREQ_NAME_LEN]; struct cpufreq_frequency_table *(*get_freq_tbl)(u32 cluster, int *count); void (*put_freq_tbl)(u32 cluster); }; @@ -31,13 +32,7 @@ struct cpufreq_frequency_table * arm_bl_copy_table_from_array(unsigned int *table, int count); void arm_bl_free_freq_table(u32 cluster); -#if defined(CONFIG_ARM_VEXPRESS_BL_CPUFREQ) || defined(CONFIG_ARM_VEXPRESS_BL_CPUFREQ_MODULE) -struct cpufreq_arm_bl_ops *vexpress_bl_get_ops(void); -#else -static struct cpufreq_arm_bl_ops *vexpress_bl_get_ops(void) -{ - return NULL; -} -#endif +int bl_cpufreq_register(struct cpufreq_arm_bl_ops *ops); +void bl_cpufreq_unregister(struct cpufreq_arm_bl_ops *ops); #endif /* CPUFREQ_ARM_BIG_LITTLE_H */ diff --git a/drivers/cpufreq/vexpress_big_little.c b/drivers/cpufreq/vexpress_big_little.c index 1b44ae2ad36..81080b1f437 100644 --- a/drivers/cpufreq/vexpress_big_little.c +++ b/drivers/cpufreq/vexpress_big_little.c @@ -24,6 +24,7 @@ #include #include +#include #include #include #include "arm_big_little.h" @@ -47,17 +48,27 @@ static void vexpress_put_freq_tbl(u32 cluster) } static struct cpufreq_arm_bl_ops vexpress_bl_ops = { + .name = "vexpress-bl", .get_freq_tbl = vexpress_get_freq_tbl, .put_freq_tbl = vexpress_put_freq_tbl, }; -struct cpufreq_arm_bl_ops *vexpress_bl_get_ops(void) +static int vexpress_bl_init(void) { if (!vexpress_spc_check_loaded()) { - pr_info("No SPC found\n"); - return NULL; + pr_info("%s: No SPC found\n", __func__); + return -ENOENT; } - return &vexpress_bl_ops; + return bl_cpufreq_register(&vexpress_bl_ops); } -EXPORT_SYMBOL_GPL(vexpress_bl_get_ops); +module_init(vexpress_bl_init); + +static void vexpress_bl_exit(void) +{ + return bl_cpufreq_unregister(&vexpress_bl_ops); +} +module_exit(vexpress_bl_exit); + +MODULE_DESCRIPTION("ARM Vexpress big LITTLE cpufreq driver"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 2f977cc6afbb0eacc58cb0543d489bdf463e711e Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Fri, 21 Dec 2012 14:25:43 +0530 Subject: cpufreq: arm_big_little: Add generic driver to read frequency table from DT Some platforms might read frequency table from cluster nodes in DT, as they might not have specific hardware for that. For them, this patch adds a generic big LITTLE cpufreq driver. Freqs passed from DT must be in KHz and DT node should be like: cluster0: cluster@0 { reg = <0>; freqs = <500000 600000 700000 800000>; } cluster1: cluster@1 { reg = <1>; freqs = <900000 1000000 1100000 1200000>; } Signed-off-by: Viresh Kumar --- drivers/cpufreq/Kconfig.arm | 9 ++++ drivers/cpufreq/Makefile | 3 ++ drivers/cpufreq/arm_dt_big_little.c | 101 ++++++++++++++++++++++++++++++++++++ 3 files changed, 113 insertions(+) create mode 100644 drivers/cpufreq/arm_dt_big_little.c diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm index 55c53bd742e..84d08cabbac 100644 --- a/drivers/cpufreq/Kconfig.arm +++ b/drivers/cpufreq/Kconfig.arm @@ -118,6 +118,15 @@ config ARM_BIG_LITTLE_CPUFREQ tristate depends on ARM_CPU_TOPOLOGY +config ARM_DT_BL_CPUFREQ + tristate "Generic ARM big LITTLE CPUfreq driver probed via DT" + select ARM_BIG_LITTLE_CPUFREQ + depends on OF + default y + help + This enables the Generic CPUfreq driver for ARM big.LITTLE platform. + This gets frequency tables from DT. + config ARM_VEXPRESS_BL_CPUFREQ tristate "ARM Vexpress big LITTLE CPUfreq driver" select ARM_BIG_LITTLE_CPUFREQ diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile index f26fd28849e..6148eac24fc 100644 --- a/drivers/cpufreq/Makefile +++ b/drivers/cpufreq/Makefile @@ -59,6 +59,9 @@ obj-$(CONFIG_ARM_HIGHBANK_CPUFREQ) += highbank-cpufreq.o obj-$(CONFIG_ARM_IMX6Q_CPUFREQ) += imx6q-cpufreq.o obj-$(CONFIG_ARM_BIG_LITTLE_CPUFREQ) += arm_big_little.o obj-$(CONFIG_ARM_VEXPRESS_BL_CPUFREQ) += vexpress_big_little.o +#Keep DT_BL_CPUFREQ as the last entry in all big LITTLE drivers, so that it is +#probed last. +obj-$(CONFIG_ARM_DT_BL_CPUFREQ) += arm_dt_big_little.o ################################################################################## # PowerPC platform drivers diff --git a/drivers/cpufreq/arm_dt_big_little.c b/drivers/cpufreq/arm_dt_big_little.c new file mode 100644 index 00000000000..2a41854bd4c --- /dev/null +++ b/drivers/cpufreq/arm_dt_big_little.c @@ -0,0 +1,101 @@ +/* + * Generic big.LITTLE CPUFreq Interface driver + * + * It provides necessary ops to arm_big_little cpufreq driver and gets + * Frequency information from Device Tree. Freq table in DT must be in KHz. + * + * Copyright (C) 2012 Linaro. + * Viresh Kumar + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include "arm_big_little.h" + +static struct cpufreq_frequency_table *generic_get_freq_tbl(u32 cluster, + int *count) +{ + struct device_node *np = NULL; + const struct property *pp; + unsigned int *table = NULL; + int cluster_id; + struct cpufreq_frequency_table *cpufreq_table; + + while ((np = of_find_node_by_name(np, "cluster"))) { + if (of_property_read_u32(np, "reg", &cluster_id)) + continue; + + if (cluster_id != cluster) + continue; + + pp = of_find_property(np, "freqs", NULL); + if (!pp) + continue; + + *count = pp->length / sizeof(u32); + if (!*count) + continue; + + table = kmalloc(sizeof(*table) * (*count), GFP_KERNEL); + if (!table) { + pr_err("%s: Failed to allocate memory for table\n", + __func__); + return NULL; + } + + of_property_read_u32_array(np, "freqs", table, *count); + break; + } + + if (!table) { + pr_err("%s: Unable to retrieve Freq table from Device Tree", + __func__); + return NULL; + } + + cpufreq_table = arm_bl_copy_table_from_array(table, *count); + kfree(table); + + return cpufreq_table; +} + +static void generic_put_freq_tbl(u32 cluster) +{ + arm_bl_free_freq_table(cluster); +} + +static struct cpufreq_arm_bl_ops generic_bl_ops = { + .name = "generic-bl", + .get_freq_tbl = generic_get_freq_tbl, + .put_freq_tbl = generic_put_freq_tbl, +}; + +static int generic_bl_init(void) +{ + return bl_cpufreq_register(&generic_bl_ops); +} +module_init(generic_bl_init); + +static void generic_bl_exit(void) +{ + return bl_cpufreq_unregister(&generic_bl_ops); +} +module_exit(generic_bl_exit); + +MODULE_DESCRIPTION("Generic ARM big LITTLE cpufreq driver"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From f42fc2850fbdd9a1560b0446842f00fe9cd02749 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Tue, 20 Nov 2012 13:59:32 +0530 Subject: cpufreq: vexpress: Don't initialize policy-> min, max twice This is already done by: cpufreq_frequency_table_cpuinfo() Signed-off-by: Viresh Kumar --- drivers/cpufreq/arm_big_little.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/cpufreq/arm_big_little.c b/drivers/cpufreq/arm_big_little.c index 8455ac77153..45a2ddaba4c 100644 --- a/drivers/cpufreq/arm_big_little.c +++ b/drivers/cpufreq/arm_big_little.c @@ -202,10 +202,6 @@ static int bl_cpufreq_init(struct cpufreq_policy *policy) cpufreq_frequency_table_get_attr(freq_table[cur_cluster], policy->cpu); - /* set default policy and cpuinfo */ - policy->min = policy->cpuinfo.min_freq; - policy->max = policy->cpuinfo.max_freq; - policy->cpuinfo.transition_latency = 1000000; /* 1 ms assumed */ policy->cur = bl_cpufreq_get(policy->cpu); -- cgit v1.2.3 From a3cafa7631efb9d73c00ceb56e54eacba7104556 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Sat, 12 Jan 2013 21:11:53 +0530 Subject: cpufreq: ARM big LITTLE: Replace "bl" with standard shortname "bL" Throughout Linux code big LITTLE is written as "bL". We have used "bl" instead of "bL". Lets fix it. Signed-off-by: Viresh Kumar Reported-by: Nicolas Pitre --- drivers/cpufreq/arm_big_little.c | 76 +++++++++++++++++------------------ drivers/cpufreq/arm_big_little.h | 10 ++--- drivers/cpufreq/arm_dt_big_little.c | 18 ++++----- drivers/cpufreq/vexpress_big_little.c | 20 ++++----- 4 files changed, 62 insertions(+), 62 deletions(-) diff --git a/drivers/cpufreq/arm_big_little.c b/drivers/cpufreq/arm_big_little.c index 45a2ddaba4c..b5601fcd79e 100644 --- a/drivers/cpufreq/arm_big_little.c +++ b/drivers/cpufreq/arm_big_little.c @@ -31,7 +31,7 @@ #define MAX_CLUSTERS 2 -static struct cpufreq_arm_bl_ops *arm_bl_ops; +static struct cpufreq_arm_bL_ops *arm_bL_ops; static struct clk *clk[MAX_CLUSTERS]; static struct cpufreq_frequency_table *freq_table[MAX_CLUSTERS]; static atomic_t cluster_usage[MAX_CLUSTERS] = {ATOMIC_INIT(0), ATOMIC_INIT(0)}; @@ -46,7 +46,7 @@ static int cpu_to_cluster(int cpu) return topology_physical_package_id(cpu); } -static unsigned int bl_cpufreq_get(unsigned int cpu) +static unsigned int bL_cpufreq_get(unsigned int cpu) { u32 cur_cluster = cpu_to_cluster(cpu); @@ -54,7 +54,7 @@ static unsigned int bl_cpufreq_get(unsigned int cpu) } /* Validate policy frequency range */ -static int bl_cpufreq_verify_policy(struct cpufreq_policy *policy) +static int bL_cpufreq_verify_policy(struct cpufreq_policy *policy) { u32 cur_cluster = cpu_to_cluster(policy->cpu); @@ -63,7 +63,7 @@ static int bl_cpufreq_verify_policy(struct cpufreq_policy *policy) } /* Set clock frequency */ -static int bl_cpufreq_set_target(struct cpufreq_policy *policy, +static int bL_cpufreq_set_target(struct cpufreq_policy *policy, unsigned int target_freq, unsigned int relation) { struct cpufreq_freqs freqs; @@ -73,7 +73,7 @@ static int bl_cpufreq_set_target(struct cpufreq_policy *policy, /* ASSUMPTION: The cpu can't be hotplugged in this function */ cur_cluster = cpu_to_cluster(policy->cpu); - freqs.old = bl_cpufreq_get(policy->cpu); + freqs.old = bL_cpufreq_get(policy->cpu); /* Determine valid target frequency using freq_table */ cpufreq_frequency_table_target(policy, freq_table[cur_cluster], @@ -108,7 +108,7 @@ static int bl_cpufreq_set_target(struct cpufreq_policy *policy, /* translate the integer array into cpufreq_frequency_table entries */ struct cpufreq_frequency_table * -arm_bl_copy_table_from_array(unsigned int *table, int count) +arm_bL_copy_table_from_array(unsigned int *table, int count) { int i; @@ -131,22 +131,22 @@ arm_bl_copy_table_from_array(unsigned int *table, int count) return freq_table; } -EXPORT_SYMBOL_GPL(arm_bl_copy_table_from_array); +EXPORT_SYMBOL_GPL(arm_bL_copy_table_from_array); -void arm_bl_free_freq_table(u32 cluster) +void arm_bL_free_freq_table(u32 cluster) { pr_debug("%s: free freq table\n", __func__); kfree(freq_table[cluster]); } -EXPORT_SYMBOL_GPL(arm_bl_free_freq_table); +EXPORT_SYMBOL_GPL(arm_bL_free_freq_table); static void put_cluster_clk_and_freq_table(u32 cluster) { if (!atomic_dec_return(&cluster_usage[cluster])) { clk_put(clk[cluster]); clk[cluster] = NULL; - arm_bl_ops->put_freq_tbl(cluster); + arm_bL_ops->put_freq_tbl(cluster); freq_table[cluster] = NULL; pr_debug("%s: cluster: %d\n", __func__, cluster); } @@ -160,7 +160,7 @@ static int get_cluster_clk_and_freq_table(u32 cluster) if (atomic_inc_return(&cluster_usage[cluster]) != 1) return 0; - freq_table[cluster] = arm_bl_ops->get_freq_tbl(cluster, &count); + freq_table[cluster] = arm_bL_ops->get_freq_tbl(cluster, &count); if (!freq_table[cluster]) goto atomic_dec; @@ -173,7 +173,7 @@ static int get_cluster_clk_and_freq_table(u32 cluster) return 0; } - arm_bl_ops->put_freq_tbl(cluster); + arm_bL_ops->put_freq_tbl(cluster); atomic_dec: atomic_dec(&cluster_usage[cluster]); @@ -182,7 +182,7 @@ atomic_dec: } /* Per-CPU initialization */ -static int bl_cpufreq_init(struct cpufreq_policy *policy) +static int bL_cpufreq_init(struct cpufreq_policy *policy) { u32 cur_cluster = cpu_to_cluster(policy->cpu); int result; @@ -203,7 +203,7 @@ static int bl_cpufreq_init(struct cpufreq_policy *policy) cpufreq_frequency_table_get_attr(freq_table[cur_cluster], policy->cpu); policy->cpuinfo.transition_latency = 1000000; /* 1 ms assumed */ - policy->cur = bl_cpufreq_get(policy->cpu); + policy->cur = bL_cpufreq_get(policy->cpu); cpumask_copy(policy->cpus, topology_core_cpumask(policy->cpu)); cpumask_copy(policy->related_cpus, policy->cpus); @@ -212,7 +212,7 @@ static int bl_cpufreq_init(struct cpufreq_policy *policy) return 0; } -static int bl_cpufreq_exit(struct cpufreq_policy *policy) +static int bL_cpufreq_exit(struct cpufreq_policy *policy) { put_cluster_clk_and_freq_table(cpu_to_cluster(policy->cpu)); pr_debug("%s: Exited, cpu: %d\n", __func__, policy->cpu); @@ -221,44 +221,44 @@ static int bl_cpufreq_exit(struct cpufreq_policy *policy) } /* Export freq_table to sysfs */ -static struct freq_attr *bl_cpufreq_attr[] = { +static struct freq_attr *bL_cpufreq_attr[] = { &cpufreq_freq_attr_scaling_available_freqs, NULL, }; -static struct cpufreq_driver bl_cpufreq_driver = { +static struct cpufreq_driver bL_cpufreq_driver = { .name = "arm-big-little", .flags = CPUFREQ_STICKY, - .verify = bl_cpufreq_verify_policy, - .target = bl_cpufreq_set_target, - .get = bl_cpufreq_get, - .init = bl_cpufreq_init, - .exit = bl_cpufreq_exit, - .attr = bl_cpufreq_attr, + .verify = bL_cpufreq_verify_policy, + .target = bL_cpufreq_set_target, + .get = bL_cpufreq_get, + .init = bL_cpufreq_init, + .exit = bL_cpufreq_exit, + .attr = bL_cpufreq_attr, }; -int bl_cpufreq_register(struct cpufreq_arm_bl_ops *ops) +int bL_cpufreq_register(struct cpufreq_arm_bL_ops *ops) { int ret; - if (arm_bl_ops) { + if (arm_bL_ops) { pr_debug("%s: Already registered: %s, exiting\n", __func__, - arm_bl_ops->name); + arm_bL_ops->name); return -EBUSY; } if (!ops || !strlen(ops->name) || !ops->get_freq_tbl) { - pr_err("%s: Invalid arm_bl_ops, exiting\n", __func__); + pr_err("%s: Invalid arm_bL_ops, exiting\n", __func__); return -ENODEV; } - arm_bl_ops = ops; + arm_bL_ops = ops; - ret = cpufreq_register_driver(&bl_cpufreq_driver); + ret = cpufreq_register_driver(&bL_cpufreq_driver); if (ret) { pr_info("%s: Failed registering platform driver: %s, err: %d\n", __func__, ops->name, ret); - arm_bl_ops = NULL; + arm_bL_ops = NULL; } else { pr_info("%s: Registered platform driver: %s\n", __func__, ops->name); @@ -266,18 +266,18 @@ int bl_cpufreq_register(struct cpufreq_arm_bl_ops *ops) return ret; } -EXPORT_SYMBOL_GPL(bl_cpufreq_register); +EXPORT_SYMBOL_GPL(bL_cpufreq_register); -void bl_cpufreq_unregister(struct cpufreq_arm_bl_ops *ops) +void bL_cpufreq_unregister(struct cpufreq_arm_bL_ops *ops) { - if (arm_bl_ops != ops) { + if (arm_bL_ops != ops) { pr_info("%s: Registered with: %s, can't unregister, exiting\n", - __func__, arm_bl_ops->name); + __func__, arm_bL_ops->name); } - cpufreq_unregister_driver(&bl_cpufreq_driver); + cpufreq_unregister_driver(&bL_cpufreq_driver); pr_info("%s: Un-registered platform driver: %s\n", __func__, - arm_bl_ops->name); - arm_bl_ops = NULL; + arm_bL_ops->name); + arm_bL_ops = NULL; } -EXPORT_SYMBOL_GPL(bl_cpufreq_unregister); +EXPORT_SYMBOL_GPL(bL_cpufreq_unregister); diff --git a/drivers/cpufreq/arm_big_little.h b/drivers/cpufreq/arm_big_little.h index 5b50fc72abc..6712a501198 100644 --- a/drivers/cpufreq/arm_big_little.h +++ b/drivers/cpufreq/arm_big_little.h @@ -22,17 +22,17 @@ #include #include -struct cpufreq_arm_bl_ops { +struct cpufreq_arm_bL_ops { char name[CPUFREQ_NAME_LEN]; struct cpufreq_frequency_table *(*get_freq_tbl)(u32 cluster, int *count); void (*put_freq_tbl)(u32 cluster); }; struct cpufreq_frequency_table * -arm_bl_copy_table_from_array(unsigned int *table, int count); -void arm_bl_free_freq_table(u32 cluster); +arm_bL_copy_table_from_array(unsigned int *table, int count); +void arm_bL_free_freq_table(u32 cluster); -int bl_cpufreq_register(struct cpufreq_arm_bl_ops *ops); -void bl_cpufreq_unregister(struct cpufreq_arm_bl_ops *ops); +int bL_cpufreq_register(struct cpufreq_arm_bL_ops *ops); +void bL_cpufreq_unregister(struct cpufreq_arm_bL_ops *ops); #endif /* CPUFREQ_ARM_BIG_LITTLE_H */ diff --git a/drivers/cpufreq/arm_dt_big_little.c b/drivers/cpufreq/arm_dt_big_little.c index 2a41854bd4c..fabfb9c5c37 100644 --- a/drivers/cpufreq/arm_dt_big_little.c +++ b/drivers/cpufreq/arm_dt_big_little.c @@ -68,7 +68,7 @@ static struct cpufreq_frequency_table *generic_get_freq_tbl(u32 cluster, return NULL; } - cpufreq_table = arm_bl_copy_table_from_array(table, *count); + cpufreq_table = arm_bL_copy_table_from_array(table, *count); kfree(table); return cpufreq_table; @@ -76,26 +76,26 @@ static struct cpufreq_frequency_table *generic_get_freq_tbl(u32 cluster, static void generic_put_freq_tbl(u32 cluster) { - arm_bl_free_freq_table(cluster); + arm_bL_free_freq_table(cluster); } -static struct cpufreq_arm_bl_ops generic_bl_ops = { +static struct cpufreq_arm_bL_ops generic_bL_ops = { .name = "generic-bl", .get_freq_tbl = generic_get_freq_tbl, .put_freq_tbl = generic_put_freq_tbl, }; -static int generic_bl_init(void) +static int generic_bL_init(void) { - return bl_cpufreq_register(&generic_bl_ops); + return bL_cpufreq_register(&generic_bL_ops); } -module_init(generic_bl_init); +module_init(generic_bL_init); -static void generic_bl_exit(void) +static void generic_bL_exit(void) { - return bl_cpufreq_unregister(&generic_bl_ops); + return bL_cpufreq_unregister(&generic_bL_ops); } -module_exit(generic_bl_exit); +module_exit(generic_bL_exit); MODULE_DESCRIPTION("Generic ARM big LITTLE cpufreq driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/cpufreq/vexpress_big_little.c b/drivers/cpufreq/vexpress_big_little.c index 81080b1f437..66648c3fc94 100644 --- a/drivers/cpufreq/vexpress_big_little.c +++ b/drivers/cpufreq/vexpress_big_little.c @@ -39,36 +39,36 @@ static struct cpufreq_frequency_table *vexpress_get_freq_tbl(u32 cluster, return NULL; } - return arm_bl_copy_table_from_array(table, *count); + return arm_bL_copy_table_from_array(table, *count); } static void vexpress_put_freq_tbl(u32 cluster) { - arm_bl_free_freq_table(cluster); + arm_bL_free_freq_table(cluster); } -static struct cpufreq_arm_bl_ops vexpress_bl_ops = { - .name = "vexpress-bl", +static struct cpufreq_arm_bL_ops vexpress_bL_ops = { + .name = "vexpress-bL", .get_freq_tbl = vexpress_get_freq_tbl, .put_freq_tbl = vexpress_put_freq_tbl, }; -static int vexpress_bl_init(void) +static int vexpress_bL_init(void) { if (!vexpress_spc_check_loaded()) { pr_info("%s: No SPC found\n", __func__); return -ENOENT; } - return bl_cpufreq_register(&vexpress_bl_ops); + return bL_cpufreq_register(&vexpress_bL_ops); } -module_init(vexpress_bl_init); +module_init(vexpress_bL_init); -static void vexpress_bl_exit(void) +static void vexpress_bL_exit(void) { - return bl_cpufreq_unregister(&vexpress_bl_ops); + return bL_cpufreq_unregister(&vexpress_bL_ops); } -module_exit(vexpress_bl_exit); +module_exit(vexpress_bL_exit); MODULE_DESCRIPTION("ARM Vexpress big LITTLE cpufreq driver"); MODULE_LICENSE("GPL"); -- cgit v1.2.3 From be19b71b407ca8fbf196814a65bfa1a99bda3370 Mon Sep 17 00:00:00 2001 From: Tushar Behera Date: Wed, 23 Jan 2013 11:11:50 +0530 Subject: cpufreq: ARM_DT_BL_CPUFREQ should be enabled only when BIG_LITTLE is enabled Currently ARM_DT_BL_CPUFREQ gets enabled by default which causes a kernel-oops on Arndale board. Forcing this to only when BIG_LITTLE is enabled fixes following issue. Unable to handle kernel paging request at virtual address ffffffd0 pgd = c0004000 [ffffffd0] *pgd=6f7fe821, *pte=00000000, *ppte=00000000 Internal error: Oops: 17 [#2] PREEMPT SMP THUMB2 Modules linked in: CPU: 0 Tainted: G D (3.8.0-rc4+ #2) PC is at kthread_data+0xa/0x10 LR is at wq_worker_sleeping+0xf/0xa4 pc : [] lr : [] psr: a00000b3 sp : ef0adb98 ip : 00000001 fp : ef0a6080 r10: c1bb7140 r9 : c05cd140 r8 : ef0ac000 r7 : ef0adbb8 r6 : ef0a6080 r5 : 00000000 r4 : b0000000 r3 : 00000000 r2 : 00000000 r1 : 00000000 r0 : ef0a6080 Flags: NzCv IRQs off FIQs on Mode SVC_32 ISA Thumb Segment user Control: 50c5387d Table: 6e4c806a DAC: 55555555 Signed-off-by: Tushar Behera Signed-off-by: Jon Medhurst --- drivers/cpufreq/Kconfig.arm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm index 84d08cabbac..6ef9c7b0691 100644 --- a/drivers/cpufreq/Kconfig.arm +++ b/drivers/cpufreq/Kconfig.arm @@ -121,7 +121,7 @@ config ARM_BIG_LITTLE_CPUFREQ config ARM_DT_BL_CPUFREQ tristate "Generic ARM big LITTLE CPUfreq driver probed via DT" select ARM_BIG_LITTLE_CPUFREQ - depends on OF + depends on OF && BIG_LITTLE default y help This enables the Generic CPUfreq driver for ARM big.LITTLE platform. @@ -130,7 +130,7 @@ config ARM_DT_BL_CPUFREQ config ARM_VEXPRESS_BL_CPUFREQ tristate "ARM Vexpress big LITTLE CPUfreq driver" select ARM_BIG_LITTLE_CPUFREQ - depends on ARM_SPC + depends on ARM_SPC && BIG_LITTLE help This enables the CPUfreq driver for ARM Vexpress big.LITTLE platform. If in doubt, say N. -- cgit v1.2.3