From 0fdd7cede42f15a7e588fb1ce6616e7e59134ab8 Mon Sep 17 00:00:00 2001 From: Jon Benson Date: Mon, 27 Sep 2010 16:36:50 +1000 Subject: [PATCH] Added interactive governor and options for Overclocking and Undervolting. --- Documentation/cpu-freq/governors.txt | 24 ++ arch/arm/configs/htcleo_defconfig | 10 +- arch/arm/mach-msm/Kconfig | 36 +++ arch/arm/mach-msm/acpuclock-scorpion.c | 116 ++++++++- arch/arm/mach-msm/board-htcleo.c | 4 +- drivers/cpufreq/Kconfig | 15 ++ drivers/cpufreq/Makefile | 1 + drivers/cpufreq/cpufreq_conservative.c | 4 +- drivers/cpufreq/cpufreq_interactive.c | 324 +++++++++++++++++++++++++ drivers/cpufreq/cpufreq_ondemand.c | 6 +- include/linux/cpufreq.h | 3 + kernel/sched.c | 1 + 12 files changed, 533 insertions(+), 11 deletions(-) create mode 100644 drivers/cpufreq/cpufreq_interactive.c diff --git a/Documentation/cpu-freq/governors.txt b/Documentation/cpu-freq/governors.txt index aed082f4..d155c054 100644 --- a/Documentation/cpu-freq/governors.txt +++ b/Documentation/cpu-freq/governors.txt @@ -28,6 +28,7 @@ Contents: 2.3 Userspace 2.4 Ondemand 2.5 Conservative +2.6 Interactive 3. The Governor Interface in the CPUfreq Core @@ -182,6 +183,29 @@ governor but for the opposite direction. For example when set to its default value of '20' it means that if the CPU usage needs to be below 20% between samples to have the frequency decreased. + +2.6 Interactive +--------------- + +The CPUfreq governor "interactive" is designed for low latency, +interactive workloads. This governor sets the CPU speed depending on +usage, similar to "ondemand" and "conservative" governors. However +there is no polling, or 'sample_rate' required to scale the CPU up. + +Sampling CPU load every X ms can lead to under powering the CPU +for X ms, leading to dropped framerate, stuttering UI etc.. + +Scaling the CPU up is done when coming out of idle, and like "ondemand" +scaling up will always go to MAX, then step down based off of cpu load. + +There is only one tuneable value for this governor: + +min_sample_time: The ammount of time the CPU must spend (in uS) +at the current frequency before scaling DOWN. This is done to +more accurately determine the cpu workload and the best speed for that +workload. The default is 50ms. + + 3. The Governor Interface in the CPUfreq Core ============================================= diff --git a/arch/arm/configs/htcleo_defconfig b/arch/arm/configs/htcleo_defconfig index 822586bc..91f3e93f 100644 --- a/arch/arm/configs/htcleo_defconfig +++ b/arch/arm/configs/htcleo_defconfig @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit # Linux kernel version: 2.6.32.15 -# Fri Sep 17 14:11:22 2010 +# Mon Sep 27 15:22:40 2010 # CONFIG_ARM=y CONFIG_SYS_SUPPORTS_APM_EMULATION=y @@ -306,6 +306,12 @@ CONFIG_SMD_OFFSET_TCXO_STAT=0x0 # CONFIG_AXI_SCREEN_POLICY is not set # CONFIG_MSM_NPA is not set CONFIG_PHYS_OFFSET=0x11800000 +CONFIG_HTCLEO_CPU_VOLT=y +# CONFIG_HTCLEO_UNDERVOLT_1000 is not set +# CONFIG_HTCLEO_UNDERVOLT_925 is not set +# CONFIG_HTCLEO_UNDERVOLT_800 is not set +CONFIG_HTCLEO_NO_UNDERVOLT=y +# CONFIG_HTCLEO_OVERCLOCK is not set # CONFIG_HTC_FB_CONSOLE is not set CONFIG_VERY_EARLY_CONSOLE=y CONFIG_HTCLEO_ENABLE_MULTI_TOUCH=y @@ -413,10 +419,12 @@ CONFIG_CPU_FREQ_STAT_DETAILS=y # CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE is not set CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y # CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE is not set CONFIG_CPU_FREQ_GOV_PERFORMANCE=y CONFIG_CPU_FREQ_GOV_POWERSAVE=y CONFIG_CPU_FREQ_GOV_USERSPACE=y CONFIG_CPU_FREQ_GOV_ONDEMAND=y +CONFIG_CPU_FREQ_GOV_INTERACTIVE=y CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y # CONFIG_CPU_IDLE is not set diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig index 5a17cc89..915e44e8 100644 --- a/arch/arm/mach-msm/Kconfig +++ b/arch/arm/mach-msm/Kconfig @@ -1053,6 +1053,42 @@ config PHYS_OFFSET depends on MACH_HTCLEO default "0x11800000" if MACH_HTCLEO +config HTCLEO_CPU_VOLT + bool + default y if HTCLEO_NO_UNDERVOLT + +choice + prompt "The minimum amount of voltage feeds into the CPU is" + + default HTCLEO_NO_UNDERVOLT + + config HTCLEO_UNDERVOLT_1000 + bool "1000 mV" + + config HTCLEO_UNDERVOLT_925 + bool "925 mV" + + config HTCLEO_UNDERVOLT_800 + bool "800 mV - does not work on every device" + + config HTCLEO_NO_UNDERVOLT + bool "DEFAULT - 1050mV" + +endchoice + +config HTCLEO_OVERCLOCK + bool "Support overclock to 1.113GHz" + default n + help + add overclock ability to 1.113GHz. +config HTCLEO_EXOVERCLOCK + bool "Support overclock to 1.305GHz" + depends on HTCLEO_OVERCLOCK + default n + help + add overclock ability to 1.305GHz. + + config HTC_FB_CONSOLE bool "Boot console for HTC phones (needs a font which has width <= 8)" default n diff --git a/arch/arm/mach-msm/acpuclock-scorpion.c b/arch/arm/mach-msm/acpuclock-scorpion.c index 44e9b60c..e87e6439 100644 --- a/arch/arm/mach-msm/acpuclock-scorpion.c +++ b/arch/arm/mach-msm/acpuclock-scorpion.c @@ -73,6 +73,75 @@ struct clkctl_acpu_speed { #define SRC_PLL1 3 /* 768 MHz */ struct clkctl_acpu_speed acpu_freq_tbl[] = { +#ifdef CONFIG_HTCLEO_UNDERVOLT_1000 + { 19200, CCTL(CLK_TCXO, 1), SRC_RAW, 0, 0, 1000, 14000 }, + { 128000, CCTL(CLK_TCXO, 1), SRC_AXI, 0, 0, 1000, 14000 }, + { 245000, CCTL(CLK_MODEM_PLL, 1), SRC_RAW, 0, 0, 1000, 29000 }, + //{ 256000, CCTL(CLK_GLOBAL_PLL, 3), SRC_RAW, 0, 0, 1000, 29000 }, + { 384000, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x0A, 0, 1000, 58000 }, + { 422400, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x0B, 0, 1000, 117000 }, + { 460800, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x0C, 0, 1000, 117000 }, + { 499200, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x0D, 0, 1025, 117000 }, + { 537600, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x0E, 0, 1050, 117000 }, + { 576000, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x0F, 0, 1050, 117000 }, + { 614400, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x10, 0, 1075, 117000 }, + { 652800, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x11, 0, 1100, 117000 }, + { 691200, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x12, 0, 1125, 117000 }, + { 729600, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x13, 0, 1150, 117000 }, + { 768000, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x14, 0, 1150, 128000 }, + { 806400, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x15, 0, 1175, 128000 }, + { 844800, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x16, 0, 1200, 128000 }, + { 883200, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x17, 0, 1200, 128000 }, + { 921600, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x18, 0, 1225, 128000 }, + { 960000, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x19, 0, 1225, 128000 }, + { 998400, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x1A, 0, 1225, 128000 }, +#elif CONFIG_HTCLEO_UNDERVOLT_925 + // should work with most of HD2s + { 19200, CCTL(CLK_TCXO, 1), SRC_RAW, 0, 0, 925, 14000 }, + { 128000, CCTL(CLK_TCXO, 1), SRC_AXI, 0, 0, 925, 14000 }, + { 245000, CCTL(CLK_MODEM_PLL, 1), SRC_RAW, 0, 0, 925, 29000 }, + //{ 256000, CCTL(CLK_GLOBAL_PLL, 3), SRC_RAW, 0, 0, 925, 29000 }, + { 384000, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x0A, 0, 950, 58000 }, + { 422400, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x0B, 0, 975, 117000 }, + { 460800, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x0C, 0, 1000, 117000 }, + { 499200, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x0D, 0, 1025, 117000 }, + { 537600, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x0E, 0, 1050, 117000 }, + { 576000, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x0F, 0, 1050, 117000 }, + { 614400, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x10, 0, 1075, 117000 }, + { 652800, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x11, 0, 1100, 117000 }, + { 691200, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x12, 0, 1125, 117000 }, + { 729600, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x13, 0, 1150, 117000 }, + { 768000, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x14, 0, 1150, 128000 }, + { 806400, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x15, 0, 1175, 128000 }, + { 844800, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x16, 0, 1200, 128000 }, + { 883200, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x17, 0, 1200, 128000 }, + { 921600, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x18, 0, 1225, 128000 }, + { 960000, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x19, 0, 1225, 128000 }, + { 998400, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x1A, 0, 1225, 128000 }, +#elif CONFIG_HTCLEO_UNDERVOLT_800 + // not working yet + { 19200, CCTL(CLK_TCXO, 1), SRC_RAW, 0, 0, 850, 14000 }, + { 128000, CCTL(CLK_TCXO, 1), SRC_AXI, 0, 0, 850, 14000 }, + { 245000, CCTL(CLK_MODEM_PLL, 1), SRC_RAW, 0, 0, 850, 29000 }, + //{ 256000, CCTL(CLK_GLOBAL_PLL, 3), SRC_RAW, 0, 0, 850, 29000 }, + { 384000, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x0A, 0, 850, 58000 }, + { 422400, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x0B, 0, 875, 117000 }, + { 460800, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x0C, 0, 900, 117000 }, + { 499200, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x0D, 0, 925, 117000 }, + { 537600, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x0E, 0, 950, 117000 }, + { 576000, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x0F, 0, 950, 117000 }, + { 614400, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x10, 0, 975, 117000 }, + { 652800, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x11, 0, 1000, 117000 }, + { 691200, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x12, 0, 1025, 117000 }, + { 729600, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x13, 0, 1050, 117000 }, + { 768000, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x14, 0, 1125, 128000 }, + { 806400, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x15, 0, 1125, 128000 }, + { 844800, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x16, 0, 1150, 128000 }, + { 883200, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x17, 0, 1150, 128000 }, + { 921600, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x18, 0, 1175, 128000 }, + { 960000, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x19, 0, 1175, 128000 }, + { 998400, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x1A, 0, 1200, 128000 }, +#else { 19200, CCTL(CLK_TCXO, 1), SRC_RAW, 0, 0, 1050, 14000}, { 128000, CCTL(CLK_TCXO, 1), SRC_AXI, 0, 0, 1050, 14000 }, { 245000, CCTL(CLK_MODEM_PLL, 1), SRC_RAW, 0, 0, 1050, 29000 }, @@ -95,6 +164,30 @@ struct clkctl_acpu_speed acpu_freq_tbl[] = { { 921600, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x18, 0, 1300, 128000 }, { 960000, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x19, 0, 1300, 128000 }, { 998400, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x1A, 0, 1300, 128000 }, +#endif +#ifdef CONFIG_HTCLEO_OVERCLOCK +#ifdef CONFIG_HTCLEO_UNDERVOLT_1000 + { 1036800, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x1B, 0, 1225, 128000 }, + { 1075200, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x1C, 0, 1250, 128000 }, + { 1113600, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x1D, 0, 1275, 128000 }, +#elif CONFIG_HTCLEO_UNDERVOLT_925 + { 1036800, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x1B, 0, 1225, 128000 }, + { 1075200, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x1C, 0, 1250, 128000 }, + { 1113600, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x1D, 0, 1275, 128000 }, +#elif CONFIG_HTCLEO_UNDERVOLT_800 + { 1036800, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x1B, 0, 1225, 128000 }, + { 1075200, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x1C, 0, 1250, 128000 }, + { 1113600, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x1D, 0, 1275, 128000 }, +#else + { 1036800, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x1B, 0, 1300, 128000 }, + { 1075200, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x1C, 0, 1300, 128000 }, + { 1113600, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x1D, 0, 1300, 128000 }, +#endif +#endif +#ifdef CONFIG_HTCLEO_EXOVERCLOCK + { 1152000, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x1E, 0, 1300, 128000 }, + { 1190400, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x1F, 0, 1300, 128000 }, +#endif { 0 }, }; @@ -120,9 +213,10 @@ static void __init acpuclk_init_cpufreq_table(void) freq_table[i].index = i; freq_table[i].frequency = CPUFREQ_ENTRY_INVALID; - /* Skip speeds using the global pll */ - if (acpu_freq_tbl[i].acpu_khz == 256000 || - acpu_freq_tbl[i].acpu_khz == 19200) + /* Skip speeds we don't want */ + if ( acpu_freq_tbl[i].acpu_khz == 19200 || + acpu_freq_tbl[i].acpu_khz == 128000 || + acpu_freq_tbl[i].acpu_khz == 256000) continue; vdd = acpu_freq_tbl[i].vdd; @@ -411,6 +505,8 @@ static int pll_request(unsigned id, unsigned on) #define CT_CSR_PHYS 0xA8700000 #define TCSR_SPARE2_ADDR (ct_csr_base + 0x60) +/* hastarin - This appears to remove the extra frequencies from the table + so only the highest frequency per VDD is available */ void __init acpu_freq_tbl_fixup(void) { void __iomem *ct_csr_base; @@ -436,6 +532,9 @@ void __init acpu_freq_tbl_fixup(void) case 0x30: case 0x00: max_acpu_khz = 998400; +#ifdef CONFIG_HTCLEO_OVERCLOCK + max_acpu_khz = 1113600; +#endif break; case 0x10: max_acpu_khz = 1267200; @@ -485,11 +584,22 @@ static void __init acpuclk_init(void) */ speed = acpu_freq_tbl; for (;;) { +#ifdef CONFIG_HTCLEO_OVERCLOCK + if (speed->acpu_khz == 883200) + break; +#else if (speed->acpu_khz == 768000) break; +#endif +#ifdef CONFIG_HTCLEO_OVERCLOCK + if (speed->acpu_khz == 0) { + pr_err("acpuclk_init: cannot find 883.2MHz\n"); + BUG(); +#else if (speed->acpu_khz == 0) { pr_err("acpuclk_init: cannot find 768MHz\n"); BUG(); +#endif } speed++; } diff --git a/arch/arm/mach-msm/board-htcleo.c b/arch/arm/mach-msm/board-htcleo.c index b6522715..70e2e8d9 100644 --- a/arch/arm/mach-msm/board-htcleo.c +++ b/arch/arm/mach-msm/board-htcleo.c @@ -110,8 +110,8 @@ static struct regulator_init_data tps65023_data[5] = .constraints = { .name = "dcdc1", /* VREG_MSMC2_1V29 */ - .min_uV = 1000000, - .max_uV = 1300000, + .min_uV = 800000, + .max_uV = 1350000, .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE, }, .consumer_supplies = tps65023_dcdc1_supplies, diff --git a/drivers/cpufreq/Kconfig b/drivers/cpufreq/Kconfig index a8c8d9c1..ca7b0887 100644 --- a/drivers/cpufreq/Kconfig +++ b/drivers/cpufreq/Kconfig @@ -110,6 +110,14 @@ config CPU_FREQ_DEFAULT_GOV_CONSERVATIVE Be aware that not all cpufreq drivers support the conservative governor. If unsure have a look at the help section of the driver. Fallback governor will be the performance governor. + +config CPU_FREQ_DEFAULT_GOV_INTERACTIVE + bool "interactive" + select CPU_FREQ_GOV_INTERACTIVE + help + Use the 'interactive' governor as default. This gets full cpu frequency + scaling for workloads that are latency sensitive, typically interactive + workloads. endchoice config CPU_FREQ_GOV_PERFORMANCE @@ -167,6 +175,13 @@ config CPU_FREQ_GOV_ONDEMAND If in doubt, say N. +config CPU_FREQ_GOV_INTERACTIVE + tristate "'interactive' cpufreq governor" + help + 'interactive' - This driver adds a dynamic cpufreq policy governor. + Designed for low latency burst workloads. Sclaing is done when + coming out idle instead of polling. + config CPU_FREQ_GOV_CONSERVATIVE tristate "'conservative' cpufreq governor" depends on CPU_FREQ diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile index 71fc3b41..30629f7d 100644 --- a/drivers/cpufreq/Makefile +++ b/drivers/cpufreq/Makefile @@ -9,6 +9,7 @@ obj-$(CONFIG_CPU_FREQ_GOV_POWERSAVE) += cpufreq_powersave.o obj-$(CONFIG_CPU_FREQ_GOV_USERSPACE) += cpufreq_userspace.o obj-$(CONFIG_CPU_FREQ_GOV_ONDEMAND) += cpufreq_ondemand.o obj-$(CONFIG_CPU_FREQ_GOV_CONSERVATIVE) += cpufreq_conservative.o +obj-$(CONFIG_CPU_FREQ_GOV_INTERACTIVE) += cpufreq_interactive.o # CPUfreq cross-arch helpers obj-$(CONFIG_CPU_FREQ_TABLE) += freq_table.o diff --git a/drivers/cpufreq/cpufreq_conservative.c b/drivers/cpufreq/cpufreq_conservative.c index c7b081b8..bd3825df 100644 --- a/drivers/cpufreq/cpufreq_conservative.c +++ b/drivers/cpufreq/cpufreq_conservative.c @@ -29,8 +29,8 @@ * It helps to keep variable names smaller, simpler */ -#define DEF_FREQUENCY_UP_THRESHOLD (80) -#define DEF_FREQUENCY_DOWN_THRESHOLD (20) +#define DEF_FREQUENCY_UP_THRESHOLD (65) +#define DEF_FREQUENCY_DOWN_THRESHOLD (30) /* * The polling frequency of this governor depends on the capability of diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c new file mode 100644 index 00000000..36859a78 --- /dev/null +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -0,0 +1,324 @@ +/* + * drivers/cpufreq/cpufreq_interactive.c + * + * Copyright (C) 2010 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + * Author: Mike Chan (mike@android.com) + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +static void (*pm_idle_old)(void); +static atomic_t active_count = ATOMIC_INIT(0); + +static DEFINE_PER_CPU(struct timer_list, cpu_timer); + +static DEFINE_PER_CPU(u64, time_in_idle); +static DEFINE_PER_CPU(u64, idle_exit_time); + +static struct cpufreq_policy *policy; +static unsigned int target_freq; + +/* Workqueues handle frequency scaling */ +static struct workqueue_struct *up_wq; +static struct workqueue_struct *down_wq; +static struct work_struct freq_scale_work; + +static u64 freq_change_time; +static u64 freq_change_time_in_idle; + +static cpumask_t work_cpumask; + +/* + * The minimum ammount of time to spend at a frequency before we can ramp down, + * default is 50ms. + */ +#define DEFAULT_MIN_SAMPLE_TIME 50000; +static unsigned long min_sample_time; + +static int cpufreq_governor_interactive(struct cpufreq_policy *policy, + unsigned int event); + +#ifndef CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE +static +#endif +struct cpufreq_governor cpufreq_gov_interactive = { + .name = "interactive", + .governor = cpufreq_governor_interactive, + .max_transition_latency = 10000000, + .owner = THIS_MODULE, +}; + +static void cpufreq_interactive_timer(unsigned long data) +{ + u64 delta_idle; + u64 update_time; + u64 *cpu_time_in_idle; + u64 *cpu_idle_exit_time; + struct timer_list *t; + + u64 now_idle = get_cpu_idle_time_us(data, + &update_time); + + + cpu_time_in_idle = &per_cpu(time_in_idle, data); + cpu_idle_exit_time = &per_cpu(idle_exit_time, data); + + if (update_time == *cpu_idle_exit_time) + return; + + delta_idle = cputime64_sub(now_idle, *cpu_time_in_idle); + + /* Scale up if there were no idle cycles since coming out of idle */ + if (delta_idle == 0) { + if (policy->cur == policy->max) + return; + + if (nr_running() < 1) + return; + + target_freq = policy->max; + cpumask_set_cpu(data, &work_cpumask); + queue_work(up_wq, &freq_scale_work); + return; + } + + /* + * There is a window where if the cpu utlization can go from low to high + * between the timer expiring, delta_idle will be > 0 and the cpu will + * be 100% busy, preventing idle from running, and this timer from + * firing. So setup another timer to fire to check cpu utlization. + * Do not setup the timer if there is no scheduled work. + */ + t = &per_cpu(cpu_timer, data); + if (!timer_pending(t) && nr_running() > 0) { + *cpu_time_in_idle = get_cpu_idle_time_us( + data, cpu_idle_exit_time); + mod_timer(t, jiffies + 2); + } + + if (policy->cur == policy->min) + return; + + /* + * Do not scale down unless we have been at this frequency for the + * minimum sample time. + */ + if (cputime64_sub(update_time, freq_change_time) < min_sample_time) + return; + + target_freq = policy->min; + cpumask_set_cpu(data, &work_cpumask); + queue_work(down_wq, &freq_scale_work); +} + +static void cpufreq_idle(void) +{ + struct timer_list *t; + u64 *cpu_time_in_idle; + u64 *cpu_idle_exit_time; + + pm_idle_old(); + + if (!cpumask_test_cpu(smp_processor_id(), policy->cpus)) + return; + + /* Timer to fire in 1-2 ticks, jiffie aligned. */ + t = &per_cpu(cpu_timer, smp_processor_id()); + cpu_idle_exit_time = &per_cpu(idle_exit_time, smp_processor_id()); + cpu_time_in_idle = &per_cpu(time_in_idle, smp_processor_id()); + + if (timer_pending(t) == 0) { + *cpu_time_in_idle = get_cpu_idle_time_us( + smp_processor_id(), cpu_idle_exit_time); + mod_timer(t, jiffies + 2); + } +} + +/* + * Choose the cpu frequency based off the load. For now choose the minimum + * frequency that will satisfy the load, which is not always the lower power. + */ +static unsigned int cpufreq_interactive_calc_freq(unsigned int cpu) +{ + unsigned int delta_time; + unsigned int idle_time; + unsigned int cpu_load; + u64 current_wall_time; + u64 current_idle_time;; + + current_idle_time = get_cpu_idle_time_us(cpu, ¤t_wall_time); + + idle_time = (unsigned int) current_idle_time - freq_change_time_in_idle; + delta_time = (unsigned int) current_wall_time - freq_change_time; + + cpu_load = 100 * (delta_time - idle_time) / delta_time; + + return policy->cur * cpu_load / 100; +} + + +/* We use the same work function to sale up and down */ +static void cpufreq_interactive_freq_change_time_work(struct work_struct *work) +{ + unsigned int cpu; + cpumask_t *tmp_mask = &work_cpumask; + for_each_cpu(cpu, tmp_mask) { + if (target_freq == policy->max) { + if (nr_running() == 1) { + cpumask_clear_cpu(cpu, &work_cpumask); + return; + } + + __cpufreq_driver_target(policy, target_freq, + CPUFREQ_RELATION_H); + } else { + target_freq = cpufreq_interactive_calc_freq(cpu); + __cpufreq_driver_target(policy, target_freq, + CPUFREQ_RELATION_L); + } + freq_change_time_in_idle = get_cpu_idle_time_us(cpu, + &freq_change_time); + + cpumask_clear_cpu(cpu, &work_cpumask); + } + + +} + +static ssize_t show_min_sample_time(struct kobject *kobj, + struct attribute *attr, char *buf) +{ + return sprintf(buf, "%lu\n", min_sample_time); +} + +static ssize_t store_min_sample_time(struct kobject *kobj, + struct attribute *attr, const char *buf, size_t count) +{ + return strict_strtoul(buf, 0, &min_sample_time); +} + +static struct global_attr min_sample_time_attr = __ATTR(min_sample_time, 0644, + show_min_sample_time, store_min_sample_time); + +static struct attribute *interactive_attributes[] = { + &min_sample_time_attr.attr, + NULL, +}; + +static struct attribute_group interactive_attr_group = { + .attrs = interactive_attributes, + .name = "interactive", +}; + +static int cpufreq_governor_interactive(struct cpufreq_policy *new_policy, + unsigned int event) +{ + int rc; + switch (event) { + case CPUFREQ_GOV_START: + if (!cpu_online(new_policy->cpu)) + return -EINVAL; + + /* + * Do not register the idle hook and create sysfs + * entries if we have already done so. + */ + if (atomic_inc_return(&active_count) > 1) + return 0; + + rc = sysfs_create_group(cpufreq_global_kobject, + &interactive_attr_group); + if (rc) + return rc; + + pm_idle_old = pm_idle; + pm_idle = cpufreq_idle; + policy = new_policy; + break; + + case CPUFREQ_GOV_STOP: + if (atomic_dec_return(&active_count) > 1) + return 0; + + sysfs_remove_group(cpufreq_global_kobject, + &interactive_attr_group); + + pm_idle = pm_idle_old; + del_timer(&per_cpu(cpu_timer, new_policy->cpu)); + break; + + case CPUFREQ_GOV_LIMITS: + if (new_policy->max < new_policy->cur) + __cpufreq_driver_target(new_policy, + new_policy->max, CPUFREQ_RELATION_H); + else if (new_policy->min > new_policy->cur) + __cpufreq_driver_target(new_policy, + new_policy->min, CPUFREQ_RELATION_L); + break; + } + return 0; +} + +static int __init cpufreq_interactive_init(void) +{ + unsigned int i; + struct timer_list *t; + min_sample_time = DEFAULT_MIN_SAMPLE_TIME; + + /* Initalize per-cpu timers */ + for_each_possible_cpu(i) { + t = &per_cpu(cpu_timer, i); + init_timer_deferrable(t); + t->function = cpufreq_interactive_timer; + t->data = i; + } + + /* Scale up is high priority */ + up_wq = create_rt_workqueue("kinteractive_up"); + down_wq = create_workqueue("knteractive_down"); + + INIT_WORK(&freq_scale_work, cpufreq_interactive_freq_change_time_work); + + return cpufreq_register_governor(&cpufreq_gov_interactive); +} + +#ifdef CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE +pure_initcall(cpufreq_interactive_init); +#else +module_init(cpufreq_interactive_init); +#endif + +static void __exit cpufreq_interactive_exit(void) +{ + cpufreq_unregister_governor(&cpufreq_gov_interactive); + destroy_workqueue(up_wq); + destroy_workqueue(down_wq); +} + +module_exit(cpufreq_interactive_exit); + +MODULE_AUTHOR("Mike Chan "); +MODULE_DESCRIPTION("'cpufreq_interactive' - A cpufreq governor for " + "Latency sensitive workloads"); +MODULE_LICENSE("GPL"); + diff --git a/drivers/cpufreq/cpufreq_ondemand.c b/drivers/cpufreq/cpufreq_ondemand.c index 26e4759a..6ee17cb0 100644 --- a/drivers/cpufreq/cpufreq_ondemand.c +++ b/drivers/cpufreq/cpufreq_ondemand.c @@ -30,11 +30,11 @@ * It helps to keep variable names smaller, simpler */ -#define DEF_FREQUENCY_DOWN_DIFFERENTIAL (10) -#define DEF_FREQUENCY_UP_THRESHOLD (80) +#define DEF_FREQUENCY_DOWN_DIFFERENTIAL (30) +#define DEF_FREQUENCY_UP_THRESHOLD (65) #define MICRO_FREQUENCY_DOWN_DIFFERENTIAL (3) #define MICRO_FREQUENCY_UP_THRESHOLD (95) -#define MICRO_FREQUENCY_MIN_SAMPLE_RATE (10000) +#define MICRO_FREQUENCY_MIN_SAMPLE_RATE (9500) #define MIN_FREQUENCY_UP_THRESHOLD (11) #define MAX_FREQUENCY_UP_THRESHOLD (100) diff --git a/include/linux/cpufreq.h b/include/linux/cpufreq.h index 79a2340d..61dca28f 100644 --- a/include/linux/cpufreq.h +++ b/include/linux/cpufreq.h @@ -338,6 +338,9 @@ extern struct cpufreq_governor cpufreq_gov_ondemand; #elif defined(CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE) extern struct cpufreq_governor cpufreq_gov_conservative; #define CPUFREQ_DEFAULT_GOVERNOR (&cpufreq_gov_conservative) +#elif defined(CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE) +extern struct cpufreq_governor cpufreq_gov_interactive; +#define CPUFREQ_DEFAULT_GOVERNOR (&cpufreq_gov_interactive) #endif diff --git a/kernel/sched.c b/kernel/sched.c index f03aff63..a7ee0eb5 100644 --- a/kernel/sched.c +++ b/kernel/sched.c @@ -2910,6 +2910,7 @@ unsigned long nr_running(void) return sum; } +EXPORT_SYMBOL_GPL(nr_running); unsigned long nr_uninterruptible(void) {