From patchwork Thu Mar 29 13:15:33 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dave Martin X-Patchwork-Id: 7519 Return-Path: X-Original-To: patchwork@peony.canonical.com Delivered-To: patchwork@peony.canonical.com Received: from fiordland.canonical.com (fiordland.canonical.com [91.189.94.145]) by peony.canonical.com (Postfix) with ESMTP id CBBA923E29 for ; Thu, 29 Mar 2012 13:15:50 +0000 (UTC) Received: from mail-yx0-f180.google.com (mail-yx0-f180.google.com [209.85.213.180]) by fiordland.canonical.com (Postfix) with ESMTP id 85A7CA185EA for ; Thu, 29 Mar 2012 13:15:50 +0000 (UTC) Received: by yenl4 with SMTP id l4so1721991yen.11 for ; Thu, 29 Mar 2012 06:15:50 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20120113; h=x-forwarded-to:x-forwarded-for:delivered-to:received-spf:from:to:cc :subject:date:message-id:x-mailer:in-reply-to:references :x-gm-message-state; bh=OyVbRfk2TU4RRmGv/i2OKdXapTVVi9Bn1zBb7tHOosE=; b=Fw7+eTYmO3o0BSpJSHZ/4Ud5l/KqmnL/Nr3lZaBqQSQEnG5lEyphq2qY9fG7deHGM8 8Vtc3DPghbNE/pew8mZ9Xv94XqRqEQLJc93NoaMYUK1EDPqUA683Aqb0ZcQ1czXUX6fi 1DktwHoyiytcfRTZDQ5cu8VIATAm+88sLt5wL+vvhbYO+pUBNqi6r4XBNz+5Z4X04tHT 1SRsMxgo61GOTPIysKiSAeZLANZH5/pUmZAiuwXgyrUSiG8ZGAx2YnDZotQoOf504Epx mZzzRefCanjmG1C20EkLkwOtYgmRkTqto+u9XkzEn93/83zO6lzLwoGXvU1zmIV1MbKD Khyw== Received: by 10.50.191.233 with SMTP id hb9mr1431349igc.44.1333026949858; Thu, 29 Mar 2012 06:15:49 -0700 (PDT) X-Forwarded-To: linaro-patchwork@canonical.com X-Forwarded-For: patch@linaro.org linaro-patchwork@canonical.com Delivered-To: patches@linaro.org Received: by 10.231.5.205 with SMTP id 13csp30185ibw; Thu, 29 Mar 2012 06:15:49 -0700 (PDT) Received: by 10.204.9.205 with SMTP id m13mr13670067bkm.68.1333026947732; Thu, 29 Mar 2012 06:15:47 -0700 (PDT) Received: from mail-bk0-f50.google.com (mail-bk0-f50.google.com [209.85.214.50]) by mx.google.com with ESMTPS id il18si3406385bkc.128.2012.03.29.06.15.47 (version=TLSv1/SSLv3 cipher=OTHER); Thu, 29 Mar 2012 06:15:47 -0700 (PDT) Received-SPF: neutral (google.com: 209.85.214.50 is neither permitted nor denied by best guess record for domain of dave.martin@linaro.org) client-ip=209.85.214.50; Authentication-Results: mx.google.com; spf=neutral (google.com: 209.85.214.50 is neither permitted nor denied by best guess record for domain of dave.martin@linaro.org) smtp.mail=dave.martin@linaro.org Received: by mail-bk0-f50.google.com with SMTP id w11so2812908bku.37 for ; Thu, 29 Mar 2012 06:15:47 -0700 (PDT) Received: by 10.205.130.1 with SMTP id hk1mr14197103bkc.51.1333026947169; Thu, 29 Mar 2012 06:15:47 -0700 (PDT) Received: from e103592.peterhouse.linaro.org (fw-lnat.cambridge.arm.com. [217.140.96.63]) by mx.google.com with ESMTPS id z14sm10772032bky.15.2012.03.29.06.15.45 (version=SSLv3 cipher=OTHER); Thu, 29 Mar 2012 06:15:46 -0700 (PDT) From: Dave Martin To: linaro-big-little@lists.linaro.org Cc: patches@linaro.org, Chris Redpath , Morten Rasmussen , Robin Randhawa , Dave Martin Subject: [PATCH v3 1/2] cpufreq/arm-bl-cpufreq: Add simple cpufreq big.LITTLE switcher frontend Date: Thu, 29 Mar 2012 14:15:33 +0100 Message-Id: <1333026934-21429-2-git-send-email-dave.martin@linaro.org> X-Mailer: git-send-email 1.7.4.1 In-Reply-To: <1333026934-21429-1-git-send-email-dave.martin@linaro.org> References: <1333026934-21429-1-git-send-email-dave.martin@linaro.org> X-Gm-Message-State: ALoCoQlP9tIGiGQzta0vKtllegiqeIQxa32SgqBlOhJKZKgoYkg8AtQTH/6S6bCYoTdt//3z/zuM This patch adds a very simple cpufreq-based frontend to the ARM big.LITTLE switcher. This driver simply simulates two performance points corresponding to the big and little clusters. Note that this driver requires the ARM switcher implementation to be loaded in order to work. The switcher must have been built with ASYNC = FALSE in big-little/Makefile in order for the synchronous switching interface to be exposed. Bugs and limitations: * Switching twice in quick succession currently tends to cause a deadlock inside the switcher. I still need to identify exactly what is going wrong here. * There is currently no tracing interface, and no interface for reporting what the dummy frequencies exposed by the driver actually mean in terms of real cluster / performance point combinations. For the very simple case supported, cpuinfo_max_freq corresponds to big and cpuinfo_min_freq corresponds to LITTLE. * cpufreq will trigger spurious extra cluster switches. This is a "feature" since I didn't tell cpufreq that this matters, but it may not be desirable. Setting policy->cpus can probably fix this. Signed-off-by: Dave Martin --- Documentation/cpu-freq/cpufreq-arm-bl.txt | 47 ++++++ arch/arm/Kconfig | 1 + drivers/cpufreq/Kconfig.arm | 18 +++ drivers/cpufreq/Makefile | 4 + drivers/cpufreq/arm-bl-cpufreq.h | 13 ++ drivers/cpufreq/arm-bl-cpufreq_driver.c | 216 +++++++++++++++++++++++++++++ drivers/cpufreq/arm-bl-cpufreq_hvc.S | 15 ++ 7 files changed, 314 insertions(+), 0 deletions(-) create mode 100644 Documentation/cpu-freq/cpufreq-arm-bl.txt create mode 100644 drivers/cpufreq/arm-bl-cpufreq.h create mode 100644 drivers/cpufreq/arm-bl-cpufreq_driver.c create mode 100644 drivers/cpufreq/arm-bl-cpufreq_hvc.S 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..ffcd03b --- /dev/null +++ b/drivers/cpufreq/arm-bl-cpufreq_driver.c @@ -0,0 +1,216 @@ +/* + * 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 */ + +#define MODULE_NAME "arm-bl-cpufreq" +#define pr_fmt(fmt) MODULE_NAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "arm-bl-cpufreq.h" + +/* 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, pr_fmt("%s(): invalid cluster number %d, assuming 0\n"), + __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) +{ + pr_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. + */ + + pr_info("cpufreq initialised successfully\n"); + return 0; + +error: + pr_warning("cpufreq initialisation failed (%d)\n", 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; + + err = cpufreq_frequency_table_target(policy, bl_freqs, target_freq, + relation, &index); + if(err) + 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 = MODULE_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) + pr_info("cpufreq backend driver registration failed (%d)\n", + err); + else + pr_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); + + pr_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 + +#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)