diff --git a/Documentation/cpu-freq/cpufreq-arm-bl.txt b/Documentation/cpu-freq/cpufreq-arm-bl.txt
new file mode 100644
index 0000000..52e2f3a
--- /dev/null
+++ b/Documentation/cpu-freq/cpufreq-arm-bl.txt
@@ -0,0 +1,47 @@
+Synchronous cluster switching interface for the ARM big.LITTLE switcher
+-----------------------------------------------------------------------
+
+The arm-bl-cpufreq driver provides a simple interface which models two
+clusters as two performance points.
+
+Within each CPU's cpufreq directory in sysfs
+(/sys/devices/system/cpu/cpu?/cpufreq/):
+
+cpuinfo_max_freq:
+
+	reports the dummy frequency value which corresponds to the "big"
+	cluster.
+
+cpuinfo_min_freq:
+
+	reports the dummy frequency value which corresponds to the
+	"little" cluster.
+
+cpuinfo_cur_freq:
+
+	reports the dummy frequency corresponding to the currently
+	running cluster.
+
+
+To switch clusters, either the built-in "powersave" or "performance"
+governors can be used to force the "little" or "big" cluster
+respectively; or alternatively the "userspace" governor can be used,
+
+The following script fragment demonstrates how the userspace governor
+can be used to switch:
+
+
+for x in /sys/devices/system/cpu/cpu[0-9]*; do
+	echo userspace >$x/cpufreq/scaling_governor
+done
+
+big_freq=`cat /sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq`
+little_freq=`cat /sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_min_freq`
+
+switch_to_big () {
+	echo $big_freq >/sys/devices/system/cpu/cpu0/cpufreq/scaling_setspeed
+}
+
+switch_to_little () {
+	echo $little_freq >/sys/devices/system/cpu/cpu0/cpufreq/scaling_setspeed
+}
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 455367d..907d44a 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -301,6 +301,7 @@ config ARCH_VERSATILE
 config ARCH_VEXPRESS
 	bool "ARM Ltd. Versatile Express family"
 	select ARCH_WANT_OPTIONAL_GPIOLIB
+	select ARCH_HAS_CPUFREQ
 	select ARM_AMBA
 	select ARM_TIMER_SP804
 	select CLKDEV_LOOKUP
diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm
index 72a0044..0e4f6d0 100644
--- a/drivers/cpufreq/Kconfig.arm
+++ b/drivers/cpufreq/Kconfig.arm
@@ -30,3 +30,21 @@ config ARM_EXYNOS4210_CPUFREQ
 	  SoC (S5PV310 or S5PC210).
 
 	  If in doubt, say N.
+
+config ARM_BL_CPUFREQ
+	depends on EXPERIMENTAL
+	depends on ARCH_VEXPRESS_DT
+	tristate "Simple cpufreq interface for the ARM big.LITTLE switcher"
+	help
+	  Provides a simple cpufreq interface to control the ARM
+	  big.LITTLE switcher.
+
+	  Note that this code is not currently safe unless the
+	  big.LITTLE switcher binary has been loaded separately by an
+	  external bootloader or firmware before entering the kernel.
+	  Otherwise, you can still build this code as a module,
+	  providing that you don't load it.
+
+	  Refer to Documentation/cpufreq/cpufreq-arm-bl.txt for details.
+
+	  If unsure, say N.
diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile
index a48bc02..ecf492d 100644
--- a/drivers/cpufreq/Makefile
+++ b/drivers/cpufreq/Makefile
@@ -40,6 +40,10 @@ obj-$(CONFIG_X86_CPUFREQ_NFORCE2)	+= cpufreq-nforce2.o
 ##################################################################################
 # ARM SoC drivers
 obj-$(CONFIG_UX500_SOC_DB8500)		+= db8500-cpufreq.o
+obj-$(CONFIG_ARM_BL_CPUFREQ)		+= arm-bl-cpufreq.o
+arm-bl-cpufreq-y			+= arm-bl-cpufreq_driver.o \
+					   arm-bl-cpufreq_hvc.o
+AFLAGS_arm-bl-cpufreq_hvc.o		:= -march=armv7-a
 obj-$(CONFIG_ARM_S3C64XX_CPUFREQ)	+= s3c64xx-cpufreq.o
 obj-$(CONFIG_ARM_S5PV210_CPUFREQ)	+= s5pv210-cpufreq.o
 obj-$(CONFIG_ARM_EXYNOS4210_CPUFREQ)	+= exynos4210-cpufreq.o
