Added interactive governor and options for Overclocking and Undervolting.
This commit is contained in:
		| @@ -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 | ||||
| ============================================= | ||||
|  | ||||
|   | ||||
| @@ -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 | ||||
|  | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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++; | ||||
| 	} | ||||
|   | ||||
| @@ -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, | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
							
								
								
									
										324
									
								
								drivers/cpufreq/cpufreq_interactive.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										324
									
								
								drivers/cpufreq/cpufreq_interactive.c
									
									
									
									
									
										Normal file
									
								
							| @@ -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 <linux/cpu.h> | ||||
| #include <linux/cpumask.h> | ||||
| #include <linux/cpufreq.h> | ||||
| #include <linux/mutex.h> | ||||
| #include <linux/sched.h> | ||||
| #include <linux/tick.h> | ||||
| #include <linux/timer.h> | ||||
| #include <linux/workqueue.h> | ||||
|  | ||||
| #include <asm/cputime.h> | ||||
|  | ||||
| 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 <mike@android.com>"); | ||||
| MODULE_DESCRIPTION("'cpufreq_interactive' - A cpufreq governor for " | ||||
| 	"Latency sensitive workloads"); | ||||
| MODULE_LICENSE("GPL"); | ||||
|  | ||||
| @@ -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) | ||||
|  | ||||
|   | ||||
| @@ -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 | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -2910,6 +2910,7 @@ unsigned long nr_running(void) | ||||
|  | ||||
| 	return sum; | ||||
| } | ||||
| EXPORT_SYMBOL_GPL(nr_running); | ||||
|  | ||||
| unsigned long nr_uninterruptible(void) | ||||
| { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user