GIT 46426df6bec41d045b27f3a4a2a4f6509c0eac78 git+ssh://master.kernel.org/pub/scm/linux/kernel/git/lenb/linux-acpi-2.6.git#test commit eaca2d3f6c4de9d4274a4e2be54c9693e76b0303 Author: Len Brown Date: Mon Apr 30 23:27:43 2007 -0400 ACPI: delete un-reliable concept of cooling mode The scheme where the thermal driver displayed the cooling mode /proc/acpi/thermal_zone/*/cooling_mode was flawed in two ways. First, the success of _SCP doesn't actually mean that the BIOS moved any trip points. On many BIOS, _SCP is present, but does nothing. So displaying what _SCP executed actually was wrong more times than it was right. Second, examining the relative position of the trip points when the thermal_zone is added is insufficient -- as the BIOS reserves the right to change the trip points at run-time. The only reliable way for the user to determine if the thermal zone is in active, passive, or critical mode is to examine the relative position of the trip points. The user can do this without the kernel doing it for them by looking in /proc/acpi/thermal_zone/*/trip_points New contents for /proc/acpi/thermal_zone/*/cooling_mode: If _SCP available: "0 - Active; 1 - Passive\n" If _SCP unavailable: "\n" Signed-off-by: Len Brown commit 11ccc0f249cb01a129f54760b8ff087f242935d4 Author: Len Brown Date: Mon Apr 30 22:36:01 2007 -0400 ACPI: thermal trip points are read-only /proc/acpi/thermal_zone/*/trip_points displays what the kernel reads from the BIOS via ACPI. If you echo a string of ':' deliminted numbers to this file then it will change what it displays. But it shouldn't, since the kernel has no way to communicate these changes to ACPI thermal zones. ACPI thermal zone trip points are read-only. The kernel does have the opportunity to ask the BIOS to change the trip points with _SCP - Set Cooling Policy. Request Active Cooling Mode: # echo 0 > /proc/acpi/thermal_zone/*/cooling_policy Request Passive Cooling Mode: # echo 1 > /proc/acpi/thermal_zone/*/cooling_policy However, in practice it is quite rare for the BIOS to support the optional _SCP, and it is even more rare for the BIOS to export an _SCP that actually changes the trip points. Signed-off-by: Len Brown commit e1d4b76cbe138f7c93a051010362d0f9add57566 Author: Venki Pallipadi Date: Thu Apr 26 00:03:59 2007 -0700 cpuidle: hang fix Prevent hang on x86-64, when ACPI processor driver is added as a module on a system that does not support C-states. x86-64 expects all idle handlers to enable interrupts before returning from idle handler. This is due to enter_idle(), exit_idle() races. Make cpuidle_idle_call() confirm to this when there is no pm_idle_old. Also, cpuidle look at the return values of attch_driver() and set current_driver to NULL if attach fails on all CPUs. Signed-off-by: Venkatesh Pallipadi Signed-off-by: Andrew Morton Signed-off-by: Len Brown commit ff0e02b9a13936484eea6a713e99df0204d99732 Author: Shaohua Li Date: Thu Apr 26 10:40:09 2007 +0800 cpuidle: add support for max_cstate limit With CPUIDLE framework, the max_cstate (to limit max cpu c-state) parameter is ingored. Some systems require it to ignore C2/C3 and some drivers like ipw require it too. Signed-off-by: Shaohua Li Signed-off-by: Len Brown commit 4d91bed5b98905d0399466e6500385c7c9aa4675 Author: Shaohua Li Date: Thu Apr 26 10:40:13 2007 +0800 cpuidle: add cpuidle_fore_redetect_devices API add cpuidle_force_redetect_devices API, which forces all CPU redetect idle states. Next patch will use it. Signed-off-by: Shaohua Li Signed-off-by: Len Brown commit 55705c005660bf8c5e83359f3d1ef009702af0da Author: Shaohua Li Date: Thu Apr 26 10:40:01 2007 +0800 cpuidle: fix sysfs related issue Fix the cpuidle sysfs issue. a. make kobject dynamicaly allocated b. fixed sysfs init issue to avoid suspend/resume issue Signed-off-by: Shaohua Li Signed-off-by: Len Brown commit 6eedeef73e7fff32eb5fa25178c3c77b1db0ec0f Author: Vladimir Lebedev Date: Sat Apr 21 22:41:48 2007 -0400 process reading battery status hangs http://bugzilla.kernel.org/show_bug.cgi?id=8351 Signed-off-by: Vladimir Lebedev Signed-off-by: Len Brown commit 4d8f36e7d95a98fe7099a76e1fb766a77d26efff Author: Randy Dunlap Date: Wed Mar 28 22:52:53 2007 -0400 cpuidle: 1-bit field must be unsigned A 1-bit bitfield has no room for a sign bit. drivers/cpuidle/governors/ladder.c:54:16: error: dubious bitfield without explicit `signed' or `unsigned' Signed-off-by: Randy Dunlap Cc: Venkatesh Pallipadi Signed-off-by: Andrew Morton Signed-off-by: Len Brown commit 6e122e5e891d5e66fe3df37474e00b6ab241b80c Author: Venkatesh Pallipadi Date: Wed Mar 28 22:52:41 2007 -0400 cpuidle: fix boot hang Patch for cpuidle boot hang reported by Larry Finger here. http://www.ussg.iu.edu/hypermail/linux/kernel/0703.2/2025.html Signed-off-by: Venkatesh Pallipadi Cc: Larry Finger Signed-off-by: Andrew Morton Signed-off-by: Len Brown commit 8eecab24ab74a9b565888f62ab172b5993b85784 Author: Len Brown Date: Wed Mar 7 04:37:53 2007 -0500 cpuidle: ladder does not depend on ACPI build fix for CONFIG_ACPI=n In file included from drivers/cpuidle/governors/ladder.c:21: include/acpi/processor.h:88: error: expected specifier-qualifier-list before ‘acpi_integer’ include/acpi/processor.h:106: error: expected specifier-qualifier-list before ‘acpi_integer’ include/acpi/processor.h:168: error: expected specifier-qualifier-list before ‘acpi_handle’ Signed-off-by: Len Brown commit ed6a8fc4d67a601706e72442a0568852e20aa7e7 Author: Adrian Bunk Date: Tue Mar 6 02:29:40 2007 -0800 cpuidle: make code static This patch makes the following needlessly global code static: - driver.c: __cpuidle_find_driver() - governor.c: __cpuidle_find_governor() - ladder.c: struct ladder_governor Signed-off-by: Adrian Bunk Cc: Venkatesh Pallipadi Cc: Adam Belay Cc: Shaohua Li Signed-off-by: Andrew Morton Signed-off-by: Len Brown commit 0b7b292b9e893f21f275ab40457e0a6a9fb3302a Author: Venkatesh Pallipadi Date: Wed Mar 7 02:38:22 2007 -0500 cpu_idle: fix build break This patch fixes a build breakage with !CONFIG_HOTPLUG_CPU and CONFIG_CPU_IDLE. Signed-off-by: Venkatesh Pallipadi Signed-off-by: Adrian Bunk Signed-off-by: Andrew Morton Signed-off-by: Len Brown commit 5520c1cdb3154acb2c1250c395bd7d6f3c6f01b5 Author: Venkatesh Pallipadi Date: Tue Mar 6 02:29:39 2007 -0800 cpuidle: build fix for !CPU_IDLE Fix the compile issues when CPU_IDLE is not configured. Signed-off-by: Venkatesh Pallipadi Cc: Adam Belay Cc: Shaohua Li Signed-off-by: Andrew Morton Signed-off-by: Len Brown commit bd5951fdfdef243135ea275c27e6ff5ba20a3d7d Author: Venkatesh Pallipadi Date: Thu Feb 22 13:54:57 2007 -0800 cpuidle take2: Basic documentation for cpuidle Documentation for cpuidle infrastructure Signed-off-by: Venkatesh Pallipadi Signed-off-by: Adam Belay Signed-off-by: Shaohua Li Signed-off-by: Len Brown commit 33741f25522cf4e660e263106df951b9cbe02df1 Author: Venkatesh Pallipadi Date: Thu Feb 22 13:54:03 2007 -0800 cpuidle take2: Hookup ACPI C-states driver with cpuidle Hookup ACPI C-states onto generic cpuidle infrastructure. drivers/acpi/procesor_idle.c is now a ACPI C-states driver that registers as a driver in cpuidle infrastructure and the policy part is removed from drivers/acpi/processor_idle.c. We use governor in cpuidle instead. Signed-off-by: Shaohua Li Signed-off-by: Venkatesh Pallipadi Signed-off-by: Adam Belay Signed-off-by: Len Brown commit b89790e9968a77c6cdc9fa08c5260d73face5487 Author: Venkatesh Pallipadi Date: Thu Feb 22 13:52:57 2007 -0800 cpuidle take2: Core cpuidle infrastructure Announcing 'cpuidle', a new CPU power management infrastructure to manage idle CPUs in a clean and efficient manner. cpuidle separates out the drivers that can provide support for multiple types of idle states and policy governors that decide on what idle state to use at run time. A cpuidle driver can support multiple idle states based on parameters like varying power consumption, wakeup latency, etc (ACPI C-states for example). A cpuidle governor can be usage model specific (laptop, server, laptop on battery etc). Main advantage of the infrastructure being, it allows independent development of drivers and governors and allows for better CPU power management. A huge thanks to Adam Belay and Shaohua Li who were part of this mini-project since its beginning and are greatly responsible for this patchset. This patch: Core cpuidle infrastructure. Introduces a new abstraction layer for cpuidle: * which manages drivers that can support multiple idles states. Drivers can be generic or particular to specific hardware/platform * allows pluging in multiple policy governors that can take idle state policy decision * The core also has a set of sysfs interfaces with which administrato can know about supported drivers and governors and switch them at run time. Signed-off-by: Adam Belay Signed-off-by: Shaohua Li Signed-off-by: Venkatesh Pallipadi Signed-off-by: Len Brown commit 9ea7d57576f40c6af03c8c9fa7a069f2222b498b Author: Vladimir Lebedev Date: Tue Feb 20 15:48:06 2007 +0300 ACPI: battery: Lindent Signed-off-by: Vladimir Lebedev Signed-off-by: Len Brown commit b6ce4083ed8e2a01a3a59301eabe0fc1e68a8a84 Author: Vladimir Lebedev Date: Tue Feb 20 15:48:06 2007 +0300 ACPI: Cache battery status instead of re-evaluating AML /proc exports _BST in a single file, and _BST is re-evaulated whenever that file is read. Sometimes user-space reads this file frequently, and on some systems _BST takes a long time to evaluate due to a slow EC. Further, when we move to sysfs, the values returned from _BST will be in multiple files, and evaluating _BST for each file read would make matters worse. Here code is added to support caching the results of _BST. A new module parameter "update_time" tells how many seconds the cached _BST should be used before it is re-evaluated. Currently, update_time defaults to 0, and so the existing behaviour of re-evaluating on each read retained. Signed-off-by: Vladimir Lebedev Signed-off-by: Len Brown commit a1f0eff21edac1bd87e397f56c4258b9611b5a50 Author: Vladimir Lebedev Date: Tue Feb 20 15:48:06 2007 +0300 ACPI: battery: make internal names consistent with battery "state" Cleanup -- No functional changes. Battery state is currently exported in a proc "state" file. Update associated #defines and routines to be consistent. Signed-off-by: Vladimir Lebedev Signed-off-by: Len Brown Documentation/cpuidle/core.txt | 17 + Documentation/cpuidle/driver.txt | 24 + Documentation/cpuidle/governor.txt | 24 + Documentation/cpuidle/sysfs.txt | 27 + arch/i386/Kconfig | 2 arch/x86_64/Kconfig | 2 drivers/Makefile | 1 drivers/acpi/battery.c | 639 +++++++++++++++++++------- drivers/acpi/osl.c | 10 drivers/acpi/processor_core.c | 5 drivers/acpi/processor_idle.c | 890 ++++++++++++++---------------------- drivers/acpi/thermal.c | 104 ---- drivers/cpuidle/Kconfig | 28 + drivers/cpuidle/Makefile | 5 drivers/cpuidle/cpuidle.c | 312 +++++++++++++ drivers/cpuidle/cpuidle.h | 50 ++ drivers/cpuidle/driver.c | 276 +++++++++++ drivers/cpuidle/governor.c | 160 ++++++ drivers/cpuidle/governors/Makefile | 5 drivers/cpuidle/governors/ladder.c | 227 +++++++++ drivers/cpuidle/sysfs.c | 373 +++++++++++++++ include/acpi/processor.h | 3 include/linux/acpi.h | 7 include/linux/cpuidle.h | 189 ++++++++ 24 files changed, 2555 insertions(+), 825 deletions(-) diff --git a/Documentation/cpuidle/core.txt b/Documentation/cpuidle/core.txt new file mode 100644 index 0000000..e686cfc --- /dev/null +++ b/Documentation/cpuidle/core.txt @@ -0,0 +1,17 @@ + + Supporting multiple CPU idle levels in kernel + + cpuidle + +General Information: + +Various CPUs today support multiple idle levels that are differentiated +by varying exit latencies and power consumption during idle. +cpuidle is a generic in-kernel infrastructure that separates +idle policy (governor) from idle mechanism (driver) and provides a +standardized infrastructure to support independent development of +governors and drivers. + +cpuidle resides under /drivers/cpuidle. + + diff --git a/Documentation/cpuidle/driver.txt b/Documentation/cpuidle/driver.txt new file mode 100644 index 0000000..2dbee4b --- /dev/null +++ b/Documentation/cpuidle/driver.txt @@ -0,0 +1,24 @@ + + + Supporting multiple CPU idle levels in kernel + + cpuidle drivers + + + + +cpuidle driver supports capability detection for a particular system. The +init and exit routines will be called for each online CPU, with a percpu +cpuidle_driver object and driver should fill in cpuidle_states inside +cpuidle_driver depending on the CPU capability. + +Driver can handle dynamic state changes (like battery<->AC), by calling +force_redetect interface. + +It is possible to have more than one driver registered at the same time and +user can switch between drivers using /sysfs interface. + +Interfaces: +int cpuidle_register_driver(struct cpuidle_driver *drv); +void cpuidle_unregister_driver(struct cpuidle_driver *drv); +int cpuidle_force_redetect(struct cpuidle_device *dev); diff --git a/Documentation/cpuidle/governor.txt b/Documentation/cpuidle/governor.txt new file mode 100644 index 0000000..a0fc3e5 --- /dev/null +++ b/Documentation/cpuidle/governor.txt @@ -0,0 +1,24 @@ + + + + Supporting multiple CPU idle levels in kernel + + cpuidle governors + + + + +cpuidle governor is policy routine that decides what idle state to enter at +any given time. cpuidle core uses different callbacks to governor while +handling idle entry. +* select_state callback where governor can determine next idle state to enter +* prepare_idle callback is called before entering an idle state +* scan callback is called after a driver forces redetection of the states + +More than one governor can be registered at the same time and +user can switch between drivers using /sysfs interface. + +Interfaces: +int cpuidle_register_governor(struct cpuidle_governor *gov); +void cpuidle_unregister_governor(struct cpuidle_governor *gov); + diff --git a/Documentation/cpuidle/sysfs.txt b/Documentation/cpuidle/sysfs.txt new file mode 100644 index 0000000..7fbf644 --- /dev/null +++ b/Documentation/cpuidle/sysfs.txt @@ -0,0 +1,27 @@ + + + Supporting multiple CPU idle levels in kernel + + cpuidle sysfs + +System global cpuidle information are under +/sys/devices/system/cpu/cpuidle + +The current interfaces in this directory has self-explanatory names: +* available_drivers +* available_governors +* current_driver +* current_governor + +Per logical CPU specific cpuidle information are under +/sys/devices/system/cpu/cpuX/cpuidle +for each online cpu X + +Under this percpu directory, there is a directory for each idle state supported +by the driver, which in turn has +* latency +* power +* time +* usage + + diff --git a/arch/i386/Kconfig b/arch/i386/Kconfig index 53d6237..2984341 100644 --- a/arch/i386/Kconfig +++ b/arch/i386/Kconfig @@ -1065,6 +1065,8 @@ endmenu source "arch/i386/kernel/cpu/cpufreq/Kconfig" +source "drivers/cpuidle/Kconfig" + endmenu menu "Bus options (PCI, PCMCIA, EISA, MCA, ISA)" diff --git a/arch/x86_64/Kconfig b/arch/x86_64/Kconfig index 56eb14c..fd01428 100644 --- a/arch/x86_64/Kconfig +++ b/arch/x86_64/Kconfig @@ -670,6 +670,8 @@ source "drivers/acpi/Kconfig" source "arch/x86_64/kernel/cpufreq/Kconfig" +source "drivers/cpuidle/Kconfig" + endmenu menu "Bus options (PCI etc.)" diff --git a/drivers/Makefile b/drivers/Makefile index 920c975..b84393e 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -69,6 +69,7 @@ obj-$(CONFIG_EDAC) += edac/ obj-$(CONFIG_MCA) += mca/ obj-$(CONFIG_EISA) += eisa/ obj-$(CONFIG_CPU_FREQ) += cpufreq/ +obj-$(CONFIG_CPU_IDLE) += cpuidle/ obj-$(CONFIG_MMC) += mmc/ obj-$(CONFIG_NEW_LEDS) += leds/ obj-$(CONFIG_INFINIBAND) += infiniband/ diff --git a/drivers/acpi/battery.c b/drivers/acpi/battery.c index e64c76c..b5df318 100644 --- a/drivers/acpi/battery.c +++ b/drivers/acpi/battery.c @@ -44,7 +44,7 @@ #define ACPI_BATTERY_CLASS "battery" #define ACPI_BATTERY_HID "PNP0C0A" #define ACPI_BATTERY_DEVICE_NAME "Battery" #define ACPI_BATTERY_FILE_INFO "info" -#define ACPI_BATTERY_FILE_STATUS "state" +#define ACPI_BATTERY_FILE_STATE "state" #define ACPI_BATTERY_FILE_ALARM "alarm" #define ACPI_BATTERY_NOTIFY_STATUS 0x80 #define ACPI_BATTERY_NOTIFY_INFO 0x81 @@ -52,12 +52,24 @@ #define ACPI_BATTERY_UNITS_WATTS "mW" #define ACPI_BATTERY_UNITS_AMPS "mA" #define _COMPONENT ACPI_BATTERY_COMPONENT + +#define ACPI_BATTERY_UPDATE_TIME 0 + +#define ACPI_BATTERY_NONE_UPDATE 0 +#define ACPI_BATTERY_EASY_UPDATE 1 +#define ACPI_BATTERY_INIT_UPDATE 2 + ACPI_MODULE_NAME("battery"); MODULE_AUTHOR("Paul Diefenbaugh"); MODULE_DESCRIPTION("ACPI Battery Driver"); MODULE_LICENSE("GPL"); +static unsigned int update_time = ACPI_BATTERY_UPDATE_TIME; + +/* 0 - every time, > 0 - by update_time */ +module_param(update_time, uint, 0644); + extern struct proc_dir_entry *acpi_lock_battery_dir(void); extern void *acpi_unlock_battery_dir(struct proc_dir_entry *acpi_battery_dir); @@ -76,7 +88,7 @@ static struct acpi_driver acpi_battery_d }, }; -struct acpi_battery_status { +struct acpi_battery_state { acpi_integer state; acpi_integer present_rate; acpi_integer remaining_capacity; @@ -100,32 +112,117 @@ struct acpi_battery_info { }; struct acpi_battery_flags { - u8 present:1; /* Bay occupied? */ - u8 power_unit:1; /* 0=watts, 1=apms */ - u8 alarm:1; /* _BTP present? */ - u8 reserved:5; -}; - -struct acpi_battery_trips { - unsigned long warning; - unsigned long low; + u8 battery_present_prev; + u8 alarm_present; + u8 init_update; + u8 info_update; + u8 state_update; + u8 alarm_update; + u8 power_unit; }; struct acpi_battery { - struct acpi_device * device; + struct mutex mutex; + struct acpi_device *device; struct acpi_battery_flags flags; - struct acpi_battery_trips trips; + struct acpi_buffer bif_data; + struct acpi_buffer bst_data; unsigned long alarm; - struct acpi_battery_info *info; + unsigned long info_update_time; + unsigned long state_update_time; + unsigned long alarm_update_time; }; +#define acpi_battery_present(battery) battery->device->status.battery_present +#define acpi_battery_present_prev(battery) battery->flags.battery_present_prev +#define acpi_battery_alarm_present(battery) battery->flags.alarm_present +#define acpi_battery_init_update_flag(battery) battery->flags.init_update +#define acpi_battery_info_update_flag(battery) battery->flags.info_update +#define acpi_battery_state_update_flag(battery) battery->flags.state_update +#define acpi_battery_alarm_update_flag(battery) battery->flags.alarm_update +#define acpi_battery_power_units(battery) battery->flags.power_unit ? \ + ACPI_BATTERY_UNITS_AMPS : ACPI_BATTERY_UNITS_WATTS +#define acpi_battery_handle(battery) battery->device->handle +#define acpi_battery_inserted(battery) (!acpi_battery_present_prev(battery) & acpi_battery_present(battery)) +#define acpi_battery_removed(battery) (acpi_battery_present_prev(battery) & !acpi_battery_present(battery)) +#define acpi_battery_bid(battery) acpi_device_bid(battery->device) +#define acpi_battery_status_str(battery) acpi_battery_present(battery) ? "present" : "absent" + /* -------------------------------------------------------------------------- Battery Management -------------------------------------------------------------------------- */ -static int -acpi_battery_get_info(struct acpi_battery *battery, - struct acpi_battery_info **bif) +static void acpi_battery_mutex_lock(struct acpi_battery *battery) +{ + mutex_lock(&battery->mutex); +} + +static void acpi_battery_mutex_unlock(struct acpi_battery *battery) +{ + mutex_unlock(&battery->mutex); +} + +static void acpi_battery_check_result(struct acpi_battery *battery, int result) +{ + if (!battery) + return; + + if (result) { + acpi_battery_init_update_flag(battery) = 1; + } +} + +static int acpi_battery_extract_package(struct acpi_battery *battery, + union acpi_object *package, + struct acpi_buffer *format, + struct acpi_buffer *data, + char *package_name) +{ + acpi_status status = AE_OK; + struct acpi_buffer data_null = { 0, NULL }; + + status = acpi_extract_package(package, format, &data_null); + if (status != AE_BUFFER_OVERFLOW) { + ACPI_EXCEPTION((AE_INFO, status, "Extracting size %s", + package_name)); + return -ENODEV; + } + + if (data_null.length != data->length) { + if (data->pointer) { + kfree(data->pointer); + } + data->pointer = kzalloc(data_null.length, GFP_KERNEL); + if (!data->pointer) { + ACPI_EXCEPTION((AE_INFO, AE_NO_MEMORY, "kzalloc()")); + return -ENOMEM; + } + data->length = data_null.length; + } + + status = acpi_extract_package(package, format, data); + if (ACPI_FAILURE(status)) { + ACPI_EXCEPTION((AE_INFO, status, "Extracting %s", + package_name)); + return -ENODEV; + } + + return 0; +} + +static int acpi_battery_get_status(struct acpi_battery *battery) +{ + int result = 0; + + result = acpi_bus_get_status(battery->device); + if (result) { + ACPI_EXCEPTION((AE_INFO, AE_ERROR, "Evaluating _STA")); + return -ENODEV; + } + return result; +} + +static int acpi_battery_get_info(struct acpi_battery *battery) { int result = 0; acpi_status status = 0; @@ -133,16 +230,20 @@ acpi_battery_get_info(struct acpi_batter struct acpi_buffer format = { sizeof(ACPI_BATTERY_FORMAT_BIF), ACPI_BATTERY_FORMAT_BIF }; - struct acpi_buffer data = { 0, NULL }; union acpi_object *package = NULL; + struct acpi_buffer *data = NULL; + struct acpi_battery_info *bif = NULL; + battery->info_update_time = get_seconds(); - if (!battery || !bif) - return -EINVAL; + if (!acpi_battery_present(battery)) + return 0; /* Evalute _BIF */ - status = acpi_evaluate_object(battery->device->handle, "_BIF", NULL, &buffer); + status = + acpi_evaluate_object(acpi_battery_handle(battery), "_BIF", NULL, + &buffer); if (ACPI_FAILURE(status)) { ACPI_EXCEPTION((AE_INFO, status, "Evaluating _BIF")); return -ENODEV; @@ -150,41 +251,31 @@ acpi_battery_get_info(struct acpi_batter package = buffer.pointer; + data = &battery->bif_data; + /* Extract Package Data */ - status = acpi_extract_package(package, &format, &data); - if (status != AE_BUFFER_OVERFLOW) { - ACPI_EXCEPTION((AE_INFO, status, "Extracting _BIF")); - result = -ENODEV; + result = + acpi_battery_extract_package(battery, package, &format, data, + "_BIF"); + if (result) goto end; - } - data.pointer = kzalloc(data.length, GFP_KERNEL); - if (!data.pointer) { - result = -ENOMEM; - goto end; - } + end: - status = acpi_extract_package(package, &format, &data); - if (ACPI_FAILURE(status)) { - ACPI_EXCEPTION((AE_INFO, status, "Extracting _BIF")); - kfree(data.pointer); - result = -ENODEV; - goto end; + if (buffer.pointer) { + kfree(buffer.pointer); } - end: - kfree(buffer.pointer); - - if (!result) - (*bif) = data.pointer; + if (!result) { + bif = data->pointer; + battery->flags.power_unit = bif->power_unit; + } return result; } -static int -acpi_battery_get_status(struct acpi_battery *battery, - struct acpi_battery_status **bst) +static int acpi_battery_get_state(struct acpi_battery *battery) { int result = 0; acpi_status status = 0; @@ -192,16 +283,19 @@ acpi_battery_get_status(struct acpi_batt struct acpi_buffer format = { sizeof(ACPI_BATTERY_FORMAT_BST), ACPI_BATTERY_FORMAT_BST }; - struct acpi_buffer data = { 0, NULL }; union acpi_object *package = NULL; + struct acpi_buffer *data = NULL; + battery->state_update_time = get_seconds(); - if (!battery || !bst) - return -EINVAL; + if (!acpi_battery_present(battery)) + return 0; /* Evalute _BST */ - status = acpi_evaluate_object(battery->device->handle, "_BST", NULL, &buffer); + status = + acpi_evaluate_object(acpi_battery_handle(battery), "_BST", NULL, + &buffer); if (ACPI_FAILURE(status)) { ACPI_EXCEPTION((AE_INFO, status, "Evaluating _BST")); return -ENODEV; @@ -209,55 +303,51 @@ acpi_battery_get_status(struct acpi_batt package = buffer.pointer; - /* Extract Package Data */ + data = &battery->bst_data; - status = acpi_extract_package(package, &format, &data); - if (status != AE_BUFFER_OVERFLOW) { - ACPI_EXCEPTION((AE_INFO, status, "Extracting _BST")); - result = -ENODEV; - goto end; - } + /* Extract Package Data */ - data.pointer = kzalloc(data.length, GFP_KERNEL); - if (!data.pointer) { - result = -ENOMEM; + result = + acpi_battery_extract_package(battery, package, &format, data, + "_BST"); + if (result) goto end; - } - status = acpi_extract_package(package, &format, &data); - if (ACPI_FAILURE(status)) { - ACPI_EXCEPTION((AE_INFO, status, "Extracting _BST")); - kfree(data.pointer); - result = -ENODEV; - goto end; + end: + if (buffer.pointer) { + kfree(buffer.pointer); } - end: - kfree(buffer.pointer); + return result; +} - if (!result) - (*bst) = data.pointer; +static int acpi_battery_get_alarm(struct acpi_battery *battery) +{ + battery->alarm_update_time = get_seconds(); - return result; + return 0; } -static int -acpi_battery_set_alarm(struct acpi_battery *battery, unsigned long alarm) +static int acpi_battery_set_alarm(struct acpi_battery *battery, + unsigned long alarm) { acpi_status status = 0; union acpi_object arg0 = { ACPI_TYPE_INTEGER }; struct acpi_object_list arg_list = { 1, &arg0 }; + battery->alarm_update_time = get_seconds(); - if (!battery) - return -EINVAL; + if (!acpi_battery_present(battery)) + return -ENODEV; - if (!battery->flags.alarm) + if (!acpi_battery_alarm_present(battery)) return -ENODEV; arg0.integer.value = alarm; - status = acpi_evaluate_object(battery->device->handle, "_BTP", &arg_list, NULL); + status = + acpi_evaluate_object(acpi_battery_handle(battery), "_BTP", + &arg_list, NULL); if (ACPI_FAILURE(status)) return -ENODEV; @@ -268,65 +358,111 @@ acpi_battery_set_alarm(struct acpi_batte return 0; } -static int acpi_battery_check(struct acpi_battery *battery) +static int acpi_battery_init_alarm(struct acpi_battery *battery) { int result = 0; acpi_status status = AE_OK; acpi_handle handle = NULL; - struct acpi_device *device = NULL; - struct acpi_battery_info *bif = NULL; - + struct acpi_battery_info *bif = battery->bif_data.pointer; + unsigned long alarm = battery->alarm; - if (!battery) - return -EINVAL; + /* See if alarms are supported, and if so, set default */ - device = battery->device; + status = acpi_get_handle(acpi_battery_handle(battery), "_BTP", &handle); + if (ACPI_SUCCESS(status)) { + acpi_battery_alarm_present(battery) = 1; + if (!alarm && bif) { + alarm = bif->design_capacity_warning; + } + result = acpi_battery_set_alarm(battery, alarm); + if (result) + goto end; + } else { + acpi_battery_alarm_present(battery) = 0; + } - result = acpi_bus_get_status(device); - if (result) - return result; + end: - /* Insertion? */ + return result; +} - if (!battery->flags.present && device->status.battery_present) { +static int acpi_battery_init_update(struct acpi_battery *battery) +{ + int result = 0; - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Battery inserted\n")); + result = acpi_battery_get_status(battery); + if (result) + return result; - /* Evalute _BIF to get certain static information */ + acpi_battery_present_prev(battery) = acpi_battery_present(battery); - result = acpi_battery_get_info(battery, &bif); + if (acpi_battery_present(battery)) { + result = acpi_battery_get_info(battery); + if (result) + return result; + result = acpi_battery_get_state(battery); if (result) return result; - battery->flags.power_unit = bif->power_unit; - battery->trips.warning = bif->design_capacity_warning; - battery->trips.low = bif->design_capacity_low; - kfree(bif); + acpi_battery_init_alarm(battery); + } + + return result; +} + +static int acpi_battery_update(struct acpi_battery *battery, + int update, int *update_result_ptr) +{ + int result = 0; + int update_result = ACPI_BATTERY_NONE_UPDATE; - /* See if alarms are supported, and if so, set default */ + if (!acpi_battery_present(battery)) { + update = 1; + } - status = acpi_get_handle(battery->device->handle, "_BTP", &handle); - if (ACPI_SUCCESS(status)) { - battery->flags.alarm = 1; - acpi_battery_set_alarm(battery, battery->trips.warning); + if (acpi_battery_init_update_flag(battery)) { + result = acpi_battery_init_update(battery); + if (result) + goto end;; + update_result = ACPI_BATTERY_INIT_UPDATE; + } else if (update) { + result = acpi_battery_get_status(battery); + if (result) + goto end;; + if (acpi_battery_inserted(battery) + || acpi_battery_removed(battery)) { + result = acpi_battery_init_update(battery); + if (result) + goto end;; + update_result = ACPI_BATTERY_INIT_UPDATE; + } else { + update_result = ACPI_BATTERY_EASY_UPDATE; } } - /* Removal? */ + end: - else if (battery->flags.present && !device->status.battery_present) { - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Battery removed\n")); - } + acpi_battery_init_update_flag(battery) = (result != 0); - battery->flags.present = device->status.battery_present; + *update_result_ptr = update_result; return result; } -static void acpi_battery_check_present(struct acpi_battery *battery) +static void acpi_battery_notify_update(struct acpi_battery *battery) { - if (!battery->flags.present) { - acpi_battery_check(battery); + acpi_battery_get_status(battery); + + if (acpi_battery_init_update_flag(battery)) { + return; + } + + if (acpi_battery_inserted(battery) || acpi_battery_removed(battery)) { + acpi_battery_init_update_flag(battery) = 1; + } else { + acpi_battery_info_update_flag(battery) = 1; + acpi_battery_state_update_flag(battery) = 1; + acpi_battery_alarm_update_flag(battery) = 1; } } @@ -335,37 +471,33 @@ static void acpi_battery_check_present(s -------------------------------------------------------------------------- */ static struct proc_dir_entry *acpi_battery_dir; -static int acpi_battery_read_info(struct seq_file *seq, void *offset) + +static int acpi_battery_read_info_print(struct seq_file *seq, int result) { - int result = 0; struct acpi_battery *battery = seq->private; struct acpi_battery_info *bif = NULL; char *units = "?"; - - if (!battery) + if (result) goto end; - acpi_battery_check_present(battery); - - if (battery->flags.present) + if (acpi_battery_present(battery)) seq_printf(seq, "present: yes\n"); else { seq_printf(seq, "present: no\n"); goto end; } - /* Battery Info (_BIF) */ - - result = acpi_battery_get_info(battery, &bif); - if (result || !bif) { - seq_printf(seq, "ERROR: Unable to read battery information\n"); + bif = battery->bif_data.pointer; + if (!bif) { + ACPI_EXCEPTION((AE_INFO, AE_ERROR, "BIF buffer is NULL")); + result = -ENODEV; goto end; } - units = - bif-> - power_unit ? ACPI_BATTERY_UNITS_AMPS : ACPI_BATTERY_UNITS_WATTS; + /* Battery Units */ + + units = acpi_battery_power_units(battery); if (bif->design_capacity == ACPI_BATTERY_VALUE_UNKNOWN) seq_printf(seq, "design capacity: unknown\n"); @@ -396,7 +528,6 @@ static int acpi_battery_read_info(struct else seq_printf(seq, "design voltage: %d mV\n", (u32) bif->design_voltage); - seq_printf(seq, "design capacity warning: %d %sh\n", (u32) bif->design_capacity_warning, units); seq_printf(seq, "design capacity low: %d %sh\n", @@ -411,9 +542,48 @@ static int acpi_battery_read_info(struct seq_printf(seq, "OEM info: %s\n", bif->oem_info); end: - kfree(bif); - return 0; + if (result) + seq_printf(seq, "ERROR: Unable to read battery info\n"); + + return result; +} + +static int acpi_battery_read_info(struct seq_file *seq, void *offset) +{ + struct acpi_battery *battery = seq->private; + int result = 0; + int update_result = ACPI_BATTERY_NONE_UPDATE; + int update = 0; + + acpi_battery_mutex_lock(battery); + + update = (get_seconds() - battery->info_update_time >= update_time); + update = (update | acpi_battery_info_update_flag(battery)); + + result = acpi_battery_update(battery, update, &update_result); + if (result) + goto end; + + /* Battery Info (_BIF) */ + + if (update_result == ACPI_BATTERY_EASY_UPDATE) { + result = acpi_battery_get_info(battery); + if (result) + goto end; + } + + end: + + result = acpi_battery_read_info_print(seq, result); + + acpi_battery_check_result(battery, result); + + acpi_battery_info_update_flag(battery) = result; + + acpi_battery_mutex_unlock(battery); + + return result; } static int acpi_battery_info_open_fs(struct inode *inode, struct file *file) @@ -421,40 +591,33 @@ static int acpi_battery_info_open_fs(str return single_open(file, acpi_battery_read_info, PDE(inode)->data); } -static int acpi_battery_read_state(struct seq_file *seq, void *offset) +static int acpi_battery_read_state_print(struct seq_file *seq, int result) { - int result = 0; struct acpi_battery *battery = seq->private; - struct acpi_battery_status *bst = NULL; + struct acpi_battery_state *bst = NULL; char *units = "?"; - - if (!battery) + if (result) goto end; - acpi_battery_check_present(battery); - - if (battery->flags.present) + if (acpi_battery_present(battery)) seq_printf(seq, "present: yes\n"); else { seq_printf(seq, "present: no\n"); goto end; } - /* Battery Units */ - - units = - battery->flags. - power_unit ? ACPI_BATTERY_UNITS_AMPS : ACPI_BATTERY_UNITS_WATTS; - - /* Battery Status (_BST) */ - - result = acpi_battery_get_status(battery, &bst); - if (result || !bst) { - seq_printf(seq, "ERROR: Unable to read battery status\n"); + bst = battery->bst_data.pointer; + if (!bst) { + ACPI_EXCEPTION((AE_INFO, AE_ERROR, "BST buffer is NULL")); + result = -ENODEV; goto end; } + /* Battery Units */ + + units = acpi_battery_power_units(battery); + if (!(bst->state & 0x04)) seq_printf(seq, "capacity state: ok\n"); else @@ -490,9 +653,49 @@ static int acpi_battery_read_state(struc (u32) bst->present_voltage); end: - kfree(bst); - return 0; + if (result) { + seq_printf(seq, "ERROR: Unable to read battery state\n"); + } + + return result; +} + +static int acpi_battery_read_state(struct seq_file *seq, void *offset) +{ + struct acpi_battery *battery = seq->private; + int result = 0; + int update_result = ACPI_BATTERY_NONE_UPDATE; + int update = 0; + + acpi_battery_mutex_lock(battery); + + update = (get_seconds() - battery->state_update_time >= update_time); + update = (update | acpi_battery_state_update_flag(battery)); + + result = acpi_battery_update(battery, update, &update_result); + if (result) + goto end; + + /* Battery State (_BST) */ + + if (update_result == ACPI_BATTERY_EASY_UPDATE) { + result = acpi_battery_get_state(battery); + if (result) + goto end; + } + + end: + + result = acpi_battery_read_state_print(seq, result); + + acpi_battery_check_result(battery, result); + + acpi_battery_state_update_flag(battery) = result; + + acpi_battery_mutex_unlock(battery); + + return result; } static int acpi_battery_state_open_fs(struct inode *inode, struct file *file) @@ -500,38 +703,72 @@ static int acpi_battery_state_open_fs(st return single_open(file, acpi_battery_read_state, PDE(inode)->data); } -static int acpi_battery_read_alarm(struct seq_file *seq, void *offset) +static int acpi_battery_read_alarm_print(struct seq_file *seq, int result) { struct acpi_battery *battery = seq->private; char *units = "?"; - - if (!battery) + if (result) goto end; - acpi_battery_check_present(battery); - - if (!battery->flags.present) { + if (!acpi_battery_present(battery)) { seq_printf(seq, "present: no\n"); goto end; } /* Battery Units */ - units = - battery->flags. - power_unit ? ACPI_BATTERY_UNITS_AMPS : ACPI_BATTERY_UNITS_WATTS; - - /* Battery Alarm */ + units = acpi_battery_power_units(battery); seq_printf(seq, "alarm: "); if (!battery->alarm) seq_printf(seq, "unsupported\n"); else - seq_printf(seq, "%d %sh\n", (u32) battery->alarm, units); + seq_printf(seq, "%lu %sh\n", battery->alarm, units); end: - return 0; + + if (result) + seq_printf(seq, "ERROR: Unable to read battery alarm\n"); + + return result; +} + +static int acpi_battery_read_alarm(struct seq_file *seq, void *offset) +{ + struct acpi_battery *battery = seq->private; + int result = 0; + int update_result = ACPI_BATTERY_NONE_UPDATE; + int update = 0; + + acpi_battery_mutex_lock(battery); + + update = (get_seconds() - battery->alarm_update_time >= update_time); + update = (update | acpi_battery_alarm_update_flag(battery)); + + result = acpi_battery_update(battery, update, &update_result); + if (result) + goto end; + + /* Battery Alarm */ + + if (update_result == ACPI_BATTERY_EASY_UPDATE) { + result = acpi_battery_get_alarm(battery); + if (result) + goto end; + } + + end: + + result = acpi_battery_read_alarm_print(seq, result); + + acpi_battery_check_result(battery, result); + + acpi_battery_alarm_update_flag(battery) = result; + + acpi_battery_mutex_unlock(battery); + + return result; } static ssize_t @@ -543,27 +780,46 @@ acpi_battery_write_alarm(struct file *fi char alarm_string[12] = { '\0' }; struct seq_file *m = file->private_data; struct acpi_battery *battery = m->private; - + int update_result = ACPI_BATTERY_NONE_UPDATE; if (!battery || (count > sizeof(alarm_string) - 1)) return -EINVAL; - acpi_battery_check_present(battery); + acpi_battery_mutex_lock(battery); - if (!battery->flags.present) - return -ENODEV; + result = acpi_battery_update(battery, 1, &update_result); + if (result) { + result = -ENODEV; + goto end; + } - if (copy_from_user(alarm_string, buffer, count)) - return -EFAULT; + if (!acpi_battery_present(battery)) { + result = -ENODEV; + goto end; + } + + if (copy_from_user(alarm_string, buffer, count)) { + result = -EFAULT; + goto end; + } alarm_string[count] = '\0'; result = acpi_battery_set_alarm(battery, simple_strtoul(alarm_string, NULL, 0)); if (result) - return result; + goto end; - return count; + end: + + acpi_battery_check_result(battery, result); + + if (!result) + result = count; + + acpi_battery_mutex_unlock(battery); + + return result; } static int acpi_battery_alarm_open_fs(struct inode *inode, struct file *file) @@ -600,7 +856,6 @@ static int acpi_battery_add_fs(struct ac { struct proc_dir_entry *entry = NULL; - if (!acpi_device_dir(device)) { acpi_device_dir(device) = proc_mkdir(acpi_device_bid(device), acpi_battery_dir); @@ -621,7 +876,7 @@ static int acpi_battery_add_fs(struct ac } /* 'status' [R] */ - entry = create_proc_entry(ACPI_BATTERY_FILE_STATUS, + entry = create_proc_entry(ACPI_BATTERY_FILE_STATE, S_IRUGO, acpi_device_dir(device)); if (!entry) return -ENODEV; @@ -648,11 +903,10 @@ static int acpi_battery_add_fs(struct ac static int acpi_battery_remove_fs(struct acpi_device *device) { - if (acpi_device_dir(device)) { remove_proc_entry(ACPI_BATTERY_FILE_ALARM, acpi_device_dir(device)); - remove_proc_entry(ACPI_BATTERY_FILE_STATUS, + remove_proc_entry(ACPI_BATTERY_FILE_STATE, acpi_device_dir(device)); remove_proc_entry(ACPI_BATTERY_FILE_INFO, acpi_device_dir(device)); @@ -673,7 +927,6 @@ static void acpi_battery_notify(acpi_han struct acpi_battery *battery = data; struct acpi_device *device = NULL; - if (!battery) return; @@ -684,8 +937,10 @@ static void acpi_battery_notify(acpi_han case ACPI_BATTERY_NOTIFY_INFO: case ACPI_NOTIFY_BUS_CHECK: case ACPI_NOTIFY_DEVICE_CHECK: - acpi_battery_check(battery); - acpi_bus_generate_event(device, event, battery->flags.present); + device = battery->device; + acpi_battery_notify_update(battery); + acpi_bus_generate_event(device, event, + acpi_battery_present(battery)); break; default: ACPI_DEBUG_PRINT((ACPI_DB_INFO, @@ -702,7 +957,6 @@ static int acpi_battery_add(struct acpi_ acpi_status status = 0; struct acpi_battery *battery = NULL; - if (!device) return -EINVAL; @@ -710,15 +964,21 @@ static int acpi_battery_add(struct acpi_ if (!battery) return -ENOMEM; + mutex_init(&battery->mutex); + + acpi_battery_mutex_lock(battery); + battery->device = device; strcpy(acpi_device_name(device), ACPI_BATTERY_DEVICE_NAME); strcpy(acpi_device_class(device), ACPI_BATTERY_CLASS); acpi_driver_data(device) = battery; - result = acpi_battery_check(battery); + result = acpi_battery_get_status(battery); if (result) goto end; + acpi_battery_init_update_flag(battery) = 1; + result = acpi_battery_add_fs(device); if (result) goto end; @@ -727,6 +987,7 @@ static int acpi_battery_add(struct acpi_ ACPI_ALL_NOTIFY, acpi_battery_notify, battery); if (ACPI_FAILURE(status)) { + ACPI_EXCEPTION((AE_INFO, status, "Installing notify handler")); result = -ENODEV; goto end; } @@ -736,11 +997,14 @@ static int acpi_battery_add(struct acpi_ device->status.battery_present ? "present" : "absent"); end: + if (result) { acpi_battery_remove_fs(device); kfree(battery); } + acpi_battery_mutex_unlock(battery); + return result; } @@ -749,18 +1013,29 @@ static int acpi_battery_remove(struct ac acpi_status status = 0; struct acpi_battery *battery = NULL; - if (!device || !acpi_driver_data(device)) return -EINVAL; battery = acpi_driver_data(device); + acpi_battery_mutex_lock(battery); + status = acpi_remove_notify_handler(device->handle, ACPI_ALL_NOTIFY, acpi_battery_notify); acpi_battery_remove_fs(device); + if (battery->bif_data.pointer) + kfree(battery->bif_data.pointer); + + if (battery->bst_data.pointer) + kfree(battery->bst_data.pointer); + + acpi_battery_mutex_unlock(battery); + + mutex_destroy(&battery->mutex); + kfree(battery); return 0; @@ -775,7 +1050,10 @@ static int acpi_battery_resume(struct ac return -EINVAL; battery = device->driver_data; - return acpi_battery_check(battery); + + acpi_battery_init_update_flag(battery) = 1; + + return 0; } static int __init acpi_battery_init(void) @@ -800,7 +1078,6 @@ static int __init acpi_battery_init(void static void __exit acpi_battery_exit(void) { - acpi_bus_unregister_driver(&acpi_battery_driver); acpi_unlock_battery_dir(acpi_battery_dir); diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c index 971eca4..090b844 100644 --- a/drivers/acpi/osl.c +++ b/drivers/acpi/osl.c @@ -997,6 +997,16 @@ unsigned int max_cstate = ACPI_PROCESSOR EXPORT_SYMBOL(max_cstate); +void (*acpi_do_set_cstate_limit)(void); +EXPORT_SYMBOL(acpi_do_set_cstate_limit); + +void acpi_set_cstate_limit(unsigned int new_limit) +{ + max_cstate = new_limit; + if (acpi_do_set_cstate_limit) + acpi_do_set_cstate_limit(); +} + /* * Acquire a spinlock. * diff --git a/drivers/acpi/processor_core.c b/drivers/acpi/processor_core.c index f7de02a..60646dd 100644 --- a/drivers/acpi/processor_core.c +++ b/drivers/acpi/processor_core.c @@ -44,6 +44,7 @@ #include #include #include #include +#include #include #include @@ -1022,11 +1023,15 @@ #endif acpi_processor_ppc_init(); + cpuidle_register_driver(&acpi_idle_driver); + acpi_do_set_cstate_limit = acpi_max_cstate_changed; return 0; } static void __exit acpi_processor_exit(void) { + acpi_do_set_cstate_limit = NULL; + cpuidle_unregister_driver(&acpi_idle_driver); acpi_processor_ppc_exit(); diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c index ae0654c..d42e259 100644 --- a/drivers/acpi/processor_idle.c +++ b/drivers/acpi/processor_idle.c @@ -40,6 +40,7 @@ #include #include /* need_resched() */ #include #include +#include /* * Include the apic definitions for x86 to have the APIC timer related defines @@ -62,25 +63,34 @@ #define ACPI_PROCESSOR_CLASS #define _COMPONENT ACPI_PROCESSOR_COMPONENT ACPI_MODULE_NAME("processor_idle"); #define ACPI_PROCESSOR_FILE_POWER "power" -#define US_TO_PM_TIMER_TICKS(t) ((t * (PM_TIMER_FREQUENCY/1000)) / 1000) -#define C2_OVERHEAD 4 /* 1us (3.579 ticks per us) */ -#define C3_OVERHEAD 4 /* 1us (3.579 ticks per us) */ -static void (*pm_idle_save) (void) __read_mostly; -module_param(max_cstate, uint, 0644); +#define PM_TIMER_TICKS_TO_US(p) (((p) * 1000)/(PM_TIMER_FREQUENCY/1000)) +#define C2_OVERHEAD 1 /* 1us */ +#define C3_OVERHEAD 1 /* 1us */ + +void acpi_max_cstate_changed(void) +{ + /* Driver will reset devices' max cstate limit */ + cpuidle_force_redetect_devices(&acpi_idle_driver); +} + +static int change_max_cstate(const char *val, struct kernel_param *kp) +{ + int max; + + max = simple_strtol(val, NULL, 0); + if (!max) + return -EINVAL; + max_cstate = max; + if (acpi_do_set_cstate_limit) + acpi_do_set_cstate_limit(); + return 0; +} + +module_param_call(max_cstate, change_max_cstate, param_get_uint, &max_cstate, 0644); static unsigned int nocst __read_mostly; module_param(nocst, uint, 0000); -/* - * bm_history -- bit-mask with a bit per jiffy of bus-master activity - * 1000 HZ: 0xFFFFFFFF: 32 jiffies = 32ms - * 800 HZ: 0xFFFFFFFF: 32 jiffies = 40ms - * 100 HZ: 0x0000000F: 4 jiffies = 40ms - * reduce history for more aggressive entry into C3 - */ -static unsigned int bm_history __read_mostly = - (HZ >= 800 ? 0xFFFFFFFF : ((1U << (HZ / 25)) - 1)); -module_param(bm_history, uint, 0644); /* -------------------------------------------------------------------------- Power Management -------------------------------------------------------------------------- */ @@ -166,88 +176,6 @@ static struct dmi_system_id __cpuinitdat {}, }; -static inline u32 ticks_elapsed(u32 t1, u32 t2) -{ - if (t2 >= t1) - return (t2 - t1); - else if (!(acpi_gbl_FADT.flags & ACPI_FADT_32BIT_TIMER)) - return (((0x00FFFFFF - t1) + t2) & 0x00FFFFFF); - else - return ((0xFFFFFFFF - t1) + t2); -} - -static void -acpi_processor_power_activate(struct acpi_processor *pr, - struct acpi_processor_cx *new) -{ - struct acpi_processor_cx *old; - - if (!pr || !new) - return; - - old = pr->power.state; - - if (old) - old->promotion.count = 0; - new->demotion.count = 0; - - /* Cleanup from old state. */ - if (old) { - switch (old->type) { - case ACPI_STATE_C3: - /* Disable bus master reload */ - if (new->type != ACPI_STATE_C3 && pr->flags.bm_check) - acpi_set_register(ACPI_BITREG_BUS_MASTER_RLD, 0); - break; - } - } - - /* Prepare to use new state. */ - switch (new->type) { - case ACPI_STATE_C3: - /* Enable bus master reload */ - if (old->type != ACPI_STATE_C3 && pr->flags.bm_check) - acpi_set_register(ACPI_BITREG_BUS_MASTER_RLD, 1); - break; - } - - pr->power.state = new; - - return; -} - -static void acpi_safe_halt(void) -{ - current_thread_info()->status &= ~TS_POLLING; - /* - * TS_POLLING-cleared state must be visible before we - * test NEED_RESCHED: - */ - smp_mb(); - if (!need_resched()) - safe_halt(); - current_thread_info()->status |= TS_POLLING; -} - -static atomic_t c3_cpu_count; - -/* Common C-state entry for C2, C3, .. */ -static void acpi_cstate_enter(struct acpi_processor_cx *cstate) -{ - if (cstate->space_id == ACPI_CSTATE_FFH) { - /* Call into architectural FFH based C-state */ - acpi_processor_ffh_cstate_enter(cstate); - } else { - int unused; - /* IO port based C-state */ - inb(cstate->address); - /* Dummy wait op - must do something useless after P_LVL2 read - because chipsets cannot guarantee that STPCLK# signal - gets asserted in time to freeze execution properly. */ - unused = inl(acpi_gbl_FADT.xpm_timer_block.address); - } -} - #ifdef ARCH_APICTIMER_STOPS_ON_C3 /* @@ -323,376 +251,6 @@ static void acpi_state_timer_broadcast(s } #endif - -static void acpi_processor_idle(void) -{ - struct acpi_processor *pr = NULL; - struct acpi_processor_cx *cx = NULL; - struct acpi_processor_cx *next_state = NULL; - int sleep_ticks = 0; - u32 t1, t2 = 0; - - pr = processors[smp_processor_id()]; - if (!pr) - return; - - /* - * Interrupts must be disabled during bus mastering calculations and - * for C2/C3 transitions. - */ - local_irq_disable(); - - /* - * Check whether we truly need to go idle, or should - * reschedule: - */ - if (unlikely(need_resched())) { - local_irq_enable(); - return; - } - - cx = pr->power.state; - if (!cx) { - if (pm_idle_save) - pm_idle_save(); - else - acpi_safe_halt(); - return; - } - - /* - * Check BM Activity - * ----------------- - * Check for bus mastering activity (if required), record, and check - * for demotion. - */ - if (pr->flags.bm_check) { - u32 bm_status = 0; - unsigned long diff = jiffies - pr->power.bm_check_timestamp; - - if (diff > 31) - diff = 31; - - pr->power.bm_activity <<= diff; - - acpi_get_register(ACPI_BITREG_BUS_MASTER_STATUS, &bm_status); - if (bm_status) { - pr->power.bm_activity |= 0x1; - acpi_set_register(ACPI_BITREG_BUS_MASTER_STATUS, 1); - } - /* - * PIIX4 Erratum #18: Note that BM_STS doesn't always reflect - * the true state of bus mastering activity; forcing us to - * manually check the BMIDEA bit of each IDE channel. - */ - else if (errata.piix4.bmisx) { - if ((inb_p(errata.piix4.bmisx + 0x02) & 0x01) - || (inb_p(errata.piix4.bmisx + 0x0A) & 0x01)) - pr->power.bm_activity |= 0x1; - } - - pr->power.bm_check_timestamp = jiffies; - - /* - * If bus mastering is or was active this jiffy, demote - * to avoid a faulty transition. Note that the processor - * won't enter a low-power state during this call (to this - * function) but should upon the next. - * - * TBD: A better policy might be to fallback to the demotion - * state (use it for this quantum only) istead of - * demoting -- and rely on duration as our sole demotion - * qualification. This may, however, introduce DMA - * issues (e.g. floppy DMA transfer overrun/underrun). - */ - if ((pr->power.bm_activity & 0x1) && - cx->demotion.threshold.bm) { - local_irq_enable(); - next_state = cx->demotion.state; - goto end; - } - } - -#ifdef CONFIG_HOTPLUG_CPU - /* - * Check for P_LVL2_UP flag before entering C2 and above on - * an SMP system. We do it here instead of doing it at _CST/P_LVL - * detection phase, to work cleanly with logical CPU hotplug. - */ - if ((cx->type != ACPI_STATE_C1) && (num_online_cpus() > 1) && - !pr->flags.has_cst && !(acpi_gbl_FADT.flags & ACPI_FADT_C2_MP_SUPPORTED)) - cx = &pr->power.states[ACPI_STATE_C1]; -#endif - - /* - * Sleep: - * ------ - * Invoke the current Cx state to put the processor to sleep. - */ - if (cx->type == ACPI_STATE_C2 || cx->type == ACPI_STATE_C3) { - current_thread_info()->status &= ~TS_POLLING; - /* - * TS_POLLING-cleared state must be visible before we - * test NEED_RESCHED: - */ - smp_mb(); - if (need_resched()) { - current_thread_info()->status |= TS_POLLING; - local_irq_enable(); - return; - } - } - - switch (cx->type) { - - case ACPI_STATE_C1: - /* - * Invoke C1. - * Use the appropriate idle routine, the one that would - * be used without acpi C-states. - */ - if (pm_idle_save) - pm_idle_save(); - else - acpi_safe_halt(); - - /* - * TBD: Can't get time duration while in C1, as resumes - * go to an ISR rather than here. Need to instrument - * base interrupt handler. - */ - sleep_ticks = 0xFFFFFFFF; - break; - - case ACPI_STATE_C2: - /* Get start time (ticks) */ - t1 = inl(acpi_gbl_FADT.xpm_timer_block.address); - /* Invoke C2 */ - acpi_state_timer_broadcast(pr, cx, 1); - acpi_cstate_enter(cx); - /* Get end time (ticks) */ - t2 = inl(acpi_gbl_FADT.xpm_timer_block.address); - -#ifdef CONFIG_GENERIC_TIME - /* TSC halts in C2, so notify users */ - mark_tsc_unstable(); -#endif - /* Re-enable interrupts */ - local_irq_enable(); - current_thread_info()->status |= TS_POLLING; - /* Compute time (ticks) that we were actually asleep */ - sleep_ticks = - ticks_elapsed(t1, t2) - cx->latency_ticks - C2_OVERHEAD; - acpi_state_timer_broadcast(pr, cx, 0); - break; - - case ACPI_STATE_C3: - - if (pr->flags.bm_check) { - if (atomic_inc_return(&c3_cpu_count) == - num_online_cpus()) { - /* - * All CPUs are trying to go to C3 - * Disable bus master arbitration - */ - acpi_set_register(ACPI_BITREG_ARB_DISABLE, 1); - } - } else { - /* SMP with no shared cache... Invalidate cache */ - ACPI_FLUSH_CPU_CACHE(); - } - - /* Get start time (ticks) */ - t1 = inl(acpi_gbl_FADT.xpm_timer_block.address); - /* Invoke C3 */ - acpi_state_timer_broadcast(pr, cx, 1); - acpi_cstate_enter(cx); - /* Get end time (ticks) */ - t2 = inl(acpi_gbl_FADT.xpm_timer_block.address); - if (pr->flags.bm_check) { - /* Enable bus master arbitration */ - atomic_dec(&c3_cpu_count); - acpi_set_register(ACPI_BITREG_ARB_DISABLE, 0); - } - -#ifdef CONFIG_GENERIC_TIME - /* TSC halts in C3, so notify users */ - mark_tsc_unstable(); -#endif - /* Re-enable interrupts */ - local_irq_enable(); - current_thread_info()->status |= TS_POLLING; - /* Compute time (ticks) that we were actually asleep */ - sleep_ticks = - ticks_elapsed(t1, t2) - cx->latency_ticks - C3_OVERHEAD; - acpi_state_timer_broadcast(pr, cx, 0); - break; - - default: - local_irq_enable(); - return; - } - cx->usage++; - if ((cx->type != ACPI_STATE_C1) && (sleep_ticks > 0)) - cx->time += sleep_ticks; - - next_state = pr->power.state; - -#ifdef CONFIG_HOTPLUG_CPU - /* Don't do promotion/demotion */ - if ((cx->type == ACPI_STATE_C1) && (num_online_cpus() > 1) && - !pr->flags.has_cst && !(acpi_gbl_FADT.flags & ACPI_FADT_C2_MP_SUPPORTED)) { - next_state = cx; - goto end; - } -#endif - - /* - * Promotion? - * ---------- - * Track the number of longs (time asleep is greater than threshold) - * and promote when the count threshold is reached. Note that bus - * mastering activity may prevent promotions. - * Do not promote above max_cstate. - */ - if (cx->promotion.state && - ((cx->promotion.state - pr->power.states) <= max_cstate)) { - if (sleep_ticks > cx->promotion.threshold.ticks && - cx->promotion.state->latency <= system_latency_constraint()) { - cx->promotion.count++; - cx->demotion.count = 0; - if (cx->promotion.count >= - cx->promotion.threshold.count) { - if (pr->flags.bm_check) { - if (! - (pr->power.bm_activity & cx-> - promotion.threshold.bm)) { - next_state = - cx->promotion.state; - goto end; - } - } else { - next_state = cx->promotion.state; - goto end; - } - } - } - } - - /* - * Demotion? - * --------- - * Track the number of shorts (time asleep is less than time threshold) - * and demote when the usage threshold is reached. - */ - if (cx->demotion.state) { - if (sleep_ticks < cx->demotion.threshold.ticks) { - cx->demotion.count++; - cx->promotion.count = 0; - if (cx->demotion.count >= cx->demotion.threshold.count) { - next_state = cx->demotion.state; - goto end; - } - } - } - - end: - /* - * Demote if current state exceeds max_cstate - * or if the latency of the current state is unacceptable - */ - if ((pr->power.state - pr->power.states) > max_cstate || - pr->power.state->latency > system_latency_constraint()) { - if (cx->demotion.state) - next_state = cx->demotion.state; - } - - /* - * New Cx State? - * ------------- - * If we're going to start using a new Cx state we must clean up - * from the previous and prepare to use the new. - */ - if (next_state != pr->power.state) - acpi_processor_power_activate(pr, next_state); -} - -static int acpi_processor_set_power_policy(struct acpi_processor *pr) -{ - unsigned int i; - unsigned int state_is_set = 0; - struct acpi_processor_cx *lower = NULL; - struct acpi_processor_cx *higher = NULL; - struct acpi_processor_cx *cx; - - - if (!pr) - return -EINVAL; - - /* - * This function sets the default Cx state policy (OS idle handler). - * Our scheme is to promote quickly to C2 but more conservatively - * to C3. We're favoring C2 for its characteristics of low latency - * (quick response), good power savings, and ability to allow bus - * mastering activity. Note that the Cx state policy is completely - * customizable and can be altered dynamically. - */ - - /* startup state */ - for (i = 1; i < ACPI_PROCESSOR_MAX_POWER; i++) { - cx = &pr->power.states[i]; - if (!cx->valid) - continue; - - if (!state_is_set) - pr->power.state = cx; - state_is_set++; - break; - } - - if (!state_is_set) - return -ENODEV; - - /* demotion */ - for (i = 1; i < ACPI_PROCESSOR_MAX_POWER; i++) { - cx = &pr->power.states[i]; - if (!cx->valid) - continue; - - if (lower) { - cx->demotion.state = lower; - cx->demotion.threshold.ticks = cx->latency_ticks; - cx->demotion.threshold.count = 1; - if (cx->type == ACPI_STATE_C3) - cx->demotion.threshold.bm = bm_history; - } - - lower = cx; - } - - /* promotion */ - for (i = (ACPI_PROCESSOR_MAX_POWER - 1); i > 0; i--) { - cx = &pr->power.states[i]; - if (!cx->valid) - continue; - - if (higher) { - cx->promotion.state = higher; - cx->promotion.threshold.ticks = cx->latency_ticks; - if (cx->type >= ACPI_STATE_C2) - cx->promotion.threshold.count = 4; - else - cx->promotion.threshold.count = 10; - if (higher->type == ACPI_STATE_C3) - cx->promotion.threshold.bm = bm_history; - } - - higher = cx; - } - - return 0; -} - static int acpi_processor_get_power_info_fadt(struct acpi_processor *pr) { @@ -910,7 +468,7 @@ static void acpi_processor_power_verify_ * Normalize the C2 latency to expidite policy */ cx->valid = 1; - cx->latency_ticks = US_TO_PM_TIMER_TICKS(cx->latency); + cx->latency_ticks = cx->latency; return; } @@ -984,7 +542,7 @@ static void acpi_processor_power_verify_ * use this in our C3 policy */ cx->valid = 1; - cx->latency_ticks = US_TO_PM_TIMER_TICKS(cx->latency); + cx->latency_ticks = cx->latency; return; } @@ -1050,18 +608,6 @@ static int acpi_processor_get_power_info pr->power.count = acpi_processor_power_verify(pr); /* - * Set Default Policy - * ------------------ - * Now that we know which states are supported, set the default - * policy. Note that this policy can be changed dynamically - * (e.g. encourage deeper sleeps to conserve battery life when - * not on AC). - */ - result = acpi_processor_set_power_policy(pr); - if (result) - return result; - - /* * if one state of type C2 or C3 is available, mark this * CPU as being "idle manageable" */ @@ -1078,9 +624,6 @@ static int acpi_processor_get_power_info int acpi_processor_cst_has_changed(struct acpi_processor *pr) { - int result = 0; - - if (!pr) return -EINVAL; @@ -1091,16 +634,9 @@ int acpi_processor_cst_has_changed(struc if (!pr->flags.power_setup_done) return -ENODEV; - /* Fall back to the default idle loop */ - pm_idle = pm_idle_save; - synchronize_sched(); /* Relies on interrupts forcing exit from idle. */ - - pr->flags.power = 0; - result = acpi_processor_get_power_info(pr); - if ((pr->flags.power == 1) && (pr->flags.power_setup_done)) - pm_idle = acpi_processor_idle; - - return result; + acpi_processor_get_power_info(pr); + return cpuidle_force_redetect(per_cpu(cpuidle_devices, pr->id), + &acpi_idle_driver); } /* proc interface */ @@ -1186,30 +722,6 @@ static const struct file_operations acpi .release = single_release, }; -#ifdef CONFIG_SMP -static void smp_callback(void *v) -{ - /* we already woke the CPU up, nothing more to do */ -} - -/* - * This function gets called when a part of the kernel has a new latency - * requirement. This means we need to get all processors out of their C-state, - * and then recalculate a new suitable C-state. Just do a cross-cpu IPI; that - * wakes them all right up. - */ -static int acpi_processor_latency_notify(struct notifier_block *b, - unsigned long l, void *v) -{ - smp_call_function(smp_callback, NULL, 0, 1); - return NOTIFY_OK; -} - -static struct notifier_block acpi_processor_latency_notifier = { - .notifier_call = acpi_processor_latency_notify, -}; -#endif - int __cpuinit acpi_processor_power_init(struct acpi_processor *pr, struct acpi_device *device) { @@ -1226,9 +738,6 @@ int __cpuinit acpi_processor_power_init( "ACPI: processor limited to max C-state %d\n", max_cstate); first_run++; -#ifdef CONFIG_SMP - register_latency_notifier(&acpi_processor_latency_notifier); -#endif } if (!pr) @@ -1245,6 +754,7 @@ #endif acpi_processor_get_power_info(pr); + /* * Install the idle handler if processor power management is supported. * Note that we use previously set idle handler will be used on @@ -1257,11 +767,6 @@ #endif printk(" C%d[C%d]", i, pr->power.states[i].type); printk(")\n"); - - if (pr->id == 0) { - pm_idle_save = pm_idle; - pm_idle = acpi_processor_idle; - } } /* 'power' [R] */ @@ -1289,21 +794,332 @@ int acpi_processor_power_exit(struct acp if (acpi_device_dir(device)) remove_proc_entry(ACPI_PROCESSOR_FILE_POWER, acpi_device_dir(device)); + return 0; +} - /* Unregister the idle handler when processor #0 is removed. */ - if (pr->id == 0) { - pm_idle = pm_idle_save; +/** + * ticks_elapsed - a helper function that determines how many ticks (in US) + * have elapsed between two PM Timer timestamps + * @t1: the start time + * @t2: the end time + */ +static inline u32 ticks_elapsed(u32 t1, u32 t2) +{ + if (t2 >= t1) + return PM_TIMER_TICKS_TO_US(t2 - t1); + else if (!(acpi_gbl_FADT.flags & ACPI_FADT_32BIT_TIMER)) + return PM_TIMER_TICKS_TO_US(((0x00FFFFFF - t1) + t2) & 0x00FFFFFF); + else + return PM_TIMER_TICKS_TO_US((0xFFFFFFFF - t1) + t2); +} - /* - * We are about to unload the current idle thread pm callback - * (pm_idle), Wait for all processors to update cached/local - * copies of pm_idle before proceeding. - */ - cpu_idle_wait(); -#ifdef CONFIG_SMP - unregister_latency_notifier(&acpi_processor_latency_notifier); +/** + * acpi_idle_update_bm_rld - updates the BM_RLD bit depending on target state + * @pr: the processor + * @target: the new target state + */ +static inline void acpi_idle_update_bm_rld(struct acpi_processor *pr, + struct acpi_processor_cx *target) +{ + if (pr->flags.bm_rld_set && target->type != ACPI_STATE_C3) { + acpi_set_register(ACPI_BITREG_BUS_MASTER_RLD, 0); + pr->flags.bm_rld_set = 0; + } + + if (!pr->flags.bm_rld_set && target->type == ACPI_STATE_C3) { + acpi_set_register(ACPI_BITREG_BUS_MASTER_RLD, 1); + pr->flags.bm_rld_set = 1; + } +} + +/** + * acpi_idle_do_entry - a helper function that does C2 and C3 type entry + * @cx: cstate data + */ +static inline void acpi_idle_do_entry(struct acpi_processor_cx *cx) +{ + if (cx->space_id == ACPI_CSTATE_FFH) { + /* Call into architectural FFH based C-state */ + acpi_processor_ffh_cstate_enter(cx); + } else { + int unused; + /* IO port based C-state */ + inb(cx->address); + /* Dummy wait op - must do something useless after P_LVL2 read + because chipsets cannot guarantee that STPCLK# signal + gets asserted in time to freeze execution properly. */ + unused = inl(acpi_gbl_FADT.xpm_timer_block.address); + } +} + +/** + * acpi_idle_enter_c1 - enters an ACPI C1 state-type + * @dev: the target CPU + * @state: the state data + * + * This is equivalent to the HALT instruction. + */ +static int acpi_idle_enter_c1(struct cpuidle_device *dev, + struct cpuidle_state *state) +{ + struct acpi_processor *pr; + struct acpi_processor_cx *cx = cpuidle_get_statedata(state); + pr = processors[smp_processor_id()]; + + if (unlikely(!pr)) + return 0; + + if (pr->flags.bm_check) + acpi_idle_update_bm_rld(pr, cx); + + current_thread_info()->status &= ~TS_POLLING; + /* + * TS_POLLING-cleared state must be visible before we test + * NEED_RESCHED: + */ + smp_mb(); + if (!need_resched()) + safe_halt(); + current_thread_info()->status |= TS_POLLING; + + cx->usage++; + + return 0; +} + +/** + * acpi_idle_enter_c2 - enters an ACPI C2 state-type + * @dev: the target CPU + * @state: the state data + */ +static int acpi_idle_enter_c2(struct cpuidle_device *dev, + struct cpuidle_state *state) +{ + struct acpi_processor *pr; + struct acpi_processor_cx *cx = cpuidle_get_statedata(state); + u32 t1, t2; + pr = processors[smp_processor_id()]; + + if (unlikely(!pr)) + return 0; + + if (pr->flags.bm_check) + acpi_idle_update_bm_rld(pr, cx); + + local_irq_disable(); + current_thread_info()->status &= ~TS_POLLING; + /* + * TS_POLLING-cleared state must be visible before we test + * NEED_RESCHED: + */ + smp_mb(); + + if (unlikely(need_resched())) { + current_thread_info()->status |= TS_POLLING; + local_irq_enable(); + return 0; + } + + t1 = inl(acpi_gbl_FADT.xpm_timer_block.address); + acpi_state_timer_broadcast(pr, cx, 1); + acpi_idle_do_entry(cx); + t2 = inl(acpi_gbl_FADT.xpm_timer_block.address); + +#ifdef CONFIG_GENERIC_TIME + /* TSC halts in C2, so notify users */ + mark_tsc_unstable(); #endif + + local_irq_enable(); + current_thread_info()->status |= TS_POLLING; + + cx->usage++; + + acpi_state_timer_broadcast(pr, cx, 0); + return ticks_elapsed(t1, t2); +} + +static int c3_cpu_count; +static DEFINE_SPINLOCK(c3_lock); + +/** + * acpi_idle_enter_c3 - enters an ACPI C3 state-type + * @dev: the target CPU + * @state: the state data + * + * Similar to C2 entry, except special bus master handling is needed. + */ +static int acpi_idle_enter_c3(struct cpuidle_device *dev, + struct cpuidle_state *state) +{ + struct acpi_processor *pr; + struct acpi_processor_cx *cx = cpuidle_get_statedata(state); + u32 t1, t2; + pr = processors[smp_processor_id()]; + + if (unlikely(!pr)) + return 0; + + if (pr->flags.bm_check) + acpi_idle_update_bm_rld(pr, cx); + + local_irq_disable(); + current_thread_info()->status &= ~TS_POLLING; + /* + * TS_POLLING-cleared state must be visible before we test + * NEED_RESCHED: + */ + smp_mb(); + + if (unlikely(need_resched())) { + current_thread_info()->status |= TS_POLLING; + local_irq_enable(); + return 0; } + /* disable bus master */ + if (pr->flags.bm_check) { + spin_lock(&c3_lock); + c3_cpu_count++; + if (c3_cpu_count == num_online_cpus()) { + /* + * All CPUs are trying to go to C3 + * Disable bus master arbitration + */ + acpi_set_register(ACPI_BITREG_ARB_DISABLE, 1); + } + spin_unlock(&c3_lock); + } else { + /* SMP with no shared cache... Invalidate cache */ + ACPI_FLUSH_CPU_CACHE(); + } + + /* Get start time (ticks) */ + t1 = inl(acpi_gbl_FADT.xpm_timer_block.address); + acpi_state_timer_broadcast(pr, cx, 1); + acpi_idle_do_entry(cx); + t2 = inl(acpi_gbl_FADT.xpm_timer_block.address); + + if (pr->flags.bm_check) { + spin_lock(&c3_lock); + /* Enable bus master arbitration */ + if (c3_cpu_count == num_online_cpus()) + acpi_set_register(ACPI_BITREG_ARB_DISABLE, 0); + c3_cpu_count--; + spin_unlock(&c3_lock); + } + +#ifdef CONFIG_GENERIC_TIME + /* TSC halts in C3, so notify users */ + mark_tsc_unstable(); +#endif + + local_irq_enable(); + current_thread_info()->status |= TS_POLLING; + + cx->usage++; + + acpi_state_timer_broadcast(pr, cx, 0); + return ticks_elapsed(t1, t2); +} + +/** + * acpi_idle_bm_check - checks if bus master activity was detected + */ +static int acpi_idle_bm_check(void) +{ + u32 bm_status = 0; + + acpi_get_register(ACPI_BITREG_BUS_MASTER_STATUS, &bm_status); + if (bm_status) + acpi_set_register(ACPI_BITREG_BUS_MASTER_STATUS, 1); + /* + * PIIX4 Erratum #18: Note that BM_STS doesn't always reflect + * the true state of bus mastering activity; forcing us to + * manually check the BMIDEA bit of each IDE channel. + */ + else if (errata.piix4.bmisx) { + if ((inb_p(errata.piix4.bmisx + 0x02) & 0x01) + || (inb_p(errata.piix4.bmisx + 0x0A) & 0x01)) + bm_status = 1; + } + return bm_status; +} + +/** + * acpi_idle_init - attaches the driver to a CPU + * @dev: the CPU + */ +static int acpi_idle_init(struct cpuidle_device *dev) +{ + int cpu = dev->cpu; + int i, count = 0; + struct acpi_processor_cx *cx; + struct cpuidle_state *state; + + struct acpi_processor *pr = processors[cpu]; + + if (!pr->flags.power_setup_done) + return -EINVAL; + + if (pr->flags.power == 0) { + return -EINVAL; + } + + for (i = 1; i < ACPI_PROCESSOR_MAX_POWER && i <= max_cstate; i++) { + cx = &pr->power.states[i]; + state = &dev->states[count]; + + if (!cx->valid) + continue; + +#ifdef CONFIG_HOTPLUG_CPU + if ((cx->type != ACPI_STATE_C1) && (num_online_cpus() > 1) && + !pr->flags.has_cst && + !(acpi_gbl_FADT.flags & ACPI_FADT_C2_MP_SUPPORTED)) + continue; +#endif + cpuidle_set_statedata(state, cx); + + state->exit_latency = cx->latency; + state->target_residency = cx->latency * 6; + state->power_usage = cx->power; + + state->flags = 0; + switch (cx->type) { + case ACPI_STATE_C1: + state->flags |= CPUIDLE_FLAG_SHALLOW; + state->enter = acpi_idle_enter_c1; + break; + + case ACPI_STATE_C2: + state->flags |= CPUIDLE_FLAG_BALANCED; + state->flags |= CPUIDLE_FLAG_TIME_VALID; + state->enter = acpi_idle_enter_c2; + break; + + case ACPI_STATE_C3: + state->flags |= CPUIDLE_FLAG_DEEP; + state->flags |= CPUIDLE_FLAG_TIME_VALID; + state->flags |= CPUIDLE_FLAG_CHECK_BM; + state->enter = acpi_idle_enter_c3; + break; + } + + count++; + } + + if (!count) + return -EINVAL; + + dev->state_count = count; return 0; } + +struct cpuidle_driver acpi_idle_driver = { + .name = "acpi_idle", + .init = acpi_idle_init, + .redetect = acpi_idle_init, + .bm_check = acpi_idle_bm_check, + .owner = THIS_MODULE, +}; diff --git a/drivers/acpi/thermal.c b/drivers/acpi/thermal.c index 589b98b..1ada017 100644 --- a/drivers/acpi/thermal.c +++ b/drivers/acpi/thermal.c @@ -59,8 +59,6 @@ #define ACPI_THERMAL_NOTIFY_DEVICES 0x82 #define ACPI_THERMAL_NOTIFY_CRITICAL 0xF0 #define ACPI_THERMAL_NOTIFY_HOT 0xF1 #define ACPI_THERMAL_MODE_ACTIVE 0x00 -#define ACPI_THERMAL_MODE_PASSIVE 0x01 -#define ACPI_THERMAL_MODE_CRITICAL 0xff #define ACPI_THERMAL_PATH_POWEROFF "/sbin/poweroff" #define ACPI_THERMAL_MAX_ACTIVE 10 @@ -86,9 +84,6 @@ static int acpi_thermal_resume(struct ac static int acpi_thermal_state_open_fs(struct inode *inode, struct file *file); static int acpi_thermal_temp_open_fs(struct inode *inode, struct file *file); static int acpi_thermal_trip_open_fs(struct inode *inode, struct file *file); -static ssize_t acpi_thermal_write_trip_points(struct file *, - const char __user *, size_t, - loff_t *); static int acpi_thermal_cooling_open_fs(struct inode *inode, struct file *file); static ssize_t acpi_thermal_write_cooling_mode(struct file *, const char __user *, size_t, @@ -167,7 +162,6 @@ struct acpi_thermal { unsigned long temperature; unsigned long last_temperature; unsigned long polling_frequency; - u8 cooling_mode; volatile u8 zombie; struct acpi_thermal_flags flags; struct acpi_thermal_state state; @@ -193,7 +187,6 @@ static const struct file_operations acpi static const struct file_operations acpi_thermal_trip_fops = { .open = acpi_thermal_trip_open_fs, .read = seq_read, - .write = acpi_thermal_write_trip_points, .llseek = seq_lseek, .release = single_release, }; @@ -297,11 +290,6 @@ static int acpi_thermal_set_cooling_mode if (ACPI_FAILURE(status)) return -ENODEV; - tz->cooling_mode = mode; - - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Cooling mode [%s]\n", - mode ? "passive" : "active")); - return 0; } @@ -889,67 +877,6 @@ static int acpi_thermal_trip_open_fs(str return single_open(file, acpi_thermal_trip_seq_show, PDE(inode)->data); } -static ssize_t -acpi_thermal_write_trip_points(struct file *file, - const char __user * buffer, - size_t count, loff_t * ppos) -{ - struct seq_file *m = file->private_data; - struct acpi_thermal *tz = m->private; - - char *limit_string; - int num, critical, hot, passive; - int *active; - int i = 0; - - - limit_string = kzalloc(ACPI_THERMAL_MAX_LIMIT_STR_LEN, GFP_KERNEL); - if (!limit_string) - return -ENOMEM; - - active = kmalloc(ACPI_THERMAL_MAX_ACTIVE * sizeof(int), GFP_KERNEL); - if (!active) { - kfree(limit_string); - return -ENOMEM; - } - - if (!tz || (count > ACPI_THERMAL_MAX_LIMIT_STR_LEN - 1)) { - count = -EINVAL; - goto end; - } - - if (copy_from_user(limit_string, buffer, count)) { - count = -EFAULT; - goto end; - } - - limit_string[count] = '\0'; - - num = sscanf(limit_string, "%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d", - &critical, &hot, &passive, - &active[0], &active[1], &active[2], &active[3], &active[4], - &active[5], &active[6], &active[7], &active[8], - &active[9]); - if (!(num >= 5 && num < (ACPI_THERMAL_MAX_ACTIVE + 3))) { - count = -EINVAL; - goto end; - } - - tz->trips.critical.temperature = CELSIUS_TO_KELVIN(critical); - tz->trips.hot.temperature = CELSIUS_TO_KELVIN(hot); - tz->trips.passive.temperature = CELSIUS_TO_KELVIN(passive); - for (i = 0; i < num - 3; i++) { - if (!(tz->trips.active[i].flags.valid)) - break; - tz->trips.active[i].temperature = CELSIUS_TO_KELVIN(active[i]); - } - - end: - kfree(active); - kfree(limit_string); - return count; -} - static int acpi_thermal_cooling_seq_show(struct seq_file *seq, void *offset) { struct acpi_thermal *tz = seq->private; @@ -958,15 +885,10 @@ static int acpi_thermal_cooling_seq_show if (!tz) goto end; - if (!tz->flags.cooling_mode) { + if (!tz->flags.cooling_mode) seq_puts(seq, "\n"); - } - - if (tz->cooling_mode == ACPI_THERMAL_MODE_CRITICAL) - seq_printf(seq, "cooling mode: critical\n"); else - seq_printf(seq, "cooling mode: %s\n", - tz->cooling_mode ? "passive" : "active"); + seq_puts(seq, "0 - Active; 1 - Passive\n"); end: return 0; @@ -1223,28 +1145,6 @@ static int acpi_thermal_get_info(struct result = acpi_thermal_set_cooling_mode(tz, ACPI_THERMAL_MODE_ACTIVE); if (!result) tz->flags.cooling_mode = 1; - else { - /* Oh,we have not _SCP method. - Generally show cooling_mode by _ACx, _PSV,spec 12.2 */ - tz->flags.cooling_mode = 0; - if (tz->trips.active[0].flags.valid - && tz->trips.passive.flags.valid) { - if (tz->trips.passive.temperature > - tz->trips.active[0].temperature) - tz->cooling_mode = ACPI_THERMAL_MODE_ACTIVE; - else - tz->cooling_mode = ACPI_THERMAL_MODE_PASSIVE; - } else if (!tz->trips.active[0].flags.valid - && tz->trips.passive.flags.valid) { - tz->cooling_mode = ACPI_THERMAL_MODE_PASSIVE; - } else if (tz->trips.active[0].flags.valid - && !tz->trips.passive.flags.valid) { - tz->cooling_mode = ACPI_THERMAL_MODE_ACTIVE; - } else { - /* _ACx and _PSV are optional, but _CRT is required */ - tz->cooling_mode = ACPI_THERMAL_MODE_CRITICAL; - } - } /* Get default polling frequency [_TZP] (optional) */ if (tzp) diff --git a/drivers/cpuidle/Kconfig b/drivers/cpuidle/Kconfig new file mode 100644 index 0000000..b9ac217 --- /dev/null +++ b/drivers/cpuidle/Kconfig @@ -0,0 +1,28 @@ +menu "CPU idle PM support" + +config CPU_IDLE + bool "CPU idle PM support" + help + CPU idle is a generic framework for supporting software-controlled + idle processor power management. It includes modular cross-platform + governors that can be swapped during runtime. + + If you're using a mobile platform that supports CPU idle PM (e.g. + an ACPI-capable notebook), you should say Y here. + +if CPU_IDLE + +comment "Governors" + +config CPU_IDLE_GOV_LADDER + tristate "'ladder' governor" + depends on CPU_IDLE + default y + help + This cpuidle governor promotes and demotes through the supported idle + states using residency time and bus master activity as metrics. This + algorithm was originally introduced in the old ACPI processor driver. + +endif # CPU_IDLE + +endmenu diff --git a/drivers/cpuidle/Makefile b/drivers/cpuidle/Makefile new file mode 100644 index 0000000..5634f88 --- /dev/null +++ b/drivers/cpuidle/Makefile @@ -0,0 +1,5 @@ +# +# Makefile for cpuidle. +# + +obj-y += cpuidle.o driver.o governor.o sysfs.o governors/ diff --git a/drivers/cpuidle/cpuidle.c b/drivers/cpuidle/cpuidle.c new file mode 100644 index 0000000..01e76a8 --- /dev/null +++ b/drivers/cpuidle/cpuidle.c @@ -0,0 +1,312 @@ +/* + * cpuidle.c - core cpuidle infrastructure + * + * (C) 2006-2007 Venkatesh Pallipadi + * Shaohua Li + * Adam Belay + * + * This code is licenced under the GPL. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "cpuidle.h" + +DEFINE_PER_CPU(struct cpuidle_device *, cpuidle_devices); +EXPORT_PER_CPU_SYMBOL_GPL(cpuidle_devices); + +DEFINE_MUTEX(cpuidle_lock); +LIST_HEAD(cpuidle_detected_devices); +static void (*pm_idle_old)(void); + + +/** + * cpuidle_idle_call - the main idle loop + * + * NOTE: no locks or semaphores should be used here + * FIXME: DYNTICKS handling + */ +static void cpuidle_idle_call(void) +{ + struct cpuidle_device *dev = __get_cpu_var(cpuidle_devices); + + struct cpuidle_state *target_state; + int next_state; + + /* check if the device is ready */ + if (!dev || dev->status != CPUIDLE_STATUS_DOIDLE) { + if (pm_idle_old) + pm_idle_old(); + else + local_irq_enable(); + return; + } + + if (cpuidle_curr_governor->prepare_idle) + cpuidle_curr_governor->prepare_idle(dev); + + while(!need_resched()) { + next_state = cpuidle_curr_governor->select_state(dev); + if (need_resched()) + break; + + target_state = &dev->states[next_state]; + + dev->last_residency = target_state->enter(dev, target_state); + dev->last_state = target_state; + target_state->time += dev->last_residency; + target_state->usage++; + + if (dev->status != CPUIDLE_STATUS_DOIDLE) + break; + } +} + +/** + * cpuidle_install_idle_handler - installs the cpuidle idle loop handler + */ +void cpuidle_install_idle_handler(void) +{ + if (pm_idle != cpuidle_idle_call) { + /* Make sure all changes finished before we switch to new idle */ + smp_wmb(); + pm_idle = cpuidle_idle_call; + } +} + +/** + * cpuidle_uninstall_idle_handler - uninstalls the cpuidle idle loop handler + */ +void cpuidle_uninstall_idle_handler(void) +{ + if (pm_idle != pm_idle_old) { + pm_idle = pm_idle_old; + cpu_idle_wait(); + } +} + +/** + * cpuidle_rescan_device - prepares for a new state configuration + * @dev: the target device + * + * Must be called with cpuidle_lock aquired. + */ +void cpuidle_rescan_device(struct cpuidle_device *dev) +{ + int i; + + if (cpuidle_curr_governor->scan) + cpuidle_curr_governor->scan(dev); + + for (i = 0; i < dev->state_count; i++) { + dev->states[i].usage = 0; + dev->states[i].time = 0; + } +} + +/** + * cpuidle_add_device - attaches the driver to a CPU instance + * @sys_dev: the system device (driver model CPU representation) + */ +static int cpuidle_add_device(struct sys_device *sys_dev) +{ + int cpu = sys_dev->id; + struct cpuidle_device *dev; + + dev = per_cpu(cpuidle_devices, cpu); + + mutex_lock(&cpuidle_lock); + if (cpu_is_offline(cpu)) { + mutex_unlock(&cpuidle_lock); + return 0; + } + + if (!dev) { + dev = kzalloc(sizeof(struct cpuidle_device), GFP_KERNEL); + if (!dev) { + mutex_unlock(&cpuidle_lock); + return -ENOMEM; + } + init_completion(&dev->kobj_unregister); + per_cpu(cpuidle_devices, cpu) = dev; + } + dev->cpu = cpu; + + if (dev->status & CPUIDLE_STATUS_DETECTED) { + mutex_unlock(&cpuidle_lock); + return 0; + } + + cpuidle_add_sysfs(sys_dev); + + if (cpuidle_curr_driver) { + if (cpuidle_attach_driver(dev)) + goto err_ret; + } + + if (cpuidle_curr_governor) { + if (cpuidle_attach_governor(dev)) { + cpuidle_detach_driver(dev); + goto err_ret; + } + } + + if (cpuidle_device_can_idle(dev)) + cpuidle_install_idle_handler(); + + list_add(&dev->device_list, &cpuidle_detected_devices); + dev->status |= CPUIDLE_STATUS_DETECTED; + +err_ret: + mutex_unlock(&cpuidle_lock); + + return 0; +} + +/** + * __cpuidle_remove_device - detaches the driver from a CPU instance + * @sys_dev: the system device (driver model CPU representation) + * + * Must be called with cpuidle_lock aquired. + */ +static int __cpuidle_remove_device(struct sys_device *sys_dev) +{ + struct cpuidle_device *dev; + + dev = per_cpu(cpuidle_devices, sys_dev->id); + + if (!(dev->status & CPUIDLE_STATUS_DETECTED)) { + return 0; + } + dev->status &= ~CPUIDLE_STATUS_DETECTED; + /* NOTE: we don't wait because the cpu is already offline */ + if (cpuidle_curr_governor) + cpuidle_detach_governor(dev); + if (cpuidle_curr_driver) + cpuidle_detach_driver(dev); + cpuidle_remove_sysfs(sys_dev); + list_del(&dev->device_list); + wait_for_completion(&dev->kobj_unregister); + per_cpu(cpuidle_devices, sys_dev->id) = NULL; + kfree(dev); + + return 0; +} + +/** + * cpuidle_remove_device - detaches the driver from a CPU instance + * @sys_dev: the system device (driver model CPU representation) + */ +static int cpuidle_remove_device(struct sys_device *sys_dev) +{ + int ret; + mutex_lock(&cpuidle_lock); + ret = __cpuidle_remove_device(sys_dev); + mutex_unlock(&cpuidle_lock); + + return ret; +} + +static struct sysdev_driver cpuidle_sysdev_driver = { + .add = cpuidle_add_device, + .remove = cpuidle_remove_device, +}; + +static int cpuidle_cpu_callback(struct notifier_block *nfb, + unsigned long action, void *hcpu) +{ + struct sys_device *sys_dev; + + sys_dev = get_cpu_sysdev((unsigned long)hcpu); + + switch (action) { + case CPU_ONLINE: + cpuidle_add_device(sys_dev); + break; + case CPU_DOWN_PREPARE: + mutex_lock(&cpuidle_lock); + break; + case CPU_DEAD: + __cpuidle_remove_device(sys_dev); + mutex_unlock(&cpuidle_lock); + break; + case CPU_DOWN_FAILED: + mutex_unlock(&cpuidle_lock); + break; + } + + return NOTIFY_OK; +} + +static struct notifier_block __cpuinitdata cpuidle_cpu_notifier = +{ + .notifier_call = cpuidle_cpu_callback, +}; + +#ifdef CONFIG_SMP + +static void smp_callback(void *v) +{ + /* we already woke the CPU up, nothing more to do */ +} + +/* + * This function gets called when a part of the kernel has a new latency + * requirement. This means we need to get all processors out of their C-state, + * and then recalculate a new suitable C-state. Just do a cross-cpu IPI; that + * wakes them all right up. + */ +static int cpuidle_latency_notify(struct notifier_block *b, + unsigned long l, void *v) +{ + smp_call_function(smp_callback, NULL, 0, 1); + return NOTIFY_OK; +} + +static struct notifier_block cpuidle_latency_notifier = { + .notifier_call = cpuidle_latency_notify, +}; + +#define latency_notifier_init(x) do { register_latency_notifier(x); } while (0) + +#else /* CONFIG_SMP */ + +#define latency_notifier_init(x) do { } while (0) + +#endif /* CONFIG_SMP */ + +/** + * cpuidle_init - core initializer + */ +static int __init cpuidle_init(void) +{ + int ret; + + pm_idle_old = pm_idle; + + ret = cpuidle_add_class_sysfs(&cpu_sysdev_class); + if (ret) + return ret; + + register_hotcpu_notifier(&cpuidle_cpu_notifier); + + ret = sysdev_driver_register(&cpu_sysdev_class, &cpuidle_sysdev_driver); + + if (ret) { + cpuidle_remove_class_sysfs(&cpu_sysdev_class); + printk(KERN_ERR "cpuidle: failed to initialize\n"); + return ret; + } + + latency_notifier_init(&cpuidle_latency_notifier); + + return 0; +} + +core_initcall(cpuidle_init); diff --git a/drivers/cpuidle/cpuidle.h b/drivers/cpuidle/cpuidle.h new file mode 100644 index 0000000..8bbc090 --- /dev/null +++ b/drivers/cpuidle/cpuidle.h @@ -0,0 +1,50 @@ +/* + * cpuidle.h - The internal header file + */ + +#ifndef __DRIVER_CPUIDLE_H +#define __DRIVER_CPUIDLE_H + +#include + +/* For internal use only */ +extern struct cpuidle_governor *cpuidle_curr_governor; +extern struct cpuidle_driver *cpuidle_curr_driver; +extern struct list_head cpuidle_drivers; +extern struct list_head cpuidle_governors; +extern struct list_head cpuidle_detected_devices; +extern struct mutex cpuidle_lock; + +/* idle loop */ +extern void cpuidle_install_idle_handler(void); +extern void cpuidle_uninstall_idle_handler(void); +extern void cpuidle_rescan_device(struct cpuidle_device *dev); + +/* drivers */ +extern int cpuidle_attach_driver(struct cpuidle_device *dev); +extern void cpuidle_detach_driver(struct cpuidle_device *dev); +extern int cpuidle_switch_driver(struct cpuidle_driver *drv); + +/* governors */ +extern int cpuidle_attach_governor(struct cpuidle_device *dev); +extern void cpuidle_detach_governor(struct cpuidle_device *dev); +extern int cpuidle_switch_governor(struct cpuidle_governor *gov); + +/* sysfs */ +extern int cpuidle_add_class_sysfs(struct sysdev_class *cls); +extern void cpuidle_remove_class_sysfs(struct sysdev_class *cls); +extern int cpuidle_add_driver_sysfs(struct cpuidle_device *device); +extern void cpuidle_remove_driver_sysfs(struct cpuidle_device *device); +extern int cpuidle_add_sysfs(struct sys_device *sysdev); +extern void cpuidle_remove_sysfs(struct sys_device *sysdev); + +/** + * cpuidle_device_can_idle - determines if a CPU can utilize the idle loop + * @dev: the target CPU + */ +static inline int cpuidle_device_can_idle(struct cpuidle_device *dev) +{ + return (dev->status == CPUIDLE_STATUS_DOIDLE); +} + +#endif /* __DRIVER_CPUIDLE_H */ diff --git a/drivers/cpuidle/driver.c b/drivers/cpuidle/driver.c new file mode 100644 index 0000000..20978ba --- /dev/null +++ b/drivers/cpuidle/driver.c @@ -0,0 +1,276 @@ +/* + * driver.c - driver support + * + * (C) 2006-2007 Venkatesh Pallipadi + * Shaohua Li + * Adam Belay + * + * This code is licenced under the GPL. + */ + +#include +#include +#include + +#include "cpuidle.h" + +LIST_HEAD(cpuidle_drivers); +struct cpuidle_driver *cpuidle_curr_driver; + + +/** + * cpuidle_attach_driver - attaches a driver to a CPU + * @dev: the target CPU + * + * Must be called with cpuidle_lock aquired. + */ +int cpuidle_attach_driver(struct cpuidle_device *dev) +{ + int ret; + + if (dev->status & CPUIDLE_STATUS_DRIVER_ATTACHED) + return -EIO; + + if (!try_module_get(cpuidle_curr_driver->owner)) + return -EINVAL; + + ret = cpuidle_curr_driver->init(dev); + if (ret) { + module_put(cpuidle_curr_driver->owner); + printk(KERN_INFO "cpuidle: driver %s failed to attach to " + "cpu %d\n", cpuidle_curr_driver->name, dev->cpu); + } else { + if (dev->status & CPUIDLE_STATUS_GOVERNOR_ATTACHED) + cpuidle_rescan_device(dev); + smp_wmb(); + dev->status |= CPUIDLE_STATUS_DRIVER_ATTACHED; + cpuidle_add_driver_sysfs(dev); + } + + return ret; +} + +/** + * cpuidle_detach_govenor - detaches a driver from a CPU + * @dev: the target CPU + * + * Must be called with cpuidle_lock aquired. + */ +void cpuidle_detach_driver(struct cpuidle_device *dev) +{ + if (dev->status & CPUIDLE_STATUS_DRIVER_ATTACHED) { + cpuidle_remove_driver_sysfs(dev); + dev->status &= ~CPUIDLE_STATUS_DRIVER_ATTACHED; + if (cpuidle_curr_driver->exit) + cpuidle_curr_driver->exit(dev); + module_put(cpuidle_curr_driver->owner); + } +} + +/** + * __cpuidle_find_driver - finds a driver of the specified name + * @str: the name + * + * Must be called with cpuidle_lock aquired. + */ +static struct cpuidle_driver * __cpuidle_find_driver(const char *str) +{ + struct cpuidle_driver *drv; + + list_for_each_entry(drv, &cpuidle_drivers, driver_list) + if (!strnicmp(str, drv->name, CPUIDLE_NAME_LEN)) + return drv; + + return NULL; +} + +/** + * cpuidle_switch_driver - changes the driver + * @drv: the new target driver + * + * NOTE: "drv" can be NULL to specify disabled + * Must be called with cpuidle_lock aquired. + */ +int cpuidle_switch_driver(struct cpuidle_driver *drv) +{ + struct cpuidle_device *dev; + + if (drv == cpuidle_curr_driver) + return -EINVAL; + + cpuidle_uninstall_idle_handler(); + + if (cpuidle_curr_driver) + list_for_each_entry(dev, &cpuidle_detected_devices, device_list) + cpuidle_detach_driver(dev); + + cpuidle_curr_driver = drv; + + if (drv) { + int ret = 1; + list_for_each_entry(dev, &cpuidle_detected_devices, device_list) + if (cpuidle_attach_driver(dev) == 0) + ret = 0; + + /* If attach on all devices fail, switch to NULL driver */ + if (ret) + cpuidle_curr_driver = NULL; + + if (cpuidle_curr_driver && cpuidle_curr_governor) { + printk(KERN_INFO "cpuidle: using driver %s\n", + drv->name); + cpuidle_install_idle_handler(); + } + } + + return 0; +} + +/** + * cpuidle_register_driver - registers a driver + * @drv: the driver + */ +int cpuidle_register_driver(struct cpuidle_driver *drv) +{ + int ret = -EEXIST; + + if (!drv || !drv->init) + return -EINVAL; + + mutex_lock(&cpuidle_lock); + if (__cpuidle_find_driver(drv->name) == NULL) { + ret = 0; + list_add_tail(&drv->driver_list, &cpuidle_drivers); + if (!cpuidle_curr_driver) + cpuidle_switch_driver(drv); + } + mutex_unlock(&cpuidle_lock); + + return ret; +} + +EXPORT_SYMBOL_GPL(cpuidle_register_driver); + +/** + * cpuidle_unregister_driver - unregisters a driver + * @drv: the driver + */ +void cpuidle_unregister_driver(struct cpuidle_driver *drv) +{ + if (!drv) + return; + + mutex_lock(&cpuidle_lock); + if (drv == cpuidle_curr_driver) + cpuidle_switch_driver(NULL); + list_del(&drv->driver_list); + mutex_unlock(&cpuidle_lock); +} + +EXPORT_SYMBOL_GPL(cpuidle_unregister_driver); + +static void __cpuidle_force_redetect(struct cpuidle_device *dev) +{ + cpuidle_remove_driver_sysfs(dev); + cpuidle_curr_driver->redetect(dev); + cpuidle_add_driver_sysfs(dev); +} + +/** + * cpuidle_force_redetect - redetects the idle states of a CPU + * + * @dev: the CPU to redetect + * @drv: the target driver + * + * Generally, the driver will call this when the supported states set has + * changed. (e.g. as the result of an ACPI transition to battery power) + */ +int cpuidle_force_redetect(struct cpuidle_device *dev, + struct cpuidle_driver *drv) +{ + int uninstalled = 0; + + mutex_lock(&cpuidle_lock); + + if (drv != cpuidle_curr_driver) { + mutex_unlock(&cpuidle_lock); + return 0; + } + + if (!(dev->status & CPUIDLE_STATUS_DRIVER_ATTACHED) || + !cpuidle_curr_driver->redetect) { + mutex_unlock(&cpuidle_lock); + return -EIO; + } + + if (cpuidle_device_can_idle(dev)) { + uninstalled = 1; + cpuidle_uninstall_idle_handler(); + } + + __cpuidle_force_redetect(dev); + + if (cpuidle_device_can_idle(dev)) { + cpuidle_rescan_device(dev); + cpuidle_install_idle_handler(); + } + + /* other devices are still ok */ + if (uninstalled) + cpuidle_install_idle_handler(); + + mutex_unlock(&cpuidle_lock); + + return 0; +} + +EXPORT_SYMBOL_GPL(cpuidle_force_redetect); + +/** + * cpuidle_force_redetect_devices - redetects the idle states of all CPUs + * + * @drv: the target driver + * + * Generally, the driver will call this when the supported states set has + * changed. (e.g. as the result of an ACPI transition to battery power) + */ +int cpuidle_force_redetect_devices(struct cpuidle_driver *drv) +{ + struct cpuidle_device *dev; + int ret = 0; + + mutex_lock(&cpuidle_lock); + + if (drv != cpuidle_curr_driver) + goto out; + + if (!cpuidle_curr_driver->redetect) { + ret = -EIO; + goto out; + } + + cpuidle_uninstall_idle_handler(); + + list_for_each_entry(dev, &cpuidle_detected_devices, device_list) + __cpuidle_force_redetect(dev); + + cpuidle_install_idle_handler(); +out: + mutex_unlock(&cpuidle_lock); + return ret; +} + +EXPORT_SYMBOL_GPL(cpuidle_force_redetect_devices); + +/** + * cpuidle_get_bm_activity - determines if BM activity has occured + */ +int cpuidle_get_bm_activity(void) +{ + if (cpuidle_curr_driver->bm_check) + return cpuidle_curr_driver->bm_check(); + else + return 0; +} +EXPORT_SYMBOL_GPL(cpuidle_get_bm_activity); + diff --git a/drivers/cpuidle/governor.c b/drivers/cpuidle/governor.c new file mode 100644 index 0000000..fa637fa --- /dev/null +++ b/drivers/cpuidle/governor.c @@ -0,0 +1,160 @@ +/* + * governor.c - governor support + * + * (C) 2006-2007 Venkatesh Pallipadi + * Shaohua Li + * Adam Belay + * + * This code is licenced under the GPL. + */ + +#include +#include +#include + +#include "cpuidle.h" + +LIST_HEAD(cpuidle_governors); +struct cpuidle_governor *cpuidle_curr_governor; + + +/** + * cpuidle_attach_governor - attaches a governor to a CPU + * @dev: the target CPU + * + * Must be called with cpuidle_lock aquired. + */ +int cpuidle_attach_governor(struct cpuidle_device *dev) +{ + int ret = 0; + + if(dev->status & CPUIDLE_STATUS_GOVERNOR_ATTACHED) + return -EIO; + + if (!try_module_get(cpuidle_curr_governor->owner)) + return -EINVAL; + + if (cpuidle_curr_governor->init) + ret = cpuidle_curr_governor->init(dev); + if (ret) { + module_put(cpuidle_curr_governor->owner); + printk(KERN_ERR "cpuidle: governor %s failed to attach to cpu %d\n", + cpuidle_curr_governor->name, dev->cpu); + } else { + if (dev->status & CPUIDLE_STATUS_DRIVER_ATTACHED) + cpuidle_rescan_device(dev); + smp_wmb(); + dev->status |= CPUIDLE_STATUS_GOVERNOR_ATTACHED; + } + + return ret; +} + +/** + * cpuidle_detach_govenor - detaches a governor from a CPU + * @dev: the target CPU + * + * Must be called with cpuidle_lock aquired. + */ +void cpuidle_detach_governor(struct cpuidle_device *dev) +{ + if (dev->status & CPUIDLE_STATUS_GOVERNOR_ATTACHED) { + dev->status &= ~CPUIDLE_STATUS_GOVERNOR_ATTACHED; + if (cpuidle_curr_governor->exit) + cpuidle_curr_governor->exit(dev); + module_put(cpuidle_curr_governor->owner); + } +} + +/** + * __cpuidle_find_governor - finds a governor of the specified name + * @str: the name + * + * Must be called with cpuidle_lock aquired. + */ +static struct cpuidle_governor * __cpuidle_find_governor(const char *str) +{ + struct cpuidle_governor *gov; + + list_for_each_entry(gov, &cpuidle_governors, governor_list) + if (!strnicmp(str, gov->name, CPUIDLE_NAME_LEN)) + return gov; + + return NULL; +} + +/** + * cpuidle_switch_governor - changes the governor + * @gov: the new target governor + * + * NOTE: "gov" can be NULL to specify disabled + * Must be called with cpuidle_lock aquired. + */ +int cpuidle_switch_governor(struct cpuidle_governor *gov) +{ + struct cpuidle_device *dev; + + if (gov == cpuidle_curr_governor) + return -EINVAL; + + cpuidle_uninstall_idle_handler(); + + if (cpuidle_curr_governor) + list_for_each_entry(dev, &cpuidle_detected_devices, device_list) + cpuidle_detach_governor(dev); + + cpuidle_curr_governor = gov; + + if (gov) { + list_for_each_entry(dev, &cpuidle_detected_devices, device_list) + cpuidle_attach_governor(dev); + if (cpuidle_curr_driver) + cpuidle_install_idle_handler(); + printk(KERN_INFO "cpuidle: using governor %s\n", gov->name); + } + + return 0; +} + +/** + * cpuidle_register_governor - registers a governor + * @gov: the governor + */ +int cpuidle_register_governor(struct cpuidle_governor *gov) +{ + int ret = -EEXIST; + + if (!gov || !gov->select_state) + return -EINVAL; + + mutex_lock(&cpuidle_lock); + if (__cpuidle_find_governor(gov->name) == NULL) { + ret = 0; + list_add_tail(&gov->governor_list, &cpuidle_governors); + if (!cpuidle_curr_governor) + cpuidle_switch_governor(gov); + } + mutex_unlock(&cpuidle_lock); + + return ret; +} + +EXPORT_SYMBOL_GPL(cpuidle_register_governor); + +/** + * cpuidle_unregister_governor - unregisters a governor + * @gov: the governor + */ +void cpuidle_unregister_governor(struct cpuidle_governor *gov) +{ + if (!gov) + return; + + mutex_lock(&cpuidle_lock); + if (gov == cpuidle_curr_governor) + cpuidle_switch_governor(NULL); + list_del(&gov->governor_list); + mutex_unlock(&cpuidle_lock); +} + +EXPORT_SYMBOL_GPL(cpuidle_unregister_governor); diff --git a/drivers/cpuidle/governors/Makefile b/drivers/cpuidle/governors/Makefile new file mode 100644 index 0000000..becf470 --- /dev/null +++ b/drivers/cpuidle/governors/Makefile @@ -0,0 +1,5 @@ +# +# Makefile for cpuidle governors. +# + +obj-$(CONFIG_CPU_IDLE_GOV_LADDER) += ladder.o diff --git a/drivers/cpuidle/governors/ladder.c b/drivers/cpuidle/governors/ladder.c new file mode 100644 index 0000000..dbdb2a4 --- /dev/null +++ b/drivers/cpuidle/governors/ladder.c @@ -0,0 +1,227 @@ +/* + * ladder.c - the residency ladder algorithm + * + * Copyright (C) 2001, 2002 Andy Grover + * Copyright (C) 2001, 2002 Paul Diefenbaugh + * Copyright (C) 2004, 2005 Dominik Brodowski + * + * (C) 2006-2007 Venkatesh Pallipadi + * Shaohua Li + * Adam Belay + * + * This code is licenced under the GPL. + */ + +#include +#include +#include +#include +#include + +#include +#include + +#define PROMOTION_COUNT 4 +#define DEMOTION_COUNT 1 + +/* + * bm_history -- bit-mask with a bit per jiffy of bus-master activity + * 1000 HZ: 0xFFFFFFFF: 32 jiffies = 32ms + * 800 HZ: 0xFFFFFFFF: 32 jiffies = 40ms + * 100 HZ: 0x0000000F: 4 jiffies = 40ms + * reduce history for more aggressive entry into C3 + */ +static unsigned int bm_history __read_mostly = + (HZ >= 800 ? 0xFFFFFFFF : ((1U << (HZ / 25)) - 1)); +module_param(bm_history, uint, 0644); + +struct ladder_device_state { + struct { + u32 promotion_count; + u32 demotion_count; + u32 promotion_time; + u32 demotion_time; + u32 bm; + } threshold; + struct { + int promotion_count; + int demotion_count; + } stats; +}; + +struct ladder_device { + struct ladder_device_state states[CPUIDLE_STATE_MAX]; + unsigned int bm_check:1; + unsigned long bm_check_timestamp; + unsigned long bm_activity; /* FIXME: bm activity should be global */ + int last_state_idx; +}; + +/** + * ladder_do_selection - prepares private data for a state change + * @ldev: the ladder device + * @old_idx: the current state index + * @new_idx: the new target state index + */ +static inline void ladder_do_selection(struct ladder_device *ldev, + int old_idx, int new_idx) +{ + ldev->states[old_idx].stats.promotion_count = 0; + ldev->states[old_idx].stats.demotion_count = 0; + ldev->last_state_idx = new_idx; +} + +/** + * ladder_select_state - selects the next state to enter + * @dev: the CPU + */ +static int ladder_select_state(struct cpuidle_device *dev) +{ + struct ladder_device *ldev = dev->governor_data; + struct ladder_device_state *last_state; + int last_residency, last_idx = ldev->last_state_idx; + + if (unlikely(!ldev)) + return 0; + + last_state = &ldev->states[last_idx]; + + /* demote if within BM threshold */ + if (ldev->bm_check) { + unsigned long diff; + + diff = jiffies - ldev->bm_check_timestamp; + if (diff > 31) + diff = 31; + + ldev->bm_activity <<= diff; + if (cpuidle_get_bm_activity()) + ldev->bm_activity |= ((1 << diff) - 1); + + ldev->bm_check_timestamp = jiffies; + if ((last_idx > 0) && + (last_state->threshold.bm & ldev->bm_activity)) { + ladder_do_selection(ldev, last_idx, last_idx - 1); + return last_idx - 1; + } + } + + if (dev->states[last_idx].flags & CPUIDLE_FLAG_TIME_VALID) + last_residency = cpuidle_get_last_residency(dev) - dev->states[last_idx].exit_latency; + else + last_residency = last_state->threshold.promotion_time + 1; + + /* consider promotion */ + if (last_idx < dev->state_count - 1 && + last_residency > last_state->threshold.promotion_time && + dev->states[last_idx + 1].exit_latency <= system_latency_constraint()) { + last_state->stats.promotion_count++; + last_state->stats.demotion_count = 0; + if (last_state->stats.promotion_count >= last_state->threshold.promotion_count) { + ladder_do_selection(ldev, last_idx, last_idx + 1); + return last_idx + 1; + } + } + + /* consider demotion */ + if (last_idx > 0 && + last_residency < last_state->threshold.demotion_time) { + last_state->stats.demotion_count++; + last_state->stats.promotion_count = 0; + if (last_state->stats.demotion_count >= last_state->threshold.demotion_count) { + ladder_do_selection(ldev, last_idx, last_idx - 1); + return last_idx - 1; + } + } + + /* otherwise remain at the current state */ + return last_idx; +} + +/** + * ladder_scan_device - scans a CPU's states and does setup + * @dev: the CPU + */ +static void ladder_scan_device(struct cpuidle_device *dev) +{ + int i, bm_check = 0; + struct ladder_device *ldev = dev->governor_data; + struct ladder_device_state *lstate; + struct cpuidle_state *state; + + ldev->last_state_idx = 0; + ldev->bm_check_timestamp = 0; + ldev->bm_activity = 0; + + for (i = 0; i < dev->state_count; i++) { + state = &dev->states[i]; + lstate = &ldev->states[i]; + + lstate->stats.promotion_count = 0; + lstate->stats.demotion_count = 0; + + lstate->threshold.promotion_count = PROMOTION_COUNT; + lstate->threshold.demotion_count = DEMOTION_COUNT; + + if (i < dev->state_count - 1) + lstate->threshold.promotion_time = state->exit_latency; + if (i > 0) + lstate->threshold.demotion_time = state->exit_latency; + if (state->flags & CPUIDLE_FLAG_CHECK_BM) { + lstate->threshold.bm = bm_history; + bm_check = 1; + } else + lstate->threshold.bm = 0; + } + + ldev->bm_check = bm_check; +} + +/** + * ladder_init_device - initializes a CPU-instance + * @dev: the CPU + */ +static int ladder_init_device(struct cpuidle_device *dev) +{ + dev->governor_data = kmalloc(sizeof(struct ladder_device), GFP_KERNEL); + + return !dev->governor_data; +} + +/** + * ladder_exit_device - exits a CPU-instance + * @dev: the CPU + */ +static void ladder_exit_device(struct cpuidle_device *dev) +{ + kfree(dev->governor_data); +} + +static struct cpuidle_governor ladder_governor = { + .name = "ladder", + .init = ladder_init_device, + .exit = ladder_exit_device, + .scan = ladder_scan_device, + .select_state = ladder_select_state, + .owner = THIS_MODULE, +}; + +/** + * init_ladder - initializes the governor + */ +static int __init init_ladder(void) +{ + return cpuidle_register_governor(&ladder_governor); +} + +/** + * exit_ladder - exits the governor + */ +static void __exit exit_ladder(void) +{ + cpuidle_unregister_governor(&ladder_governor); +} + +MODULE_LICENSE("GPL"); +module_init(init_ladder); +module_exit(exit_ladder); diff --git a/drivers/cpuidle/sysfs.c b/drivers/cpuidle/sysfs.c new file mode 100644 index 0000000..5010762 --- /dev/null +++ b/drivers/cpuidle/sysfs.c @@ -0,0 +1,373 @@ +/* + * sysfs.c - sysfs support + * + * (C) 2006-2007 Shaohua Li + * + * This code is licenced under the GPL. + */ + +#include +#include +#include +#include + +#include "cpuidle.h" + +static ssize_t show_available_drivers(struct sys_device *dev, char *buf) +{ + ssize_t i = 0; + struct cpuidle_driver *tmp; + + mutex_lock(&cpuidle_lock); + list_for_each_entry(tmp, &cpuidle_drivers, driver_list) { + if (i >= (ssize_t)((PAGE_SIZE/sizeof(char)) - CPUIDLE_NAME_LEN - 2)) + goto out; + i += scnprintf(&buf[i], CPUIDLE_NAME_LEN, "%s ", tmp->name); + } +out: + i+= sprintf(&buf[i], "\n"); + mutex_unlock(&cpuidle_lock); + return i; +} + +static ssize_t show_available_governors(struct sys_device *dev, char *buf) +{ + ssize_t i = 0; + struct cpuidle_governor *tmp; + + mutex_lock(&cpuidle_lock); + list_for_each_entry(tmp, &cpuidle_governors, governor_list) { + if (i >= (ssize_t)((PAGE_SIZE/sizeof(char)) - CPUIDLE_NAME_LEN - 2)) + goto out; + i += scnprintf(&buf[i], CPUIDLE_NAME_LEN, "%s ", tmp->name); + } + if (list_empty(&cpuidle_governors)) + i+= sprintf(&buf[i], "no governors"); +out: + i+= sprintf(&buf[i], "\n"); + mutex_unlock(&cpuidle_lock); + return i; +} + +static ssize_t show_current_driver(struct sys_device *dev, char *buf) +{ + ssize_t ret; + + mutex_lock(&cpuidle_lock); + ret = sprintf(buf, "%s\n", cpuidle_curr_driver->name); + mutex_unlock(&cpuidle_lock); + return ret; +} + +static ssize_t store_current_driver(struct sys_device *dev, + const char *buf, size_t count) +{ + char str[CPUIDLE_NAME_LEN]; + int len = count; + struct cpuidle_driver *tmp, *found = NULL; + + if (len > CPUIDLE_NAME_LEN) + len = CPUIDLE_NAME_LEN; + + if (sscanf(buf, "%s", str) != 1) + return -EINVAL; + + mutex_lock(&cpuidle_lock); + list_for_each_entry(tmp, &cpuidle_drivers, driver_list) { + if (strncmp(tmp->name, str, CPUIDLE_NAME_LEN) == 0) { + found = tmp; + break; + } + } + if (found) + cpuidle_switch_driver(found); + mutex_unlock(&cpuidle_lock); + + return count; +} + +static ssize_t show_current_governor(struct sys_device *dev, char *buf) +{ + ssize_t i; + + mutex_lock(&cpuidle_lock); + if (cpuidle_curr_governor) + i = sprintf(buf, "%s\n", cpuidle_curr_governor->name); + else + i = sprintf(buf, "no governor\n"); + mutex_unlock(&cpuidle_lock); + + return i; +} + +static ssize_t store_current_governor(struct sys_device *dev, + const char *buf, size_t count) +{ + char str[CPUIDLE_NAME_LEN]; + int len = count; + struct cpuidle_governor *tmp, *found = NULL; + + if (len > CPUIDLE_NAME_LEN) + len = CPUIDLE_NAME_LEN; + + if (sscanf(buf, "%s", str) != 1) + return -EINVAL; + + mutex_lock(&cpuidle_lock); + list_for_each_entry(tmp, &cpuidle_governors, governor_list) { + if (strncmp(tmp->name, str, CPUIDLE_NAME_LEN) == 0) { + found = tmp; + break; + } + } + if (found) + cpuidle_switch_governor(found); + mutex_unlock(&cpuidle_lock); + + return count; +} + +static SYSDEV_ATTR(available_drivers, 0444, show_available_drivers, NULL); +static SYSDEV_ATTR(available_governors, 0444, show_available_governors, NULL); +static SYSDEV_ATTR(current_driver, 0644, show_current_driver, + store_current_driver); +static SYSDEV_ATTR(current_governor, 0644, show_current_governor, + store_current_governor); + +static struct attribute *cpuclass_default_attrs[] = { + &attr_available_drivers.attr, + &attr_available_governors.attr, + &attr_current_driver.attr, + &attr_current_governor.attr, + NULL +}; + +static struct attribute_group cpuclass_attr_group = { + .attrs = cpuclass_default_attrs, + .name = "cpuidle", +}; + +/** + * cpuidle_add_class_sysfs - add CPU global sysfs attributes + */ +int cpuidle_add_class_sysfs(struct sysdev_class *cls) +{ + return sysfs_create_group(&cls->kset.kobj, &cpuclass_attr_group); +} + +/** + * cpuidle_remove_class_sysfs - remove CPU global sysfs attributes + */ +void cpuidle_remove_class_sysfs(struct sysdev_class *cls) +{ + sysfs_remove_group(&cls->kset.kobj, &cpuclass_attr_group); +} + +struct cpuidle_attr { + struct attribute attr; + ssize_t (*show)(struct cpuidle_device *, char *); + ssize_t (*store)(struct cpuidle_device *, const char *, size_t count); +}; + +#define define_one_ro(_name, show) \ + static struct cpuidle_attr attr_##_name = __ATTR(_name, 0444, show, NULL) +#define define_one_rw(_name, show, store) \ + static struct cpuidle_attr attr_##_name = __ATTR(_name, 0644, show, store) + +#define kobj_to_cpuidledev(k) container_of(k, struct cpuidle_device, kobj) +#define attr_to_cpuidleattr(a) container_of(a, struct cpuidle_attr, attr) +static ssize_t cpuidle_show(struct kobject * kobj, struct attribute * attr ,char * buf) +{ + int ret = -EIO; + struct cpuidle_device *dev = kobj_to_cpuidledev(kobj); + struct cpuidle_attr * cattr = attr_to_cpuidleattr(attr); + + if (cattr->show) { + mutex_lock(&cpuidle_lock); + ret = cattr->show(dev, buf); + mutex_unlock(&cpuidle_lock); + } + return ret; +} + +static ssize_t cpuidle_store(struct kobject * kobj, struct attribute * attr, + const char * buf, size_t count) +{ + int ret = -EIO; + struct cpuidle_device *dev = kobj_to_cpuidledev(kobj); + struct cpuidle_attr * cattr = attr_to_cpuidleattr(attr); + + if (cattr->store) { + mutex_lock(&cpuidle_lock); + ret = cattr->store(dev, buf, count); + mutex_unlock(&cpuidle_lock); + } + return ret; +} + +static struct sysfs_ops cpuidle_sysfs_ops = { + .show = cpuidle_show, + .store = cpuidle_store, +}; + +static void cpuidle_sysfs_release(struct kobject *kobj) +{ + struct cpuidle_device *dev = kobj_to_cpuidledev(kobj); + + complete(&dev->kobj_unregister); +} + +static struct kobj_type ktype_cpuidle = { + .sysfs_ops = &cpuidle_sysfs_ops, + .release = cpuidle_sysfs_release, +}; + +struct cpuidle_state_attr { + struct attribute attr; + ssize_t (*show)(struct cpuidle_state *, char *); + ssize_t (*store)(struct cpuidle_state *, const char *, size_t); +}; + +#define define_one_state_ro(_name, show) \ +static struct cpuidle_state_attr attr_##_name = __ATTR(_name, 0444, show, NULL) + +#define define_show_state_function(_name) \ +static ssize_t show_state_##_name(struct cpuidle_state *state, char *buf) \ +{ \ + return sprintf(buf, "%d\n", state->_name);\ +} + +define_show_state_function(exit_latency) +define_show_state_function(power_usage) +define_show_state_function(usage) +define_show_state_function(time) +define_one_state_ro(latency, show_state_exit_latency); +define_one_state_ro(power, show_state_power_usage); +define_one_state_ro(usage, show_state_usage); +define_one_state_ro(time, show_state_time); + +static struct attribute *cpuidle_state_default_attrs[] = { + &attr_latency.attr, + &attr_power.attr, + &attr_usage.attr, + &attr_time.attr, + NULL +}; + +#define kobj_to_state_obj(k) container_of(k, struct cpuidle_state_kobj, kobj) +#define kobj_to_state(k) (kobj_to_state_obj(k)->state) +#define attr_to_stateattr(a) container_of(a, struct cpuidle_state_attr, attr) +static ssize_t cpuidle_state_show(struct kobject * kobj, + struct attribute * attr ,char * buf) +{ + int ret = -EIO; + struct cpuidle_state *state = kobj_to_state(kobj); + struct cpuidle_state_attr * cattr = attr_to_stateattr(attr); + + if (cattr->show) + ret = cattr->show(state, buf); + + return ret; +} + +static struct sysfs_ops cpuidle_state_sysfs_ops = { + .show = cpuidle_state_show, +}; + +static void cpuidle_state_sysfs_release(struct kobject *kobj) +{ + struct cpuidle_state_kobj *state_obj = kobj_to_state_obj(kobj); + + complete(&state_obj->kobj_unregister); +} + +static struct kobj_type ktype_state_cpuidle = { + .sysfs_ops = &cpuidle_state_sysfs_ops, + .default_attrs = cpuidle_state_default_attrs, + .release = cpuidle_state_sysfs_release, +}; + +static void inline cpuidle_free_state_kobj(struct cpuidle_device *device, int i) +{ + kobject_unregister(&device->kobjs[i]->kobj); + wait_for_completion(&device->kobjs[i]->kobj_unregister); + kfree(device->kobjs[i]); + device->kobjs[i] = NULL; +} + +/** + * cpuidle_add_driver_sysfs - adds driver-specific sysfs attributes + * @device: the target device + */ +int cpuidle_add_driver_sysfs(struct cpuidle_device *device) +{ + int i, ret; + struct cpuidle_state_kobj *kobj; + + /* state statistics */ + for (i = 0; i < device->state_count; i++) { + kobj = kzalloc(sizeof(struct cpuidle_state_kobj), GFP_KERNEL); + if (!kobj) + goto error_state; + kobj->state = &device->states[i]; + init_completion(&kobj->kobj_unregister); + + kobj->kobj.parent = &device->kobj; + kobj->kobj.ktype = &ktype_state_cpuidle; + kobject_set_name(&kobj->kobj, "state%d", i); + ret = kobject_register(&kobj->kobj); + if (ret) { + kfree(kobj); + goto error_state; + } + device->kobjs[i] = kobj; + } + + return 0; + +error_state: + for (i = i - 1; i >= 0; i--) + cpuidle_free_state_kobj(device, i); + return ret; +} + +/** + * cpuidle_remove_driver_sysfs - removes driver-specific sysfs attributes + * @device: the target device + */ +void cpuidle_remove_driver_sysfs(struct cpuidle_device *device) +{ + int i; + + for (i = 0; i < device->state_count; i++) + cpuidle_free_state_kobj(device, i); +} + +/** + * cpuidle_add_sysfs - creates a sysfs instance for the target device + * @sysdev: the target device + */ +int cpuidle_add_sysfs(struct sys_device *sysdev) +{ + int cpu = sysdev->id; + struct cpuidle_device *dev; + + dev = per_cpu(cpuidle_devices, cpu); + dev->kobj.parent = &sysdev->kobj; + dev->kobj.ktype = &ktype_cpuidle; + kobject_set_name(&dev->kobj, "%s", "cpuidle"); + return kobject_register(&dev->kobj); +} + +/** + * cpuidle_remove_sysfs - deletes a sysfs instance on the target device + * @sysdev: the target device + */ +void cpuidle_remove_sysfs(struct sys_device *sysdev) +{ + int cpu = sysdev->id; + struct cpuidle_device *dev; + + dev = per_cpu(cpuidle_devices, cpu); + kobject_unregister(&dev->kobj); +} diff --git a/include/acpi/processor.h b/include/acpi/processor.h index 916c010..70f3209 100644 --- a/include/acpi/processor.h +++ b/include/acpi/processor.h @@ -161,6 +161,7 @@ struct acpi_processor_flags { u8 bm_check:1; u8 has_cst:1; u8 power_setup_done:1; + u8 bm_rld_set:1; }; struct acpi_processor { @@ -275,6 +276,8 @@ int acpi_processor_power_init(struct acp int acpi_processor_cst_has_changed(struct acpi_processor *pr); int acpi_processor_power_exit(struct acpi_processor *pr, struct acpi_device *device); +extern struct cpuidle_driver acpi_idle_driver; +void acpi_max_cstate_changed(void); /* in processor_thermal.c */ int acpi_processor_get_limit_info(struct acpi_processor *pr); diff --git a/include/linux/acpi.h b/include/linux/acpi.h index 8bcfaa4..0e28dc1 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -205,11 +205,8 @@ static inline unsigned int acpi_get_csta { return max_cstate; } -static inline void acpi_set_cstate_limit(unsigned int new_limit) -{ - max_cstate = new_limit; - return; -} +extern void (*acpi_do_set_cstate_limit)(void); +extern void acpi_set_cstate_limit(unsigned int new_limit); #else static inline unsigned int acpi_get_cstate_limit(void) { return 0; } static inline void acpi_set_cstate_limit(unsigned int new_limit) { return; } diff --git a/include/linux/cpuidle.h b/include/linux/cpuidle.h new file mode 100644 index 0000000..885d5de --- /dev/null +++ b/include/linux/cpuidle.h @@ -0,0 +1,189 @@ +/* + * cpuidle.h - a generic framework for CPU idle power management + * + * (C) 2007 Venkatesh Pallipadi + * Shaohua Li + * Adam Belay + * + * This code is licenced under the GPL. + */ + +#ifndef _LINUX_CPUIDLE_H +#define _LINUX_CPUIDLE_H + +#include +#include +#include +#include +#include + +#define CPUIDLE_STATE_MAX 8 +#define CPUIDLE_NAME_LEN 16 + +struct cpuidle_device; + + +/**************************** + * CPUIDLE DEVICE INTERFACE * + ****************************/ + +struct cpuidle_state { + char name[CPUIDLE_NAME_LEN]; + void *driver_data; + + unsigned int flags; + unsigned int exit_latency; /* in US */ + unsigned int power_usage; /* in mW */ + unsigned int target_residency; /* in US */ + + unsigned int usage; + unsigned int time; /* in US */ + + int (*enter) (struct cpuidle_device *dev, + struct cpuidle_state *state); +}; + +/* Idle State Flags */ +#define CPUIDLE_FLAG_TIME_VALID (0x01) /* is residency time measurable? */ +#define CPUIDLE_FLAG_CHECK_BM (0x02) /* BM activity will exit state */ +#define CPUIDLE_FLAG_SHALLOW (0x10) /* low latency, minimal savings */ +#define CPUIDLE_FLAG_BALANCED (0x20) /* medium latency, moderate savings */ +#define CPUIDLE_FLAG_DEEP (0x40) /* high latency, large savings */ + +#define CPUIDLE_DRIVER_FLAGS_MASK (0xFFFF0000) + +/** + * cpuidle_get_statedata - retrieves private driver state data + * @state: the state + */ +static inline void * cpuidle_get_statedata(struct cpuidle_state *state) +{ + return state->driver_data; +} + +/** + * cpuidle_set_statedata - stores private driver state data + * @state: the state + * @data: the private data + */ +static inline void +cpuidle_set_statedata(struct cpuidle_state *state, void *data) +{ + state->driver_data = data; +} + +struct cpuidle_state_kobj { + struct cpuidle_state *state; + struct completion kobj_unregister; + struct kobject kobj; +}; + +struct cpuidle_device { + unsigned int status; + int cpu; + + int last_residency; + int state_count; + struct cpuidle_state states[CPUIDLE_STATE_MAX]; + struct cpuidle_state_kobj *kobjs[CPUIDLE_STATE_MAX]; + struct cpuidle_state *last_state; + + struct list_head device_list; + struct kobject kobj; + struct completion kobj_unregister; + void *governor_data; +}; + +DECLARE_PER_CPU(struct cpuidle_device *, cpuidle_devices); + +/* Device Status Flags */ +#define CPUIDLE_STATUS_DETECTED (0x1) +#define CPUIDLE_STATUS_DRIVER_ATTACHED (0x2) +#define CPUIDLE_STATUS_GOVERNOR_ATTACHED (0x4) +#define CPUIDLE_STATUS_DOIDLE (CPUIDLE_STATUS_DETECTED | \ + CPUIDLE_STATUS_DRIVER_ATTACHED | \ + CPUIDLE_STATUS_GOVERNOR_ATTACHED) + +/** + * cpuidle_get_last_residency - retrieves the last state's residency time + * @dev: the target CPU + * + * NOTE: this value is invalid if CPUIDLE_FLAG_TIME_VALID isn't set + */ +static inline int cpuidle_get_last_residency(struct cpuidle_device *dev) +{ + return dev->last_residency; +} + + +/**************************** + * CPUIDLE DRIVER INTERFACE * + ****************************/ + +struct cpuidle_driver { + char name[CPUIDLE_NAME_LEN]; + struct list_head driver_list; + + int (*init) (struct cpuidle_device *dev); + void (*exit) (struct cpuidle_device *dev); + int (*redetect) (struct cpuidle_device *dev); + + int (*bm_check) (void); + + struct module *owner; +}; + +#ifdef CONFIG_CPU_IDLE + +extern int cpuidle_register_driver(struct cpuidle_driver *drv); +extern void cpuidle_unregister_driver(struct cpuidle_driver *drv); +extern int cpuidle_force_redetect(struct cpuidle_device *dev, struct cpuidle_driver *drv); +extern int cpuidle_force_redetect_devices(struct cpuidle_driver *drv); + +#else + +static inline int cpuidle_register_driver(struct cpuidle_driver *drv) +{return 0;} +static inline void cpuidle_unregister_driver(struct cpuidle_driver *drv) { } +static inline int cpuidle_force_redetect(struct cpuidle_device *dev, struct cpuidle_driver *drv) +{return 0;} +static inline int cpuidle_force_redetect_devices(struct cpuidle_driver *drv) +{return 0;} + +#endif + +/****************************** + * CPUIDLE GOVERNOR INTERFACE * + ******************************/ + +struct cpuidle_governor { + char name[CPUIDLE_NAME_LEN]; + struct list_head governor_list; + + int (*init) (struct cpuidle_device *dev); + void (*exit) (struct cpuidle_device *dev); + void (*scan) (struct cpuidle_device *dev); + + void (*prepare_idle) (struct cpuidle_device *dev); + int (*select_state) (struct cpuidle_device *dev); + + struct module *owner; +}; + +#ifdef CONFIG_CPU_IDLE + +extern int cpuidle_register_governor(struct cpuidle_governor *gov); +extern void cpuidle_unregister_governor(struct cpuidle_governor *gov); +extern int cpuidle_get_bm_activity(void); + +#else + +static inline int cpuidle_register_governor(struct cpuidle_governor *gov) +{return 0;} +static inline void cpuidle_unregister_governor(struct cpuidle_governor *gov) { } +static inline int cpuidle_get_bm_activity(void) +{return 0;} + +#endif + +#endif /* _LINUX_CPUIDLE_H */