diff --git a/drivers/cpufreq/arm-bl-cpufreq.h b/drivers/cpufreq/arm-bl-cpufreq.h
new file mode 100644
index 0000000..2f9b0dc
--- /dev/null
+++ b/drivers/cpufreq/arm-bl-cpufreq.h
@@ -0,0 +1,13 @@
+#ifndef ARM_BL_CPUFREQ_HVC_H
+#define ARM_BL_CPUFREQ_HVC_H
+
+#ifndef __ASSEMBLY__
+int __arm_bl_get_cluster(void);
+void __arm_bl_switch_cluster(void);
+#endif /* ! __ASSEMBLY__ */
+
+/* Hypervisor call numbers for the ARM big.LITTLE switcher: */
+#define ARM_BL_HVC_SWITCH_CLUSTER 1
+#define ARM_BL_HVC_GET_MPIDR 2
+
+#endif /* ! ARM_BL_CPUFREQ_HVC_H */
diff --git a/drivers/cpufreq/arm-bl-cpufreq_driver.c b/drivers/cpufreq/arm-bl-cpufreq_driver.c
new file mode 100644
index 0000000..faf2fc0
--- /dev/null
+++ b/drivers/cpufreq/arm-bl-cpufreq_driver.c
@@ -0,0 +1,217 @@
+/*
+ * arm-bl-cpufreq.c: Simple cpufreq backend for the ARM big.LITTLE switcher
+ * Copyright (C) 2012  Linaro Limited
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+/* WARNING: This code is experimental and depends on external firmware */
+
+#include <linux/bug.h>
+#include <linux/cache.h>
+#include <linux/cpufreq.h>
+#include <linux/cpumask.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/printk.h>
+#include <linux/string.h>
+#include <linux/spinlock.h>
+
+#include "arm-bl-cpufreq.h"
+
+#define DRIVER_NAME "arm-bl"
+#define MODULE_NAME "arm-bl-cpufreq"
+
+#define info(format...) printk(KERN_INFO MODULE_NAME ": " format)
+#define warn(format...) printk(KERN_WARNING MODULE_NAME ": " format)
+
+/* Dummy frequencies representing the big and little clusters: */
+#define FREQ_BIG	1000000
+#define FREQ_LITTLE	 100000
+
+/*  Cluster numbers */
+#define CLUSTER_BIG	0
+#define CLUSTER_LITTLE	1
+
+static DEFINE_SPINLOCK(switcher_lock);
+
+static struct cpufreq_frequency_table __read_mostly bl_freqs[] = {
+	{ CLUSTER_BIG,		FREQ_BIG		},
+	{ CLUSTER_LITTLE,	FREQ_LITTLE		},
+	{ 0,			CPUFREQ_TABLE_END	},
+};
+
+
+/* Miscellaneous helpers */
+
+static unsigned int cluster_to_freq(int cluster)
+{
+	unsigned int i;
+
+	for(i = 0; bl_freqs[i].frequency != CPUFREQ_TABLE_END; i++)
+		if(bl_freqs[i].index == cluster)
+			return bl_freqs[i].frequency;
+
+	WARN(1, "%s: %s(): invalid cluster number %d, assuming 0\n",
+	     MODULE_NAME, __func__, cluster);
+	return bl_freqs[0].frequency;
+}
+
+/*
+ * Functions to get the current status.
+ *
+ * If you intend to use the result (i.e., it's not just for diagnostic
+ * purposes) then you should be holding switcher_lock ... otherwise
+ * the current cluster may change unexpectedly.
+ */
+static int get_current_cluster(void)
+{
+	return (__arm_bl_get_cluster() >> 8) & 0xF;
+}
+
+static unsigned int get_current_freq(void)
+{
+	return cluster_to_freq(get_current_cluster());
+}
+
+/*
+ * Switch to the requested cluster.
+ * There is no "switch_to_frequency" function, because the cpufreq frequency
+ * table helpers can easily look up the appropriate cluster number for us.
+ */
+static void switch_to_cluster(int cluster)
+{
+	info("Switching to cluster %d\n", cluster);
+
+	spin_lock(&switcher_lock);
+	if(cluster != get_current_cluster())
+		__arm_bl_switch_cluster();
+	spin_unlock(&switcher_lock);
+}
+
+
+/* Cpufreq methods and module code */
+
+static int bl_cpufreq_init(struct cpufreq_policy *policy)
+{
+	int err;
+
+	/*
+	 * Set CPU and policy min and max frequencies based on bl_freqs:
+	 */
+	err = cpufreq_frequency_table_cpuinfo(policy, bl_freqs);
+	if (err)
+		goto error;
+
+	/*
+	 * No need for locking here:
+	 * cpufreq is not active until initialisation has finished.
+	 * Ideally, transition_latency should be calibrated here.
+	 */
+	policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL;
+	policy->cur = get_current_freq();
+
+	/*
+	 * A b.L switch can be triggered from any CPU, but will affect them all.
+	 * The set of related CPUs should perhaps be determined from the
+	 * system CPU topology, rather than just the set of CPUs present...
+	 */
+	policy->shared_type = CPUFREQ_SHARED_TYPE_ANY;
+	cpumask_copy(policy->related_cpus, cpu_present_mask);
+	/*
+	 * We do not set ->cpus here, because it doesn't actually matter if
+	 * we try to switch on two CPUs at the same time.  Setting ->cpus
+	 * to cpu_present_mask might provide a way to avoid the need to take
+	 * switcher_lock when switching, though.
+	 */
+
+	info("cpufreq initialised successfully\n");
+	return 0;
+	
+error:
+	warn("%s: cpufreq initialisation failed (%d)\n", __func__, err);
+	return err;
+}
+
+static int bl_cpufreq_verify(struct cpufreq_policy *policy)
+{
+	return cpufreq_frequency_table_verify(policy, bl_freqs);
+}
+
+static int bl_cpufreq_target(struct cpufreq_policy *policy,
+			     unsigned int target_freq,
+			     unsigned int relation)
+{
+	int err;
+	int index;
+
+	if(cpufreq_frequency_table_target(policy, bl_freqs, target_freq,
+					  relation, &index))
+		return err;
+
+	switch_to_cluster(bl_freqs[index].index);
+	return 0;
+}
+
+static unsigned int bl_cpufreq_get(unsigned int __always_unused cpu)
+{
+	/*
+	 * The cpu argument is ignored, because all CPUs have the same
+	 * performance point, by definition.
+	 */
+	return get_current_freq();
+}
+
+static struct cpufreq_driver __read_mostly bl_cpufreq_driver = {
+	.owner = THIS_MODULE,
+	.name = DRIVER_NAME,
+
+	.init = bl_cpufreq_init,
+	.verify = bl_cpufreq_verify,
+	.target = bl_cpufreq_target,
+	.get = bl_cpufreq_get,
+	/* what else? */
+};	
+
+static int __init bl_cpufreq_module_init(void)
+{
+	int err;
+
+	err = cpufreq_register_driver(&bl_cpufreq_driver);
+	if(err)
+		info("cpufreq backend driver registration failed (%d)\n", err);
+	else
+		info("cpufreq backend driver registered.\n");
+
+	return err;
+}
+module_init(bl_cpufreq_module_init);
+
+static void __exit bl_cpufreq_module_exit(void)
+{
+	cpufreq_unregister_driver(&bl_cpufreq_driver);
+
+	/* Restore the "default" cluster: */
+	switch_to_cluster(CLUSTER_BIG);
+
+	info("cpufreq backend driver unloaded.\n");
+}
+module_exit(bl_cpufreq_module_exit);
+
+
+MODULE_AUTHOR("Dave Martin");
+MODULE_DESCRIPTION("Simple cpufreq interface for the ARM big.LITTLE switcher");
+MODULE_LICENSE("GPL");
diff --git a/drivers/cpufreq/arm-bl-cpufreq_hvc.S b/drivers/cpufreq/arm-bl-cpufreq_hvc.S
new file mode 100644
index 0000000..6c7eb6d
--- /dev/null
+++ b/drivers/cpufreq/arm-bl-cpufreq_hvc.S
@@ -0,0 +1,15 @@
+#include <linux/linkage.h>
+
+#include "arm-bl-cpufreq.h"
+
+.arch_extension virt
+
+ENTRY(__arm_bl_get_cluster)
+	hvc	#ARM_BL_HVC_GET_MPIDR
+	bx	lr
+ENDPROC(__arm_bl_get_cluster)
+
+ENTRY(__arm_bl_switch_cluster)
+	hvc	#ARM_BL_HVC_SWITCH_CLUSTER
+	bx	lr
+ENDPROC(__arm_bl_switch_cluster)
