aboutsummaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorViresh Kumar <viresh.kumar@linaro.org>2012-12-01 17:16:02 +0530
committerJon Medhurst <tixy@linaro.org>2013-04-29 09:43:52 +0100
commit5969835edfb72d6a848899ec9b0126cdb3f9124c (patch)
tree123472091027aa203cd8fd9b674908f5dc22245d /drivers
parent1f8d4aeb041757bf66d96b60922c753b42995a6a (diff)
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 <viresh.kumar@linaro.org>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/cpufreq/Kconfig.arm9
-rw-r--r--drivers/cpufreq/Makefile1
-rw-r--r--drivers/cpufreq/arm_big_little.c150
-rw-r--r--drivers/cpufreq/arm_big_little.h43
-rw-r--r--drivers/cpufreq/vexpress_big_little.c63
5 files changed, 198 insertions, 68 deletions
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 <linux/cpufreq.h>
#include <linux/cpumask.h>
+#include <linux/export.h>
#include <linux/module.h>
#include <linux/of_platform.h>
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/vexpress.h>
#include <asm/topology.h>
+#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 <sudeep.karkadanagesha@arm.com>
+ *
+ * Copyright (C) 2012 ARM Ltd.
+ * Viresh Kumar <viresh.kumar@linaro.org>
+ *
+ * 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 <linux/cpufreq.h>
+#include <linux/types.h>
+
+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 <sudeep.karkadanagesha@arm.com>
+ *
+ * Copyright (C) 2012 Linaro.
+ * Viresh Kumar <viresh.kumar@linaro.org>
+ *
+ * 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 <linux/cpufreq.h>
+#include <linux/export.h>
+#include <linux/types.h>
+#include <linux/vexpress.h>
+#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);