* ACPI P-States <-> cpufreq integration
@ 2002-10-23 20:56 Dominik Brodowski
0 siblings, 0 replies; 2+ messages in thread
From: Dominik Brodowski @ 2002-10-23 20:56 UTC (permalink / raw)
To: acpi-devel-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f
[-- Attachment #1.1: Type: text/plain, Size: 967 bytes --]
Hi,
The attached patch for kernel 2.5.44 (not for any additional later ACPI patch)
integrates ACPI P-States into the CPU frequency and voltage scaling
infrastructure - CPUfreq. It is a complies-only, should-work, non-tested
version (no hardware), so testing this patch is greatly appreciated.
CPUfreq offers one unified interface to various available CPU
frequency scaling methods - ACPI, SpeedStep, PowerNow!, LongRun, ...
Additionally, kernel constants like loops_per_jiffy, cpu_khz are updated;
drivers which need to force a certain speed limit (for example, display
drivers for the ARM architecture; ACPI thermal module might be a future
addition) can register cpufreq "notifiers" to voice their needs whenever
a frequency policy change occurs, or whenever a frequency transition occurs.
For further information about cpufreq, please check
linux/Documentation/cpufreq in recent 2.5. kernels, or
http://www.brodo.de/cpufreq
Dominik
[-- Attachment #1.2: cpufreq-2.5.44-acpi-1 --]
[-- Type: text/plain, Size: 10256 bytes --]
--- linux/drivers/acpi/processor.c.original Wed Oct 23 21:25:16 2002
+++ linux/drivers/acpi/processor.c Wed Oct 23 22:30:49 2002
@@ -40,6 +40,7 @@
#include <asm/delay.h>
#include <linux/compatmac.h>
#include <linux/proc_fs.h>
+#include <linux/cpufreq.h>
#include "acpi_bus.h"
#include "acpi_drivers.h"
@@ -224,6 +225,10 @@
static struct acpi_processor_errata errata;
static void (*pm_idle_save)(void) = NULL;
+static unsigned int cpufreq_usage_count = 0;
+
+static struct cpufreq_driver *acpi_cpufreq_driver;
+
/* --------------------------------------------------------------------------
Errata Handling
@@ -1037,6 +1042,7 @@
u16 port = 0;
u8 value = 0;
int i = 0;
+ struct cpufreq_freqs cpufreq_freqs;
ACPI_FUNCTION_TRACE("acpi_processor_set_performance");
@@ -1068,6 +1074,14 @@
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Transitioning from P%d to P%d\n",
pr->performance.state, state));
+ /* cpufreq frequency struct */
+ cpufreq_freqs.cpu = pr->id;
+ cpufreq_freqs.old = pr->performance.states[pr->performance.state].core_frequency;
+ cpufreq_freqs.new = pr->performance.states[state].core_frequency;
+
+ /* notify cpufreq */
+ cpufreq_notify_transition(&cpufreq_freqs, CPUFREQ_PRECHANGE);
+
/*
* First we write the target state's 'control' value to the
* control_register.
@@ -1101,7 +1115,15 @@
udelay(10);
}
+ /* notify cpufreq */
+ cpufreq_notify_transition(&cpufreq_freqs, CPUFREQ_POSTCHANGE);
+
if (value != pr->performance.states[state].status) {
+ unsigned int tmp = cpufreq_freqs.new;
+ cpufreq_freqs.new = cpufreq_freqs.old;
+ cpufreq_freqs.old = tmp;
+ cpufreq_notify_transition(&cpufreq_freqs, CPUFREQ_PRECHANGE);
+ cpufreq_notify_transition(&cpufreq_freqs, CPUFREQ_POSTCHANGE);
ACPI_DEBUG_PRINT((ACPI_DB_WARN, "Transition failed\n"));
return_VALUE(-ENODEV);
}
@@ -1406,8 +1428,13 @@
px = pr->limit.user.px;
if (pr->limit.thermal.px > px)
px = pr->limit.thermal.px;
-
- result = acpi_processor_set_performance(pr, px);
+ {
+ struct cpufreq_policy policy;
+ policy.cpu = pr->id;
+ cpufreq_get_policy(&policy, pr->id);
+ policy.max = pr->performance.states[px].core_frequency * 1000;
+ result = cpufreq_set_policy(&policy);
+ }
if (result)
goto end;
}
@@ -1562,6 +1589,253 @@
return_VALUE(0);
}
+/* --------------------------------------------------------------------------
+ cpufreq interface
+ -------------------------------------------------------------------------- */
+
+static void
+acpi_cpufreq_setpolicy (
+ struct cpufreq_policy *policy)
+{
+ unsigned int cpu = 0;
+ unsigned int i = 0;
+ struct acpi_processor *pr = NULL;
+ unsigned int next_state = 0;
+ unsigned int result = 0;
+
+ ACPI_FUNCTION_TRACE("acpi_cpufreq_setpolicy");
+
+ if (!policy)
+ return_VOID;
+
+ /* get a present, initialized CPU */
+ if (policy->cpu == CPUFREQ_ALL_CPUS)
+ {
+ for (i=0; i<NR_CPUS; i++) {
+ if (processors[i] != NULL) {
+ cpu = i;
+ pr = processors[cpu];
+ break;
+ }
+ }
+ }
+ else
+ {
+ cpu = policy->cpu;
+ pr = processors[cpu];
+ if (!pr)
+ return_VOID;
+ }
+
+ /* select appropriate P-State */
+ if (policy->policy == CPUFREQ_POLICY_POWERSAVE)
+ {
+ for (i=(pr->performance.state_count - 1); i>= pr->limit.state.px; i--)
+ {
+ unsigned int state_freq = pr->performance.states[i].core_frequency * 1000;
+ if ((policy->min <= state_freq) &&
+ (policy->max >= state_freq))
+ {
+ next_state = i;
+ break;
+ }
+ }
+ } else {
+ for (i=pr->limit.state.px; i < pr->performance.state_count; i++)
+ {
+ unsigned int state_freq = pr->performance.states[i].core_frequency * 1000;
+ if ((policy->min <= state_freq) &&
+ (policy->max >= state_freq))
+ {
+ next_state = i;
+ break;
+ }
+ }
+ }
+
+ /* set one or all CPUs to the new state */
+ if (policy->cpu == CPUFREQ_ALL_CPUS) {
+ for (i=0; i<NR_CPUS; i++)
+ {
+ pr = processors[cpu];
+ if (!pr || !cpu_online(cpu))
+ continue;
+ result = acpi_processor_set_performance (pr, next_state);
+ }
+ } else {
+ result = acpi_processor_set_performance (pr, next_state);
+ }
+
+ return_VOID;
+}
+
+
+static void
+acpi_cpufreq_verify (
+ struct cpufreq_policy *policy)
+{
+ unsigned int cpu = 0;
+ unsigned int i = 0;
+ struct acpi_processor *pr = NULL;
+ unsigned int number_states = 0;
+ unsigned int next_larger_state = 0;
+
+ ACPI_FUNCTION_TRACE("acpi_cpufreq_verify");
+
+ if (!policy)
+ return_VOID;
+
+ /* get a present, initialized CPU */
+ if (policy->cpu == CPUFREQ_ALL_CPUS)
+ {
+ for (i=0; i<NR_CPUS; i++) {
+ if (processors[i] != NULL) {
+ cpu = i;
+ pr = processors[cpu];
+ break;
+ }
+ }
+ }
+ else
+ {
+ cpu = policy->cpu;
+ pr = processors[cpu];
+ if (!pr)
+ return_VOID;
+ }
+
+ /* first check if min and max are within valid limits */
+ cpufreq_verify_within_limits(
+ policy,
+ pr->performance.states[pr->performance.state_count - 1].core_frequency * 1000,
+ pr->performance.states[pr->limit.state.px].core_frequency * 1000);
+
+ /* now check if at least one value is within this limit */
+ for (i=pr->limit.state.px; i < pr->performance.state_count; i++)
+ {
+ unsigned int state_freq = pr->performance.states[i].core_frequency * 1000;
+ if ((policy->min <= state_freq) &&
+ (policy->max >= state_freq))
+ number_states++;
+ if (state_freq > policy->max)
+ next_larger_state = i;
+ }
+
+ if (number_states)
+ return_VOID;
+
+ /* round up now */
+ policy->max = pr->performance.states[next_larger_state].core_frequency * 1000;
+
+ return_VOID;
+}
+
+static int
+acpi_cpufreq_init (
+ struct acpi_processor *pr)
+{
+ int result = 0;
+ int i = 0;
+ int current_state = 0;
+ struct cpufreq_driver *driver;
+
+ ACPI_FUNCTION_TRACE("acpi_cpufreq_init");
+
+ if (cpufreq_usage_count) {
+ if (pr->flags.performance == 1)
+ cpufreq_usage_count++;
+ return_VALUE(0);
+ }
+
+ /* test if it works */
+ current_state = pr->performance.state;
+
+ if (current_state == pr->limit.state.px) {
+ result = acpi_processor_set_performance(pr, (pr->performance.state_count - 1));
+ if (result) {
+ ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Disabled P-States due to failure while switching.\n"));
+ pr->flags.performance = 0;
+ return_VALUE(-ENODEV);
+ }
+ }
+
+ result = acpi_processor_set_performance(pr, pr->limit.state.px);
+ if (result) {
+ ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Disabled P-States due to failure while switching.\n"));
+ pr->flags.performance = 0;
+ return_VALUE(-ENODEV);
+ }
+
+ if (current_state != 0) {
+ result = acpi_processor_set_performance(pr, current_state);
+ if (result) {
+ ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Disabled P-States due to failure while switching.\n"));
+ pr->flags.performance = 0;
+ return_VALUE(-ENODEV);
+ }
+ }
+
+ /* initialization of main "cpufreq" code*/
+ driver = kmalloc(sizeof(struct cpufreq_driver) +
+ NR_CPUS * sizeof(struct cpufreq_policy), GFP_KERNEL);
+ if (!driver)
+ return_VALUE(-ENOMEM);
+
+ driver->policy = (struct cpufreq_policy *) (driver + 1);
+
+#ifdef CONFIG_CPU_FREQ_24_API
+ driver->cpu_min_freq = pr->performance.states[pr->performance.state_count - 1].core_frequency * 1000;
+ for (i=0;i<NR_CPUS;i++)
+ driver->cpu_cur_freq[0] = pr->performance.states[current_state].core_frequency * 1000;
+#endif
+
+ driver->verify = &acpi_cpufreq_verify;
+ driver->setpolicy = &acpi_cpufreq_setpolicy;
+
+ for (i=0;i<NR_CPUS;i++) {
+ driver->policy[i].cpu = pr->id;
+ driver->policy[i].min = pr->performance.states[pr->performance.state_count - 1].core_frequency * 1000;
+ driver->policy[i].max = pr->performance.states[pr->limit.state.px].core_frequency * 1000;
+ driver->policy[i].max_cpu_freq = pr->performance.states[0].core_frequency * 1000;
+ driver->policy[i].policy = ( pr->performance.states[current_state].core_frequency * 1000 == driver->policy[i].max) ?
+ CPUFREQ_POLICY_PERFORMANCE : CPUFREQ_POLICY_POWERSAVE;
+ }
+
+ acpi_cpufreq_driver = driver;
+ result = cpufreq_register(driver);
+ if (result) {
+ kfree(driver);
+ acpi_cpufreq_driver = NULL;
+ return_VALUE(result);
+ }
+
+ cpufreq_usage_count++;
+
+ return_VALUE(0);
+}
+
+static int
+acpi_cpufreq_exit (
+ struct acpi_processor *pr)
+{
+ int result = 0;
+
+ ACPI_FUNCTION_TRACE("acpi_cpufreq_exit");
+
+ if (!pr)
+ return_VALUE(-EINVAL);
+
+ if (pr->flags.performance)
+ cpufreq_usage_count--;
+
+ if (!cpufreq_usage_count) {
+ ACPI_DEBUG_PRINT((ACPI_DB_INFO,
+ "Removing cpufreq driver\n"));
+ result = cpufreq_unregister();
+ }
+
+ return_VALUE(result);
+}
/* --------------------------------------------------------------------------
FS Interface (/proc)
@@ -1750,6 +2024,8 @@
int result = 0;
struct acpi_processor *pr = (struct acpi_processor *) data;
char state_string[12] = {'\0'};
+ unsigned int new_state = 0;
+ struct cpufreq_policy policy;
ACPI_FUNCTION_TRACE("acpi_processor_write_performance");
@@ -1760,9 +2036,14 @@
return_VALUE(-EFAULT);
state_string[count] = '\0';
+ new_state = simple_strtoul(state_string, NULL, 0);
- result = acpi_processor_set_performance(pr,
- simple_strtoul(state_string, NULL, 0));
+ cpufreq_get_policy(&policy, pr->id);
+
+ policy.cpu = pr->id;
+ policy.max = pr->performance.states[new_state].core_frequency * 1000;
+
+ result = cpufreq_set_policy(&policy);
if (result)
return_VALUE(result);
@@ -2139,6 +2420,7 @@
acpi_processor_get_power_info(pr);
acpi_processor_get_performance_info(pr);
+ acpi_cpufreq_init(pr);
acpi_processor_get_throttling_info(pr);
acpi_processor_get_limit_info(pr);
@@ -2288,6 +2570,7 @@
return_VALUE(-ENODEV);
}
+ acpi_cpufreq_exit(pr);
acpi_processor_remove_fs(device);
processors[pr->id] = NULL;
[-- Attachment #2: Type: application/pgp-signature, Size: 240 bytes --]
^ permalink raw reply [flat|nested] 2+ messages in thread* RE: ACPI P-States <-> cpufreq integration
@ 2002-10-23 23:32 Grover, Andrew
0 siblings, 0 replies; 2+ messages in thread
From: Grover, Andrew @ 2002-10-23 23:32 UTC (permalink / raw)
To: 'Dominik Brodowski',
acpi-devel-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f
> From: Dominik Brodowski [mailto:linux-JhLEnvuH02M@public.gmane.org]
> The attached patch for kernel 2.5.44 (not for any additional
> later ACPI patch)
> integrates ACPI P-States into the CPU frequency and voltage scaling
> infrastructure - CPUfreq. It is a complies-only, should-work,
> non-tested
> version (no hardware), so testing this patch is greatly appreciated.
Thanks, this is great.
Can you make it so it compiles if CONFIG_CPU_FREQ is not set?
Thanks again -- Regards -- Andy
-------------------------------------------------------
This sf.net email is sponsored by: Influence the future
of Java(TM) technology. Join the Java Community
Process(SM) (JCP(SM)) program now.
http://ads.sourceforge.net/cgi-bin/redirect.pl?sunm0002en
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2002-10-23 23:32 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2002-10-23 20:56 ACPI P-States <-> cpufreq integration Dominik Brodowski
-- strict thread matches above, loose matches on Subject: below --
2002-10-23 23:32 Grover, Andrew
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox