summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJon Medhurst <tixy@linaro.org>2013-01-21 18:38:31 +0000
committerJon Medhurst <tixy@linaro.org>2013-01-21 18:38:31 +0000
commitd21ffe111eac3c6383e38af98499844b7bb07357 (patch)
treeae35f4dc0d2eba347bb97e38e93d18e4f394da8b
parentc60aee266730dd27882c07fa4f93635b3dda6ac1 (diff)
parent0db5555637570fdac43ac8ca86f1b56289991a17 (diff)
downloadlinux-topics-tracking-integration-linaro-vexpress-ll-20130122.0.tar.gz
Merge branch 'tracking-armlt-tc2-pm-new-cpufreq' into integration-linaro-vexpresstracking-integration-linaro-vexpress-ll-20130122.1tracking-integration-linaro-vexpress-ll-20130122.0
-rw-r--r--arch/arm/boot/dts/vexpress-v2p-ca15-tc2.dts2
-rw-r--r--arch/arm/kernel/topology.c2
-rw-r--r--drivers/clk/versatile/Makefile2
-rw-r--r--drivers/clk/versatile/clk-vexpress-spc.c130
-rw-r--r--drivers/clk/versatile/clk-vexpress.c1
-rw-r--r--drivers/cpufreq/Kconfig.arm22
-rw-r--r--drivers/cpufreq/Makefile6
-rw-r--r--drivers/cpufreq/arm_big_little.c283
-rw-r--r--drivers/cpufreq/arm_big_little.h38
-rw-r--r--drivers/cpufreq/arm_dt_big_little.c101
-rw-r--r--drivers/cpufreq/vexpress_bL_cpufreq.c282
-rw-r--r--drivers/cpufreq/vexpress_big_little.c74
-rw-r--r--drivers/misc/vexpress/arm-spc.c12
-rw-r--r--include/linux/vexpress.h10
14 files changed, 673 insertions, 292 deletions
diff --git a/arch/arm/boot/dts/vexpress-v2p-ca15-tc2.dts b/arch/arm/boot/dts/vexpress-v2p-ca15-tc2.dts
index 56269451116..b261bde759a 100644
--- a/arch/arm/boot/dts/vexpress-v2p-ca15-tc2.dts
+++ b/arch/arm/boot/dts/vexpress-v2p-ca15-tc2.dts
@@ -37,7 +37,6 @@
cluster0: cluster@0 {
reg = <0>;
- freqs = <500000000 600000000 700000000 800000000 900000000 1000000000 1100000000 1200000000>;
cores {
#address-cells = <1>;
#size-cells = <0>;
@@ -55,7 +54,6 @@
cluster1: cluster@1 {
reg = <1>;
- freqs = <350000000 400000000 500000000 600000000 700000000 800000000 900000000 1000000000>;
cores {
#address-cells = <1>;
#size-cells = <0>;
diff --git a/arch/arm/kernel/topology.c b/arch/arm/kernel/topology.c
index b6e04a002cf..181ee5d974d 100644
--- a/arch/arm/kernel/topology.c
+++ b/arch/arm/kernel/topology.c
@@ -13,6 +13,7 @@
#include <linux/cpu.h>
#include <linux/cpumask.h>
+#include <linux/export.h>
#include <linux/init.h>
#include <linux/percpu.h>
#include <linux/node.h>
@@ -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);
int arch_sd_local_flags(int level)
{
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..3ee7d410ddf
--- /dev/null
+++ b/drivers/clk/versatile/clk-vexpress-spc.c
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2012 ARM Limited
+ * Copyright (C) 2012 Linaro
+ *
+ * Author: Viresh Kumar <viresh.kumar@linaro.org>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+/* SPC clock programming interface for Vexpress cpus */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/vexpress.h>
+
+struct clk_spc {
+ struct clk_hw hw;
+ spinlock_t *lock;
+ int cluster;
+};
+
+#define to_clk_spc(spc) container_of(spc, struct clk_spc, hw)
+
+static unsigned long spc_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct clk_spc *spc = to_clk_spc(hw);
+ u32 freq;
+
+ if (vexpress_spc_get_performance(spc->cluster, &freq)) {
+ return -EIO;
+ pr_err("%s: Failed", __func__);
+ }
+
+ return freq * 1000;
+}
+
+static long spc_round_rate(struct clk_hw *hw, unsigned long drate,
+ unsigned long *parent_rate)
+{
+ return drate;
+}
+
+static int spc_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct clk_spc *spc = to_clk_spc(hw);
+
+ return vexpress_spc_set_performance(spc->cluster, rate / 1000);
+}
+
+static struct clk_ops clk_spc_ops = {
+ .recalc_rate = spc_recalc_rate,
+ .round_rate = spc_round_rate,
+ .set_rate = spc_set_rate,
+};
+
+struct clk *vexpress_clk_register_spc(const char *name, int cluster_id)
+{
+ struct clk_init_data init;
+ struct clk_spc *spc;
+ struct clk *clk;
+
+ if (!name) {
+ pr_err("Invalid name passed");
+ return ERR_PTR(-EINVAL);
+ }
+
+ spc = kzalloc(sizeof(*spc), GFP_KERNEL);
+ if (!spc) {
+ pr_err("could not allocate spc clk\n");
+ return ERR_PTR(-ENOMEM);
+ }
+
+ spc->hw.init = &init;
+ spc->cluster = cluster_id;
+
+ init.name = name;
+ init.ops = &clk_spc_ops;
+ init.flags = CLK_IS_ROOT | CLK_GET_RATE_NOCACHE;
+ init.num_parents = 0;
+
+ clk = clk_register(NULL, &spc->hw);
+ if (!IS_ERR_OR_NULL(clk))
+ return clk;
+
+ pr_err("clk register failed\n");
+ kfree(spc);
+
+ return NULL;
+}
+
+#if defined(CONFIG_OF)
+void __init vexpress_clk_of_register_spc(void)
+{
+ char name[9] = "cluster";
+ struct device_node *node = NULL;
+ struct clk *clk;
+ const u32 *val;
+ int cluster_id = 0, len;
+
+ if (!of_find_compatible_node(NULL, NULL, "arm,spc")) {
+ pr_debug("%s: No SPC found, Exiting!!\n", __func__);
+ return;
+ }
+
+ while ((node = of_find_node_by_name(node, "cluster"))) {
+ val = of_get_property(node, "reg", &len);
+ if (val && len == 4)
+ cluster_id = be32_to_cpup(val);
+
+ name[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);
+ }
+}
+#endif
diff --git a/drivers/clk/versatile/clk-vexpress.c b/drivers/clk/versatile/clk-vexpress.c
index c742ac7c60b..e59714c2be4 100644
--- a/drivers/clk/versatile/clk-vexpress.c
+++ b/drivers/clk/versatile/clk-vexpress.c
@@ -112,6 +112,7 @@ void __init vexpress_clk_of_init(void)
struct clk *refclk, *timclk;
of_clk_init(vexpress_fixed_clk_match);
+ vexpress_clk_of_register_spc();
node = of_find_compatible_node(NULL, NULL, "arm,sp810");
vexpress_sp810_init(of_iomap(node, 0));
diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm
index 8db6f17b749..7985dcd2f5c 100644
--- a/drivers/cpufreq/Kconfig.arm
+++ b/drivers/cpufreq/Kconfig.arm
@@ -84,11 +84,23 @@ config ARM_SPEAR_CPUFREQ
help
This adds the CPUFreq driver support for SPEAr SOCs.
-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
+ 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 CPUfreq driver for ARM Vexpress big.LITTLE
- platform.
+ 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
+ depends on ARM_SPC
+ 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 fb5bb4423b8..8847c965094 100644
--- a/drivers/cpufreq/Makefile
+++ b/drivers/cpufreq/Makefile
@@ -53,7 +53,11 @@ obj-$(CONFIG_ARM_EXYNOS4X12_CPUFREQ) += exynos4x12-cpufreq.o
obj-$(CONFIG_ARM_EXYNOS5250_CPUFREQ) += exynos5250-cpufreq.o
obj-$(CONFIG_ARM_OMAP2PLUS_CPUFREQ) += omap-cpufreq.o
obj-$(CONFIG_ARM_SPEAR_CPUFREQ) += spear-cpufreq.o
-obj-$(CONFIG_ARM_VEXPRESS_BL_CPUFREQ) += vexpress_bL_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_big_little.c b/drivers/cpufreq/arm_big_little.c
new file mode 100644
index 00000000000..b5601fcd79e
--- /dev/null
+++ b/drivers/cpufreq/arm_big_little.c
@@ -0,0 +1,283 @@
+/*
+ * ARM big.LITTLE Platforms CPUFreq support
+ *
+ * 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/clk.h>
+#include <linux/cpufreq.h>
+#include <linux/cpumask.h>
+#include <linux/export.h>
+#include <linux/of_platform.h>
+#include <linux/slab.h>
+#include <linux/types.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 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)};
+
+/*
+ * 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);
+}
+
+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)
+{
+ 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);
+
+ freqs.old = bL_cpufreq_get(policy->cpu);
+
+ /* 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;
+
+ 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;
+
+ for_each_cpu(freqs.cpu, policy->cpus)
+ cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
+
+ ret = clk_set_rate(clk[cur_cluster], freqs.new * 1000);
+ if (ret) {
+ pr_err("clk_set_rate failed: %d\n", ret);
+ return ret;
+ }
+
+ policy->cur = freqs.new;
+
+ for_each_cpu(freqs.cpu, policy->cpus)
+ cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
+
+ return ret;
+}
+
+/* translate the integer array into cpufreq_frequency_table entries */
+struct cpufreq_frequency_table *
+arm_bL_copy_table_from_array(unsigned int *table, int count)
+{
+ int i;
+
+ 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 */
+ }
+
+ freq_table[i].index = count;
+ freq_table[i].frequency = CPUFREQ_TABLE_END;
+
+ return freq_table;
+}
+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);
+
+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;
+
+ 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) {
+ 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);
+
+ 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("CPU %d initialized\n", policy->cpu);
+ return 0;
+}
+
+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);
+
+ return 0;
+}
+
+/* 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,
+ .exit = bL_cpufreq_exit,
+ .attr = bL_cpufreq_attr,
+};
+
+int bL_cpufreq_register(struct cpufreq_arm_bL_ops *ops)
+{
+ int ret;
+
+ if (arm_bL_ops) {
+ pr_debug("%s: Already registered: %s, exiting\n", __func__,
+ 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__);
+ return -ENODEV;
+ }
+
+ 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 ret;
+}
+EXPORT_SYMBOL_GPL(bL_cpufreq_register);
+
+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;
+}
+EXPORT_SYMBOL_GPL(bL_cpufreq_unregister);
diff --git a/drivers/cpufreq/arm_big_little.h b/drivers/cpufreq/arm_big_little.h
new file mode 100644
index 00000000000..6712a501198
--- /dev/null
+++ b/drivers/cpufreq/arm_big_little.h
@@ -0,0 +1,38 @@
+/*
+ * 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 {
+ 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);
+
+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
new file mode 100644
index 00000000000..fabfb9c5c37
--- /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 <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/module.h>
+#include <linux/of.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#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");
diff --git a/drivers/cpufreq/vexpress_bL_cpufreq.c b/drivers/cpufreq/vexpress_bL_cpufreq.c
deleted file mode 100644
index 5718bbecd98..00000000000
--- a/drivers/cpufreq/vexpress_bL_cpufreq.c
+++ /dev/null
@@ -1,282 +0,0 @@
-/*
- * Vexpress big.LITTLE CPUFreq support
- * Based on mach-integrator
- *
- * Copyright (C) 2012 ARM Ltd.
- * Author: Sudeep KarkadaNagesha <sudeep.karkadanagesha@arm.com>
- *
- * 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 <linux/cpufreq.h>
-#include <linux/cpumask.h>
-#include <linux/init.h>
-#include <linux/io.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/of_platform.h>
-#include <linux/sched.h>
-#include <linux/slab.h>
-#include <linux/smp.h>
-#include <linux/spinlock.h>
-#include <linux/sysfs.h>
-#include <linux/types.h>
-
-#include <linux/vexpress.h>
-
-#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, &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);
- 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 = get_current_cached_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 = get_current_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);
-
- 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 = "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);
diff --git a/drivers/cpufreq/vexpress_big_little.c b/drivers/cpufreq/vexpress_big_little.c
new file mode 100644
index 00000000000..66648c3fc94
--- /dev/null
+++ b/drivers/cpufreq/vexpress_big_little.c
@@ -0,0 +1,74 @@
+/*
+ * 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/module.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 = {
+ .name = "vexpress-bL",
+ .get_freq_tbl = vexpress_get_freq_tbl,
+ .put_freq_tbl = vexpress_put_freq_tbl,
+};
+
+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);
+}
+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");
diff --git a/drivers/misc/vexpress/arm-spc.c b/drivers/misc/vexpress/arm-spc.c
index 5708bb35a10..913dd087282 100644
--- a/drivers/misc/vexpress/arm-spc.c
+++ b/drivers/misc/vexpress/arm-spc.c
@@ -117,6 +117,7 @@ struct vexpress_spc_drvdata {
struct semaphore lock;
struct completion done;
uint32_t freqs[MAX_CLUSTERS][MAX_OPPS];
+ int freqs_cnt[MAX_CLUSTERS];
};
static struct vexpress_spc_drvdata *info;
@@ -278,7 +279,7 @@ static int vexpress_spc_find_perf_index(int cluster, u32 freq)
{
int idx;
/* Hash function would be ideal, based on hashtable in v3.8?? */
- for (idx = 0; idx < MAX_OPPS; idx++)
+ for (idx = 0; idx < info->freqs_cnt[cluster]; idx++)
if (info->freqs[cluster][idx] == freq)
break;
return idx;
@@ -613,9 +614,18 @@ static int vexpress_spc_populate_opps(uint32_t cluster)
break;
}
+ info->freqs_cnt[cluster] = j;
return ret;
}
+unsigned int *vexpress_spc_get_freq_table(uint32_t cluster, int *count)
+{
+
+ *count = info->freqs_cnt[cluster];
+ return info->freqs[cluster];
+}
+EXPORT_SYMBOL_GPL(vexpress_spc_get_freq_table);
+
static int vexpress_spc_init(void)
{
struct device_node *node = of_find_compatible_node(NULL, NULL,
diff --git a/include/linux/vexpress.h b/include/linux/vexpress.h
index ae05b033f2e..203c241cd5f 100644
--- a/include/linux/vexpress.h
+++ b/include/linux/vexpress.h
@@ -15,6 +15,7 @@
#define _LINUX_VEXPRESS_H
#include <linux/device.h>
+#include <linux/err.h>
#define VEXPRESS_SITE_MB 0
#define VEXPRESS_SITE_DB1 1
@@ -115,6 +116,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);
@@ -139,6 +143,7 @@ extern int vexpress_spc_standbywfi_status(int cluster, int cpu);
extern int vexpress_spc_standbywfil2_status(int cluster);
extern int vexpress_spc_set_cpu_wakeup_irq(u32 cpu, u32 cluster, u32 set);
extern int vexpress_spc_set_global_wakeup_intr(u32 set);
+extern unsigned int *vexpress_spc_get_freq_table(uint32_t cluster, int *count);
extern int vexpress_spc_get_performance(int cluster, u32 *freq);
extern int vexpress_spc_set_performance(int cluster, u32 freq);
extern int vexpress_spc_wfi_cpustat(int cluster);
@@ -201,6 +206,11 @@ static inline u32 vexpress_scc_read_rststat(int cluster)
return 0;
}
+static inline unsigned int *vexpress_spc_get_freq_table(uint32_t cluster, int *count)
+{
+ return ERR_PTR(-ENOSYS);
+}
+
static inline int vexpress_spc_get_performance(int cluster, u32 *freq)
{
return -ENOSYS;