From 67effe8fff32f60bdf51cba484766ba6003005bb Mon Sep 17 00:00:00 2001 From: Len Brown Date: Thu, 26 Jul 2007 00:50:06 -0400 Subject: [PATCH 01/28] ACPI: add "acpi_no_auto_ssdt" bootparam "acpi_no_auto_ssdt" prevents Linux from automatically loading all the SSDTs listed in the RSDT/XSDT. This is needed for debugging. In particular, it allows a DSDT override to optionally be a DSDT+SSDT override. http://bugzilla.kernel.org/show_bug.cgi?id=3774 Signed-off-by: Len Brown --- Documentation/kernel-parameters.txt | 2 ++ drivers/acpi/tables/tbxface.c | 23 +++++++++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index fb80e9ffea68b..379931e743341 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -166,6 +166,8 @@ and is between 256 and 4096 characters. It is defined in the file acpi_irq_isa= [HW,ACPI] If irq_balance, mark listed IRQs used by ISA Format: ,... + acpi_no_auto_ssdt [HW,ACPI] Disable automatic loading of SSDT + acpi_os_name= [HW,ACPI] Tell ACPI BIOS the name of the OS Format: To spoof as Windows 98: ="Microsoft Windows" diff --git a/drivers/acpi/tables/tbxface.c b/drivers/acpi/tables/tbxface.c index 5b302c4e293f7..a9e3331fee5de 100644 --- a/drivers/acpi/tables/tbxface.c +++ b/drivers/acpi/tables/tbxface.c @@ -52,6 +52,8 @@ ACPI_MODULE_NAME("tbxface") /* Local prototypes */ static acpi_status acpi_tb_load_namespace(void); +static int no_auto_ssdt; + /******************************************************************************* * * FUNCTION: acpi_allocate_root_table @@ -536,6 +538,10 @@ static acpi_status acpi_tb_load_namespace(void) ACPI_INFO((AE_INFO, "Table DSDT replaced by host OS")); acpi_tb_print_table_header(0, table); + + if (no_auto_ssdt == 0) { + printk(KERN_WARNING "ACPI: DSDT override uses original SSDTs unless \"acpi_no_auto_ssdt\""); + } } status = @@ -577,6 +583,11 @@ static acpi_status acpi_tb_load_namespace(void) continue; } + if (no_auto_ssdt) { + printk(KERN_WARNING "ACPI: SSDT ignored due to \"acpi_no_auto_ssdt\"\n"); + continue; + } + /* Ignore errors while loading tables, get as many as possible */ (void)acpi_ut_release_mutex(ACPI_MTX_TABLES); @@ -622,3 +633,15 @@ acpi_status acpi_load_tables(void) } ACPI_EXPORT_SYMBOL(acpi_load_tables) + + +static int __init acpi_no_auto_ssdt_setup(char *s) { + + printk(KERN_NOTICE "ACPI: SSDT auto-load disabled\n"); + + no_auto_ssdt = 1; + + return 1; +} + +__setup("acpi_no_auto_ssdt", acpi_no_auto_ssdt_setup); From e84a02ba0b33a97a8486f3248fd45f50b1a1c014 Mon Sep 17 00:00:00 2001 From: Mattia Dongili Date: Sat, 4 Aug 2007 00:22:30 +0900 Subject: [PATCH 02/28] sony-laptop: restore the last user requested brightness level on resume. Signed-off-by: Mattia Dongili Signed-off-by: Len Brown --- drivers/misc/sony-laptop.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/misc/sony-laptop.c b/drivers/misc/sony-laptop.c index 14ee06c8f1272..9a803e13c3077 100644 --- a/drivers/misc/sony-laptop.c +++ b/drivers/misc/sony-laptop.c @@ -942,6 +942,11 @@ static int sony_nc_resume(struct acpi_device *device) } } + /* set the last requested brightness level */ + if (sony_backlight_device && + !sony_backlight_update_status(sony_backlight_device)) + printk(KERN_WARNING DRV_PFX "unable to restore brightness level"); + /* re-initialize models with specific requirements */ dmi_check_system(sony_nc_ids); From 11604ecf6fb9c2ab0152fbddb7ea2724438ef76e Mon Sep 17 00:00:00 2001 From: Adrian Bunk Date: Sat, 4 Aug 2007 00:22:31 +0900 Subject: [PATCH 03/28] sony-laptop: sony_nc_ids[] can become static. Signed-off-by: Adrian Bunk Signed-off-by: Mattia Dongili Signed-off-by: Len Brown --- drivers/misc/sony-laptop.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/misc/sony-laptop.c b/drivers/misc/sony-laptop.c index 9a803e13c3077..91da6880ae939 100644 --- a/drivers/misc/sony-laptop.c +++ b/drivers/misc/sony-laptop.c @@ -845,7 +845,7 @@ static struct sony_nc_event sony_C_events[] = { }; /* SNC-only model map */ -struct dmi_system_id sony_nc_ids[] = { +static struct dmi_system_id sony_nc_ids[] = { { .ident = "Sony Vaio FE Series", .callback = sony_nc_C_enable, From f7b88ccb63188e775fe02e746c39ed177741cfc7 Mon Sep 17 00:00:00 2001 From: Eugene Teo Date: Sat, 4 Aug 2007 00:22:32 +0900 Subject: [PATCH 04/28] sonypi: fix ids member of struct acpi_driver ids member of struct acpi_driver is of type struct acpi_device_id, not a character array. Signed-off-by: Eugene Teo Signed-off-by: Mattia Dongili Signed-off-by: Len Brown --- drivers/char/sonypi.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/char/sonypi.c b/drivers/char/sonypi.c index 73037a4d3c502..aeec67e272644 100644 --- a/drivers/char/sonypi.c +++ b/drivers/char/sonypi.c @@ -1147,10 +1147,15 @@ static int sonypi_acpi_remove(struct acpi_device *device, int type) return 0; } +const static struct acpi_device_id sonypi_device_ids[] = { + {"SNY6001", 0}, + {"", 0}, +}; + static struct acpi_driver sonypi_acpi_driver = { .name = "sonypi", .class = "hkey", - .ids = "SNY6001", + .ids = sonypi_device_ids, .ops = { .add = sonypi_acpi_add, .remove = sonypi_acpi_remove, From ac36393de6034be7266264a435360e7628849005 Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Fri, 27 Jul 2007 17:04:40 -0300 Subject: [PATCH 05/28] ACPI: thinkpad-acpi: fix the module init failure path Thomas Renninger reports that if one tries to load thinkpad-acpi in a non-thinkpad, one gets: Call Trace: [] kref_get+0x2f/0x36 [] kobject_get+0x12/0x17 [] get_driver+0x14/0x1a [] driver_remove_file+0x11/0x32 [] :thinkpad_acpi:thinkpad_acpi_module_exit+0xa8/0xfc [] :thinkpad_acpi:thinkpad_acpi_module_init+0x74a/0x776 [] __link_module+0x0/0x25 [] sys_init_module+0x162c/0x178f [] system_call+0x7e/0x83 So, track if the platform driver and its driver attributes were registered, and only deallocate them in that case. This patch is based on Thomas Renninger's patch for the issue. Signed-off-by: Henrique de Moraes Holschuh Acked-by: Thomas Renninger Signed-off-by: Len Brown --- drivers/misc/thinkpad_acpi.c | 10 ++++++++-- drivers/misc/thinkpad_acpi.h | 2 ++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/drivers/misc/thinkpad_acpi.c b/drivers/misc/thinkpad_acpi.c index fa80f355e522f..f6cd34a3dbaca 100644 --- a/drivers/misc/thinkpad_acpi.c +++ b/drivers/misc/thinkpad_acpi.c @@ -4668,12 +4668,15 @@ static int __init thinkpad_acpi_module_init(void) thinkpad_acpi_module_exit(); return ret; } + tp_features.platform_drv_registered = 1; + ret = tpacpi_create_driver_attributes(&tpacpi_pdriver.driver); if (ret) { printk(IBM_ERR "unable to create sysfs driver attributes\n"); thinkpad_acpi_module_exit(); return ret; } + tp_features.platform_drv_attrs_registered = 1; /* Device initialization */ @@ -4756,8 +4759,11 @@ static void thinkpad_acpi_module_exit(void) if (tpacpi_pdev) platform_device_unregister(tpacpi_pdev); - tpacpi_remove_driver_attributes(&tpacpi_pdriver.driver); - platform_driver_unregister(&tpacpi_pdriver); + if (tp_features.platform_drv_attrs_registered) + tpacpi_remove_driver_attributes(&tpacpi_pdriver.driver); + + if (tp_features.platform_drv_registered) + platform_driver_unregister(&tpacpi_pdriver); if (proc_dir) remove_proc_entry(IBM_PROC_DIR, acpi_root_dir); diff --git a/drivers/misc/thinkpad_acpi.h b/drivers/misc/thinkpad_acpi.h index 88af089d64945..eee8809a50d9a 100644 --- a/drivers/misc/thinkpad_acpi.h +++ b/drivers/misc/thinkpad_acpi.h @@ -246,6 +246,8 @@ static struct { u16 wan:1; u16 fan_ctrl_status_undef:1; u16 input_device_registered:1; + u16 platform_drv_registered:1; + u16 platform_drv_attrs_registered:1; } tp_features; struct thinkpad_id_data { From de47b69c7b7be46b0848b2c4f8e23c478cd68690 Mon Sep 17 00:00:00 2001 From: Jesper Juhl Date: Sun, 29 Jul 2007 00:45:59 +0200 Subject: [PATCH 06/28] asus_acpi: fix possible double free (found by Coverity) Signed-off-by: Jesper Juhl Signed-off-by: Len Brown --- drivers/acpi/asus_acpi.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/acpi/asus_acpi.c b/drivers/acpi/asus_acpi.c index 9c4bd220c44fd..86fd142f4bf39 100644 --- a/drivers/acpi/asus_acpi.c +++ b/drivers/acpi/asus_acpi.c @@ -1192,6 +1192,7 @@ static int asus_hotk_get_info(void) break; default: kfree(model); + model = NULL; break; } } From f1cd1fe61b96e4312312d42c0a9784dfab12e007 Mon Sep 17 00:00:00 2001 From: Alexey Starikovskiy Date: Fri, 3 Aug 2007 17:28:17 -0400 Subject: [PATCH 07/28] ACPI: EC: Remove noisy debug printk fron EC driver. ACPI: EC: Handler for query 0x57 is not found! Signed-off-by: Alexey Starikovskiy Signed-off-by: Len Brown --- drivers/acpi/ec.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index 469f3f57f8813..dd384ec757ddf 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -471,7 +471,6 @@ static void acpi_ec_gpe_query(void *ec_cxt) } } mutex_unlock(&ec->lock); - printk(KERN_ERR PREFIX "Handler for query 0x%x is not found!\n", value); } static u32 acpi_ec_gpe_handler(void *data) From 3bd92ba19a89fe61ebf58804f9c8675372f50c1c Mon Sep 17 00:00:00 2001 From: Alexey Starikovskiy Date: Fri, 3 Aug 2007 17:38:20 -0400 Subject: [PATCH 08/28] ACPI: Battery: Synchronize battery operations. http://bugzilla.kernel.org/show_bug.cgi?id=8768 Signed-off-by: Alexey Starikovskiy Signed-off-by: Len Brown --- drivers/acpi/battery.c | 47 +++++++++++++++--------------------------- 1 file changed, 17 insertions(+), 30 deletions(-) diff --git a/drivers/acpi/battery.c b/drivers/acpi/battery.c index 81651032791b3..d7b499fe0cd93 100644 --- a/drivers/acpi/battery.c +++ b/drivers/acpi/battery.c @@ -113,7 +113,7 @@ struct acpi_battery_info { acpi_string oem_info; }; -enum acpi_battery_files{ +enum acpi_battery_files { ACPI_BATTERY_INFO = 0, ACPI_BATTERY_STATE, ACPI_BATTERY_ALARM, @@ -129,13 +129,14 @@ struct acpi_battery_flags { }; struct acpi_battery { - struct mutex mutex; struct acpi_device *device; struct acpi_battery_flags flags; struct acpi_buffer bif_data; struct acpi_buffer bst_data; + struct mutex lock; unsigned long alarm; unsigned long update_time[ACPI_BATTERY_NUMFILES]; + }; inline int acpi_battery_present(struct acpi_battery *battery) @@ -235,10 +236,10 @@ static int acpi_battery_get_info(struct acpi_battery *battery) return 0; /* Evaluate _BIF */ - - status = - acpi_evaluate_object(acpi_battery_handle(battery), "_BIF", NULL, - &buffer); + mutex_lock(&battery->lock); + status = acpi_evaluate_object(acpi_battery_handle(battery), "_BIF", + NULL, &buffer); + mutex_unlock(&battery->lock); if (ACPI_FAILURE(status)) { ACPI_EXCEPTION((AE_INFO, status, "Evaluating _BIF")); return -ENODEV; @@ -285,10 +286,10 @@ static int acpi_battery_get_state(struct acpi_battery *battery) return 0; /* Evaluate _BST */ - - status = - acpi_evaluate_object(acpi_battery_handle(battery), "_BST", NULL, - &buffer); + mutex_lock(&battery->lock); + status = acpi_evaluate_object(acpi_battery_handle(battery), "_BST", + NULL, &buffer); + mutex_unlock(&battery->lock); if (ACPI_FAILURE(status)) { ACPI_EXCEPTION((AE_INFO, status, "Evaluating _BST")); return -ENODEV; @@ -336,9 +337,10 @@ static int acpi_battery_set_alarm(struct acpi_battery *battery, arg0.integer.value = alarm; - status = - acpi_evaluate_object(acpi_battery_handle(battery), "_BTP", + mutex_lock(&battery->lock); + status = acpi_evaluate_object(acpi_battery_handle(battery), "_BTP", &arg_list, NULL); + mutex_unlock(&battery->lock); if (ACPI_FAILURE(status)) return -ENODEV; @@ -658,8 +660,6 @@ acpi_battery_write_alarm(struct file *file, if (!battery || (count > sizeof(alarm_string) - 1)) return -EINVAL; - mutex_lock(&battery->mutex); - result = acpi_battery_update(battery, 1, &update_result); if (result) { result = -ENODEV; @@ -688,9 +688,7 @@ acpi_battery_write_alarm(struct file *file, acpi_battery_check_result(battery, result); if (!result) - result = count; - - mutex_unlock(&battery->mutex); + return count; return result; } @@ -714,8 +712,6 @@ static int acpi_battery_read(int fid, struct seq_file *seq) int update_result = ACPI_BATTERY_NONE_UPDATE; int update = 0; - mutex_lock(&battery->mutex); - update = (get_seconds() - battery->update_time[fid] >= update_time); update = (update | battery->flags.update[fid]); @@ -733,7 +729,6 @@ static int acpi_battery_read(int fid, struct seq_file *seq) result = acpi_read_funcs[fid].print(seq, result); acpi_battery_check_result(battery, result); battery->flags.update[fid] = result; - mutex_unlock(&battery->mutex); return result; } @@ -897,10 +892,7 @@ static int acpi_battery_add(struct acpi_device *device) if (!battery) return -ENOMEM; - mutex_init(&battery->mutex); - - mutex_lock(&battery->mutex); - + mutex_init(&battery->lock); battery->device = device; strcpy(acpi_device_name(device), ACPI_BATTERY_DEVICE_NAME); strcpy(acpi_device_class(device), ACPI_BATTERY_CLASS); @@ -936,7 +928,6 @@ static int acpi_battery_add(struct acpi_device *device) kfree(battery); } - mutex_unlock(&battery->mutex); return result; } @@ -951,8 +942,6 @@ static int acpi_battery_remove(struct acpi_device *device, int type) battery = acpi_driver_data(device); - mutex_lock(&battery->mutex); - status = acpi_remove_notify_handler(device->handle, ACPI_ALL_NOTIFY, acpi_battery_notify); @@ -963,9 +952,7 @@ static int acpi_battery_remove(struct acpi_device *device, int type) kfree(battery->bst_data.pointer); - mutex_unlock(&battery->mutex); - - mutex_destroy(&battery->mutex); + mutex_destroy(&battery->lock); kfree(battery); From cd8c93a4e04dce8f00d1ef3a476aac8bd65ae40b Mon Sep 17 00:00:00 2001 From: Alexey Starikovskiy Date: Fri, 3 Aug 2007 17:52:48 -0400 Subject: [PATCH 09/28] ACPI: EC: If ECDT is not found, look up EC in DSDT. Some ASUS laptops access EC space from device _INI methods, but do not provide ECDT for early EC setup. In order to make them function properly, there is a need to find EC is DSDT before any _INI is called. Similar functionality was turned on by acpi_fake_ecdt=1 command line before. Now it is on all the time. http://bugzilla.kernel.org/show_bug.cgi?id=8598 Signed-off-by: Alexey Starikovskiy Signed-off-by: Len Brown --- drivers/acpi/ec.c | 50 ++++++++++++++++++++++++++--------------------- 1 file changed, 28 insertions(+), 22 deletions(-) diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index dd384ec757ddf..4d7fe829a6e28 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -664,30 +664,32 @@ acpi_ec_register_query_methods(acpi_handle handle, u32 level, return AE_OK; } -static int ec_parse_device(struct acpi_ec *ec, acpi_handle handle) +static acpi_status +ec_parse_device(acpi_handle handle, u32 Level, void *context, void **retval) { - if (ACPI_FAILURE(acpi_walk_resources(handle, METHOD_NAME__CRS, - ec_parse_io_ports, ec))) - return -EINVAL; + acpi_status status; + + struct acpi_ec *ec = context; + status = acpi_walk_resources(handle, METHOD_NAME__CRS, + ec_parse_io_ports, ec); + if (ACPI_FAILURE(status)) + return status; /* Get GPE bit assignment (EC events). */ /* TODO: Add support for _GPE returning a package */ - if (ACPI_FAILURE(acpi_evaluate_integer(handle, "_GPE", NULL, &ec->gpe))) - return -EINVAL; + status = acpi_evaluate_integer(handle, "_GPE", NULL, &ec->gpe); + if (ACPI_FAILURE(status)) + return status; /* Use the global lock for all EC transactions? */ acpi_evaluate_integer(handle, "_GLK", NULL, &ec->global_lock); - /* Find and register all query methods */ - acpi_walk_namespace(ACPI_TYPE_METHOD, handle, 1, - acpi_ec_register_query_methods, ec, NULL); - ec->handle = handle; printk(KERN_INFO PREFIX "GPE = 0x%lx, I/O: command/status = 0x%lx, data = 0x%lx", ec->gpe, ec->command_addr, ec->data_addr); - return 0; + return AE_CTRL_TERMINATE; } static int acpi_ec_add(struct acpi_device *device) @@ -704,7 +706,8 @@ static int acpi_ec_add(struct acpi_device *device) if (!ec) return -ENOMEM; - if (ec_parse_device(ec, device->handle)) { + if (ec_parse_device(device->handle, 0, ec, NULL) != + AE_CTRL_TERMINATE) { kfree(ec); return -EINVAL; } @@ -867,18 +870,21 @@ int __init acpi_ec_ecdt_probe(void) /* * Generate a boot ec context */ - status = acpi_get_table(ACPI_SIG_ECDT, 1, (struct acpi_table_header **)&ecdt_ptr); - if (ACPI_FAILURE(status)) - goto error; - - printk(KERN_INFO PREFIX "EC description table is found, configuring boot EC\n"); - - boot_ec->command_addr = ecdt_ptr->control.address; - boot_ec->data_addr = ecdt_ptr->data.address; - boot_ec->gpe = ecdt_ptr->gpe; - boot_ec->handle = ACPI_ROOT_OBJECT; + if (ACPI_SUCCESS(status)) { + printk(KERN_INFO PREFIX "EC description table is found, configuring boot EC\n\n"); + boot_ec->command_addr = ecdt_ptr->control.address; + boot_ec->data_addr = ecdt_ptr->data.address; + boot_ec->gpe = ecdt_ptr->gpe; + boot_ec->handle = ACPI_ROOT_OBJECT; + } else { + printk(KERN_DEBUG PREFIX "Look up EC in DSDT\n"); + status = acpi_get_devices(ec_device_ids[0].id, ec_parse_device, + boot_ec, NULL); + if (ACPI_FAILURE(status)) + goto error; + } ret = ec_install_handlers(boot_ec); if (!ret) { From 52fe4bdf40bc07498c5f7935551774e8f8458190 Mon Sep 17 00:00:00 2001 From: Len Brown Date: Fri, 3 Aug 2007 17:55:53 -0400 Subject: [PATCH 10/28] ACPI: EC: fix build warning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit drivers/acpi/ec.c:657: warning: ‘acpi_ec_register_query_methods’ defined but not used Signed-off-by: Len Brown --- drivers/acpi/ec.c | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index 4d7fe829a6e28..629289034b613 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -651,19 +651,6 @@ static struct acpi_ec *make_acpi_ec(void) return ec; } -static acpi_status -acpi_ec_register_query_methods(acpi_handle handle, u32 level, - void *context, void **return_value) -{ - struct acpi_namespace_node *node = handle; - struct acpi_ec *ec = context; - int value = 0; - if (sscanf(node->name.ascii, "_Q%x", &value) == 1) { - acpi_ec_add_query_handler(ec, value, handle, NULL, NULL); - } - return AE_OK; -} - static acpi_status ec_parse_device(acpi_handle handle, u32 Level, void *context, void **retval) { From 7c010de7506954e973abfab5c5999c5a97f7a73e Mon Sep 17 00:00:00 2001 From: Alexey Starikovskiy Date: Fri, 3 Aug 2007 17:57:53 -0400 Subject: [PATCH 11/28] ACPI: EC: Switch from boot_ec as soon as we find its desc in DSDT. Some ASUS laptops fail to use boot time EC and need to eventually switch to one described in DSDT. http://bugzilla.kernel.org/show_bug.cgi?id=8709 Signed-off-by: Alexey Starikovskiy Signed-off-by: Len Brown --- drivers/acpi/ec.c | 45 ++++++++++++++++----------------------------- 1 file changed, 16 insertions(+), 29 deletions(-) diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index 629289034b613..71caa7d983a3c 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -679,6 +679,14 @@ ec_parse_device(acpi_handle handle, u32 Level, void *context, void **retval) return AE_CTRL_TERMINATE; } +static void ec_remove_handlers(struct acpi_ec *ec) +{ + acpi_remove_address_space_handler(ec->handle, + ACPI_ADR_SPACE_EC, + &acpi_ec_space_handler); + acpi_remove_gpe_handler(NULL, ec->gpe, &acpi_ec_gpe_handler); +} + static int acpi_ec_add(struct acpi_device *device) { struct acpi_ec *ec = NULL; @@ -702,16 +710,15 @@ static int acpi_ec_add(struct acpi_device *device) /* Check if we found the boot EC */ if (boot_ec) { if (boot_ec->gpe == ec->gpe) { - /* We might have incorrect info for GL at boot time */ mutex_lock(&boot_ec->lock); - boot_ec->global_lock = ec->global_lock; - /* Copy handlers from new ec into boot ec */ - list_splice(&ec->list, &boot_ec->list); + ec_remove_handlers(boot_ec); mutex_unlock(&boot_ec->lock); - kfree(ec); - ec = boot_ec; + mutex_destroy(&boot_ec->lock); + kfree(boot_ec); + first_ec = boot_ec = NULL; } - } else + } + if (!first_ec) first_ec = ec; ec->handle = device->handle; acpi_driver_data(device) = ec; @@ -740,9 +747,6 @@ static int acpi_ec_remove(struct acpi_device *device, int type) if (ec == first_ec) first_ec = NULL; - /* Don't touch boot EC */ - if (boot_ec != ec) - kfree(ec); return 0; } @@ -806,9 +810,7 @@ static int acpi_ec_start(struct acpi_device *device) if (!ec) return -EINVAL; - /* Boot EC is already working */ - if (ec != boot_ec) - ret = ec_install_handlers(ec); + ret = ec_install_handlers(ec); /* EC is fully operational, allow queries */ atomic_set(&ec->query_pending, 0); @@ -818,7 +820,6 @@ static int acpi_ec_start(struct acpi_device *device) static int acpi_ec_stop(struct acpi_device *device, int type) { - acpi_status status; struct acpi_ec *ec; if (!device) @@ -827,21 +828,7 @@ static int acpi_ec_stop(struct acpi_device *device, int type) ec = acpi_driver_data(device); if (!ec) return -EINVAL; - - /* Don't touch boot EC */ - if (ec == boot_ec) - return 0; - - status = acpi_remove_address_space_handler(ec->handle, - ACPI_ADR_SPACE_EC, - &acpi_ec_space_handler); - if (ACPI_FAILURE(status)) - return -ENODEV; - - status = acpi_remove_gpe_handler(NULL, ec->gpe, &acpi_ec_gpe_handler); - if (ACPI_FAILURE(status)) - return -ENODEV; - + ec_remove_handlers(ec); return 0; } From 07ddf768d860bee7bd6581b7af3ce1009dbd05d0 Mon Sep 17 00:00:00 2001 From: Adrian Bunk Date: Sun, 29 Jul 2007 17:00:37 +0200 Subject: [PATCH 12/28] ACPI: EC: acpi_ec_remove(): fix use-after-free This patch fixes an obvious use-after-free introduced by commit 837012ede14a8fc088be3682c964da7fc6af026b. Spotted by the Coverity checker. Signed-off-by: Adrian Bunk Acked-by: Alexey Starikovskiy Signed-off-by: Len Brown --- drivers/acpi/ec.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index 71caa7d983a3c..b649ac7122a51 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -730,14 +730,14 @@ static int acpi_ec_add(struct acpi_device *device) static int acpi_ec_remove(struct acpi_device *device, int type) { struct acpi_ec *ec; - struct acpi_ec_query_handler *handler; + struct acpi_ec_query_handler *handler, *tmp; if (!device) return -EINVAL; ec = acpi_driver_data(device); mutex_lock(&ec->lock); - list_for_each_entry(handler, &ec->list, node) { + list_for_each_entry_safe(handler, tmp, &ec->list, node) { list_del(&handler->node); kfree(handler); } From bc90a0105dea7b773e99ae7abde56a6930081ddb Mon Sep 17 00:00:00 2001 From: Adrian Bunk Date: Mon, 9 Jul 2007 11:33:15 -0700 Subject: [PATCH 13/28] ACPI: sbs: remove dead code Remove dead code spotted by the Coverity checker. Signed-off-by: Adrian Bunk Signed-off-by: Andrew Morton Acked-by: Alexey Starikovskiy Signed-off-by: Len Brown --- drivers/acpi/sbs.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/drivers/acpi/sbs.c b/drivers/acpi/sbs.c index 7d8e78ea13a51..82c3a550016d8 100644 --- a/drivers/acpi/sbs.c +++ b/drivers/acpi/sbs.c @@ -1415,7 +1415,7 @@ static int acpi_sbs_update_run(struct acpi_sbs *sbs, int id, int data_type) char dir_name[32]; int do_battery_init = 0, do_ac_init = 0; int old_remaining_capacity = 0; - int update_ac = 1, update_battery = 1; + int update_battery = 1; int up_tm = update_time; if (sbs_zombie(sbs)) { @@ -1435,10 +1435,6 @@ static int acpi_sbs_update_run(struct acpi_sbs *sbs, int id, int data_type) sbs->run_cnt++; - if (!update_ac && !update_battery) { - goto end; - } - old_ac_present = sbs->ac.ac_present; result = acpi_ac_get_present(sbs); From 0a5245099819b0ae0a8e985f54909ba8414faba5 Mon Sep 17 00:00:00 2001 From: Meelis Roos Date: Thu, 26 Jul 2007 12:56:55 +0300 Subject: [PATCH 14/28] ACPI: EC: fix run-together printk lines Signed-off-by: Meelis Roos Signed-off-by: Len Brown --- drivers/acpi/ec.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index b649ac7122a51..b28b56524f0bc 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -673,7 +673,7 @@ ec_parse_device(acpi_handle handle, u32 Level, void *context, void **retval) ec->handle = handle; - printk(KERN_INFO PREFIX "GPE = 0x%lx, I/O: command/status = 0x%lx, data = 0x%lx", + printk(KERN_INFO PREFIX "GPE = 0x%lx, I/O: command/status = 0x%lx, data = 0x%lx\n", ec->gpe, ec->command_addr, ec->data_addr); return AE_CTRL_TERMINATE; From 5f70bf7510e5e51b0bac32b1470c92e9332452a4 Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Sun, 5 Aug 2007 15:20:45 -0300 Subject: [PATCH 15/28] ACPI: thinkpad-acpi: change thinkpad-acpi input default and kconfig help The current kconfig help text was misleading users. Also, the default for an input-layer-optimized support caused way too many problems without up-to-date userspace in place. So, rework the help text, and change the default to N. Note that distributions are supposed to enable this option as soon as they update HAL to a version that handles the thinkpad-acpi new input layer interface. Signed-off-by: Henrique de Moraes Holschuh Cc: Michael S. Tsirkin Cc: Hugh Dickins Signed-off-by: Len Brown --- drivers/misc/Kconfig | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index aaaa61ea4217a..518d5d3354648 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -200,14 +200,22 @@ config THINKPAD_ACPI_BAY config THINKPAD_ACPI_INPUT_ENABLED bool "Enable input layer support by default" depends on THINKPAD_ACPI - default y + default n ---help--- - Enables hot key handling over the input layer by default. If unset, - the driver does not enable any hot key handling by default, and also - starts up with a mostly empty keymap. - - If you are not sure, say Y here. Say N to retain the deprecated - behavior of ibm-acpi, and thinkpad-acpi for kernels up to 2.6.21. + This option enables thinkpad-acpi hot key handling over the input + layer at driver load time. When it is unset, the driver does not + enable hot key handling by default, and also starts up with a mostly + empty keymap. + + This option should be enabled if you have a new enough HAL or other + userspace support that properly handles the thinkpad-acpi event + device. It auto-tunes the hot key support to those reported by the + firmware and enables it automatically. + + If unsure, say N here to retain the old behaviour of ibm-acpi, and + thinkpad-acpi up to kernel 2.6.21: userspace will have to enable and + set up the thinkpad-acpi hot key handling using the sysfs interace + after loading the driver. endif # MISC_DEVICES From ed3110efb538d7acbf635095c1382118f7414f75 Mon Sep 17 00:00:00 2001 From: Venki Pallipadi Date: Tue, 31 Jul 2007 12:04:31 -0700 Subject: [PATCH 16/28] ACPI: fix "Time Problems with 2.6.23-rc1-gf695baf2" Enable C3 without bm control only for CST based C3. Signed-off-by: Venkatesh Pallipadi Signed-off-by: Len Brown --- drivers/acpi/processor_idle.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c index a898991f77cbc..a8634a0655fca 100644 --- a/drivers/acpi/processor_idle.c +++ b/drivers/acpi/processor_idle.c @@ -969,11 +969,17 @@ static void acpi_processor_power_verify_c3(struct acpi_processor *pr, } if (pr->flags.bm_check) { - /* bus mastering control is necessary */ if (!pr->flags.bm_control) { - /* In this case we enter C3 without bus mastering */ - ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "C3 support without bus mastering control\n")); + if (pr->flags.has_cst != 1) { + /* bus mastering control is necessary */ + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "C3 support requires BM control\n")); + return; + } else { + /* Here we enter C3 without bus mastering */ + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "C3 support without BM control\n")); + } } } else { /* From 501092929ccb8a1d2eb0ed700e38df4ae0de7108 Mon Sep 17 00:00:00 2001 From: Fenghua Yu Date: Tue, 7 Aug 2007 18:40:30 -0400 Subject: [PATCH 17/28] acpi-cpufreq: Fix some x86/x86-64 acpi-cpufreq driver issues This patch addresses some issues in x86/x86-64 acpi-cpufreq driver: 1. Current memory allocation for acpi_perf_data is actually open-coded alloc_percpu(). The patch defines and handles acpi_perf_data as percpu data. The code will be cleaner and easier to be maintained with this change. 2. Won't load driver in acpi_cpufreq_early_init() failure case. 3. Add __init for acpi_cpufreq_early_init(). Signed-off-by: Fenghua Yu Acked-by: Venkatesh Pallipadi Cc: Dave Jones Signed-off-by: Andrew Morton Signed-off-by: Len Brown --- arch/i386/kernel/cpu/cpufreq/acpi-cpufreq.c | 41 ++++++++------------- drivers/acpi/processor_perflib.c | 6 +-- include/acpi/processor.h | 2 +- 3 files changed, 19 insertions(+), 30 deletions(-) diff --git a/arch/i386/kernel/cpu/cpufreq/acpi-cpufreq.c b/arch/i386/kernel/cpu/cpufreq/acpi-cpufreq.c index 32d04b083e38f..705e13a307813 100644 --- a/arch/i386/kernel/cpu/cpufreq/acpi-cpufreq.c +++ b/arch/i386/kernel/cpu/cpufreq/acpi-cpufreq.c @@ -68,7 +68,8 @@ struct acpi_cpufreq_data { }; static struct acpi_cpufreq_data *drv_data[NR_CPUS]; -static struct acpi_processor_performance *acpi_perf_data[NR_CPUS]; +/* acpi_perf_data is a pointer to percpu data. */ +static struct acpi_processor_performance *acpi_perf_data; static struct cpufreq_driver acpi_cpufreq_driver; @@ -508,24 +509,14 @@ acpi_cpufreq_guess_freq(struct acpi_cpufreq_data *data, unsigned int cpu) * do _PDC and _PSD and find out the processor dependency for the * actual init that will happen later... */ -static int acpi_cpufreq_early_init(void) +static int __init acpi_cpufreq_early_init(void) { - struct acpi_processor_performance *data; - unsigned int i, j; - dprintk("acpi_cpufreq_early_init\n"); - for_each_possible_cpu(i) { - data = kzalloc(sizeof(struct acpi_processor_performance), - GFP_KERNEL); - if (!data) { - for_each_possible_cpu(j) { - kfree(acpi_perf_data[j]); - acpi_perf_data[j] = NULL; - } - return -ENOMEM; - } - acpi_perf_data[i] = data; + acpi_perf_data = alloc_percpu(struct acpi_processor_performance); + if (!acpi_perf_data) { + dprintk("Memory allocation error for acpi_perf_data.\n"); + return -ENOMEM; } /* Do initialization in ACPI core */ @@ -574,14 +565,11 @@ static int acpi_cpufreq_cpu_init(struct cpufreq_policy *policy) dprintk("acpi_cpufreq_cpu_init\n"); - if (!acpi_perf_data[cpu]) - return -ENODEV; - data = kzalloc(sizeof(struct acpi_cpufreq_data), GFP_KERNEL); if (!data) return -ENOMEM; - data->acpi_data = acpi_perf_data[cpu]; + data->acpi_data = percpu_ptr(acpi_perf_data, cpu); drv_data[cpu] = data; if (cpu_has(c, X86_FEATURE_CONSTANT_TSC)) @@ -778,24 +766,25 @@ static struct cpufreq_driver acpi_cpufreq_driver = { static int __init acpi_cpufreq_init(void) { + int ret; + dprintk("acpi_cpufreq_init\n"); - acpi_cpufreq_early_init(); + ret = acpi_cpufreq_early_init(); + if (ret) + return ret; return cpufreq_register_driver(&acpi_cpufreq_driver); } static void __exit acpi_cpufreq_exit(void) { - unsigned int i; dprintk("acpi_cpufreq_exit\n"); cpufreq_unregister_driver(&acpi_cpufreq_driver); - for_each_possible_cpu(i) { - kfree(acpi_perf_data[i]); - acpi_perf_data[i] = NULL; - } + free_percpu(acpi_perf_data); + return; } diff --git a/drivers/acpi/processor_perflib.c b/drivers/acpi/processor_perflib.c index c4efc0c17f8f3..463b0247cbc51 100644 --- a/drivers/acpi/processor_perflib.c +++ b/drivers/acpi/processor_perflib.c @@ -539,7 +539,7 @@ static int acpi_processor_get_psd(struct acpi_processor *pr) } int acpi_processor_preregister_performance( - struct acpi_processor_performance **performance) + struct acpi_processor_performance *performance) { int count, count_target; int retval = 0; @@ -567,12 +567,12 @@ int acpi_processor_preregister_performance( continue; } - if (!performance || !performance[i]) { + if (!performance || !percpu_ptr(performance, i)) { retval = -EINVAL; continue; } - pr->performance = performance[i]; + pr->performance = percpu_ptr(performance, i); cpu_set(i, pr->performance->shared_cpu_map); if (acpi_processor_get_psd(pr)) { retval = -EINVAL; diff --git a/include/acpi/processor.h b/include/acpi/processor.h index f9f987f8e661b..ec3ffdadb4d25 100644 --- a/include/acpi/processor.h +++ b/include/acpi/processor.h @@ -232,7 +232,7 @@ struct acpi_processor_errata { extern int acpi_processor_preregister_performance(struct acpi_processor_performance - **performance); + *performance); extern int acpi_processor_register_performance(struct acpi_processor_performance *performance, unsigned int cpu); From 7aa763cb56b6efdfa5c1b83c336abf1be914afcd Mon Sep 17 00:00:00 2001 From: Stephan Berberig Date: Fri, 10 Aug 2007 13:10:31 -0700 Subject: [PATCH 18/28] ACPI: bay: send envp with uevent - fix There must not be a new-line character in the uevent. Otherwise, udev gets confused. Thanks to Kay Sievers for pointing it out. Signed-off-by: Stephan Berberig Cc: Kristen Carlson Accardi Signed-off-by: Andrew Morton Acked-by: Greg Kroah-Hartman Signed-off-by: Len Brown --- drivers/acpi/bay.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/acpi/bay.c b/drivers/acpi/bay.c index 56a5b3fffeb36..6daf6088ac888 100644 --- a/drivers/acpi/bay.c +++ b/drivers/acpi/bay.c @@ -337,7 +337,7 @@ static void bay_notify(acpi_handle handle, u32 event, void *data) char *envp[] = { event_string, NULL }; bay_dprintk(handle, "Bay event"); - sprintf(event_string, "BAY_EVENT=%d\n", event); + sprintf(event_string, "BAY_EVENT=%d", event); kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, envp); } From 66b568218ab73be161dc109b913e6fa7dda44e16 Mon Sep 17 00:00:00 2001 From: Holger Macht Date: Fri, 10 Aug 2007 13:10:32 -0700 Subject: [PATCH 19/28] ACPI: dock: Send key=value pair instead of plain value Send key=value pair along with the uevent instead of a plain value so that userspace (udev) can handle it like common environment variables. Signed-off-by: Holger Macht Acked-by: Kristen Carlson Accardi Cc: Stephan Berberig Signed-off-by: Andrew Morton Acked-by: Greg Kroah-Hartman Signed-off-by: Len Brown --- drivers/acpi/dock.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/acpi/dock.c b/drivers/acpi/dock.c index 6192c8be66df6..1dabdf4c07b36 100644 --- a/drivers/acpi/dock.c +++ b/drivers/acpi/dock.c @@ -336,13 +336,13 @@ static void hotplug_dock_devices(struct dock_station *ds, u32 event) static void dock_event(struct dock_station *ds, u32 event, int num) { struct device *dev = &dock_device->dev; - char event_string[7]; + char event_string[13]; char *envp[] = { event_string, NULL }; if (num == UNDOCK_EVENT) - sprintf(event_string, "UNDOCK"); + sprintf(event_string, "EVENT=undock"); else - sprintf(event_string, "DOCK"); + sprintf(event_string, "EVENT=dock"); /* * Indicate that the status of the dock station has From 199e9e7d1106686a85ee9fdf6a824051aa82682e Mon Sep 17 00:00:00 2001 From: Alexey Starikovskiy Date: Fri, 10 Aug 2007 13:45:18 -0700 Subject: [PATCH 20/28] ACPI EC: remove potential deadlock from EC Signed-off-by: Alexey Starikovskiy Signed-off-by: Andrew Morton Signed-off-by: Len Brown --- drivers/acpi/ec.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index b28b56524f0bc..2300d81bbc4e4 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -710,9 +710,7 @@ static int acpi_ec_add(struct acpi_device *device) /* Check if we found the boot EC */ if (boot_ec) { if (boot_ec->gpe == ec->gpe) { - mutex_lock(&boot_ec->lock); ec_remove_handlers(boot_ec); - mutex_unlock(&boot_ec->lock); mutex_destroy(&boot_ec->lock); kfree(boot_ec); first_ec = boot_ec = NULL; From e13d87473284131a7ead8121d5d29345101f68a4 Mon Sep 17 00:00:00 2001 From: Adrian Bunk Date: Fri, 10 Aug 2007 13:45:18 -0700 Subject: [PATCH 21/28] ACPI: static Make the needlessly global "acpi_event_seqnum" static. Signed-off-by: Adrian Bunk Signed-off-by: Andrew Morton Signed-off-by: Len Brown --- drivers/acpi/event.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/acpi/event.c b/drivers/acpi/event.c index dfa5853b17f09..95637a4ff782b 100644 --- a/drivers/acpi/event.c +++ b/drivers/acpi/event.c @@ -108,7 +108,7 @@ static const struct file_operations acpi_system_event_ops = { }; #ifdef CONFIG_NET -unsigned int acpi_event_seqnum; +static unsigned int acpi_event_seqnum; struct acpi_genl_event { acpi_device_class device_class; char bus_id[15]; From 9de1cc4a1724adda84101912e8ba51460f110044 Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Sat, 11 Aug 2007 00:08:33 -0300 Subject: [PATCH 22/28] ACPI: thinkpad-acpi: fix sysfs paths in documentation The documentation used "thinkpad-acpi" to refer to the directories in sysfs, while it should have been using "thinkpad_acpi". Thanks to Hugh Dickins for the error report. I wish I could just call the module and everything else by the proper name with the "-", instead of using these ugly translations to "_". Signed-off-by: Henrique de Moraes Holschuh Cc: Hugh Dickins Signed-off-by: Len Brown --- Documentation/thinkpad-acpi.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/thinkpad-acpi.txt b/Documentation/thinkpad-acpi.txt index 6711fbcf40807..eb2f5986e1eb9 100644 --- a/Documentation/thinkpad-acpi.txt +++ b/Documentation/thinkpad-acpi.txt @@ -105,10 +105,10 @@ The version of thinkpad-acpi's sysfs interface is exported by the driver as a driver attribute (see below). Sysfs driver attributes are on the driver's sysfs attribute space, -for 2.6.20 this is /sys/bus/platform/drivers/thinkpad-acpi/. +for 2.6.20 this is /sys/bus/platform/drivers/thinkpad_acpi/. Sysfs device attributes are on the driver's sysfs attribute space, -for 2.6.20 this is /sys/devices/platform/thinkpad-acpi/. +for 2.6.20 this is /sys/devices/platform/thinkpad_acpi/. Driver version -------------- From 72b33ef8bb1ac7f6c5a16d23304ab25ddc73d93d Mon Sep 17 00:00:00 2001 From: Len Brown Date: Sun, 12 Aug 2007 00:12:17 -0400 Subject: [PATCH 23/28] ACPI: thermal: create "thermal.off=1" to disable ACPI thermal support "thermal.off=1" disables all ACPI thermal support at boot time. CONFIG_ACPI_THERMAL=n can do this at build time. "# rmmod thermal" can do this at run time, as long as thermal is built as a module. WARNING: On some systems, disabling ACPI thermal support will cause the system to run hotter and reduce the lifetime of the hardware. Signed-off-by: Len Brown --- Documentation/kernel-parameters.txt | 3 +++ drivers/acpi/thermal.c | 9 ++++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index a326487a3ab52..de3300c9056a5 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -1820,6 +1820,9 @@ and is between 256 and 4096 characters. It is defined in the file thash_entries= [KNL,NET] Set number of hash buckets for TCP connection + thermal.off= [HW,ACPI] + 1: disable ACPI thermal control + time Show timing data prefixed to each printk message line [deprecated, see 'printk.time'] diff --git a/drivers/acpi/thermal.c b/drivers/acpi/thermal.c index 5a62de1b7f2a0..61337d969d7fb 100644 --- a/drivers/acpi/thermal.c +++ b/drivers/acpi/thermal.c @@ -78,6 +78,10 @@ static int tzp; module_param(tzp, int, 0); MODULE_PARM_DESC(tzp, "Thermal zone polling frequency, in 1/10 seconds.\n"); +static int off; +module_param(off, int, 0); +MODULE_PARM_DESC(off, "Set to disable ACPI thermal support.\n"); + static int acpi_thermal_add(struct acpi_device *device); static int acpi_thermal_remove(struct acpi_device *device, int type); static int acpi_thermal_resume(struct acpi_device *device); @@ -1285,7 +1289,10 @@ static int __init acpi_thermal_init(void) { int result = 0; - + if (off) { + printk(KERN_NOTICE "ACPI: thermal control disabled\n"); + return -ENODEV; + } acpi_thermal_dir = proc_mkdir(ACPI_THERMAL_CLASS, acpi_root_dir); if (!acpi_thermal_dir) return -ENODEV; From 730ff34de766a6fddee25ac1c32bc49c1a2fd758 Mon Sep 17 00:00:00 2001 From: Len Brown Date: Sun, 12 Aug 2007 00:12:26 -0400 Subject: [PATCH 24/28] ACPI: thermal: expose "thermal.tzp=" to set global polling frequency Thermal Zone Polling frequency (_TZP) is an optional ACPI object recommending the rate that the OS should poll the associated thermal zone. If _TZP is 0, no polling should be used. If _TZP is non-zero, then the platform recommends that the OS poll the thermal zone at the specified rate. The minimum period is 30 seconds. The maximum period is 5 minutes. (note _TZP and thermal.tzp units are in deci-seconds, so _TZP = 300 corresponds to 30 seconds) If _TZP is not present, ACPI 3.0b recommends that the thermal zone be polled at an "OS provided default frequency". However, common industry practice is: 1. The BIOS never specifies any _TZP 2. High volume OS's from this century never poll any thermal zones Ie. The OS depends on the platform's ability to provoke thermal events when necessary, and the "OS provided default frequency" is "never":-) There is a proposal that ACPI 4.0 be updated to reflect common industry practice -- ie. no _TZP, no polling. The Linux kernel already follows this practice -- thermal zones are not polled unless _TZP is present and non-zero. But thermal zone polling is useful as a workaround for systems which have ACPI thermal control, but have an issue preventing thermal events. Indeed, some Linux distributions still set a non-zero thermal polling frequency for this reason. But rather than ask the user to write a polling frequency into all the /proc/acpi/thermal_zone/*/polling_frequency files, here we simply document and expose the already existing module parameter to do the same at system level, to simplify debugging those broken platforms. Note that thermal.tzp is a module-load time parameter only. Signed-off-by: Len Brown --- Documentation/kernel-parameters.txt | 5 +++++ drivers/acpi/thermal.c | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index de3300c9056a5..ed7d7913af8cc 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -1823,6 +1823,11 @@ and is between 256 and 4096 characters. It is defined in the file thermal.off= [HW,ACPI] 1: disable ACPI thermal control + thermal.tzp= [HW,ACPI] + Specify global default ACPI thermal zone polling rate + : poll all this frequency + 0: no polling (default) + time Show timing data prefixed to each printk message line [deprecated, see 'printk.time'] diff --git a/drivers/acpi/thermal.c b/drivers/acpi/thermal.c index 61337d969d7fb..b6b3bec845473 100644 --- a/drivers/acpi/thermal.c +++ b/drivers/acpi/thermal.c @@ -75,7 +75,7 @@ MODULE_DESCRIPTION("ACPI Thermal Zone Driver"); MODULE_LICENSE("GPL"); static int tzp; -module_param(tzp, int, 0); +module_param(tzp, int, 0444); MODULE_PARM_DESC(tzp, "Thermal zone polling frequency, in 1/10 seconds.\n"); static int off; From a70cdc5200b0eb9fc3ef64efb29baac9b2cf2431 Mon Sep 17 00:00:00 2001 From: Len Brown Date: Sun, 12 Aug 2007 00:12:35 -0400 Subject: [PATCH 25/28] ACPI: thermal: create "thermal.psv=" to override passive trip points "thermal.psv=-1" disables passive trip points for all ACPI thermal zones. "thermal.psv=C", where 'C' is degrees Celsius, overrides all existing passive trip points for all ACPI thermal zones. thermal.psv is checked at module load time, and in response to trip-point change events. Note that if the system does not deliver thermal zone temperature change events near the new trip-point, then it will not be noticed. To force your custom trip point to be noticed, you may need to enable polling: eg. thermal.tzp=3000 invokes polling every 5 minutes. Note that once passive thermal throttling is invoked, it has its own internal Thermal Sampling Period (_TSP), that is unrelated to _TZP. WARNING: disabling or raising a thermal trip point may result in increased running temperature and shorter hardware lifetime on some systems. Signed-off-by: Len Brown --- Documentation/kernel-parameters.txt | 4 ++++ drivers/acpi/thermal.c | 17 ++++++++++++++--- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index ed7d7913af8cc..52e1c2d26a25e 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -1823,6 +1823,10 @@ and is between 256 and 4096 characters. It is defined in the file thermal.off= [HW,ACPI] 1: disable ACPI thermal control + thermal.psv= [HW,ACPI] + -1: disable all passive trip points + : override all passive trip points to this value + thermal.tzp= [HW,ACPI] Specify global default ACPI thermal zone polling rate : poll all this frequency diff --git a/drivers/acpi/thermal.c b/drivers/acpi/thermal.c index b6b3bec845473..74e25be8abc9a 100644 --- a/drivers/acpi/thermal.c +++ b/drivers/acpi/thermal.c @@ -82,6 +82,10 @@ static int off; module_param(off, int, 0); MODULE_PARM_DESC(off, "Set to disable ACPI thermal support.\n"); +static int psv; +module_param(psv, int, 0644); +MODULE_PARM_DESC(psv, "Disable or override all passive trip points.\n"); + static int acpi_thermal_add(struct acpi_device *device); static int acpi_thermal_remove(struct acpi_device *device, int type); static int acpi_thermal_resume(struct acpi_device *device); @@ -343,9 +347,16 @@ static int acpi_thermal_get_trip_points(struct acpi_thermal *tz) /* Passive: Processors (optional) */ - status = - acpi_evaluate_integer(tz->device->handle, "_PSV", NULL, - &tz->trips.passive.temperature); + if (psv == -1) { + status = AE_SUPPORT; + } else if (psv > 0) { + tz->trips.passive.temperature = CELSIUS_TO_KELVIN(psv); + status = AE_OK; + } else { + status = acpi_evaluate_integer(tz->device->handle, + "_PSV", NULL, &tz->trips.passive.temperature); + } + if (ACPI_FAILURE(status)) { tz->trips.passive.flags.valid = 0; ACPI_DEBUG_PRINT((ACPI_DB_INFO, "No passive threshold\n")); From f54871456162aff557d57bec51639b1288d4a84b Mon Sep 17 00:00:00 2001 From: Len Brown Date: Sun, 12 Aug 2007 00:12:44 -0400 Subject: [PATCH 26/28] ACPI: thermal: create "thermal.nocrt" to disable critical actions thermal.nocrt=1 disables actions on _CRT and _HOT ACPI thermal zone trip-points. They will be marked as in /proc/acpi/thermal_zone/*/trip_points. There are two cases where this option is used: 1. Debugging a hot system crossing valid trip point. If your system fan is spinning at full speed, be sure that the vent is not clogged with dust. Many laptops have very fine thermal fins that are easily blocked. Check that the processor fan-sink is properly seated, has the proper thermal grease, and is really spinning. Check for fan related options in BIOS SETUP. Sometimes there is a performance vs quiet option. Defaults are generally the most conservative. If your fan is not spinning, yet /proc/acpi/fan/ has files in it, please file a Linux/ACPI bug. WARNING: you risk shortening the lifetime of your hardware if you use this parameter on a hot system. Note that this refers to all system components, including the disk drive. 2. Working around a cool system crossing critical trip point due to erroneous temperature reading. Try again with CONFIG_HWMON=n There is known potential for conflict between the the hwmon sub-system and the ACPI BIOS. If this fixes it, notify lm-sensors@lm-sensors.org and linux-acpi@vger.kernel.org Otherwise, file a Linux/ACPI bug, or notify just linux-acpi@vger.kernel.org. Signed-off-by: Len Brown --- Documentation/kernel-parameters.txt | 4 ++++ drivers/acpi/thermal.c | 18 ++++++++++++------ 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 52e1c2d26a25e..4c7d2774d2a40 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -1820,6 +1820,10 @@ and is between 256 and 4096 characters. It is defined in the file thash_entries= [KNL,NET] Set number of hash buckets for TCP connection + thermal.nocrt= [HW,ACPI] + Set to disable actions on ACPI thermal zone + critical and hot trip points. + thermal.off= [HW,ACPI] 1: disable ACPI thermal control diff --git a/drivers/acpi/thermal.c b/drivers/acpi/thermal.c index 74e25be8abc9a..57d05ff44dd1a 100644 --- a/drivers/acpi/thermal.c +++ b/drivers/acpi/thermal.c @@ -78,6 +78,10 @@ static int tzp; module_param(tzp, int, 0444); MODULE_PARM_DESC(tzp, "Thermal zone polling frequency, in 1/10 seconds.\n"); +static int nocrt; +module_param(nocrt, int, 0); +MODULE_PARM_DESC(nocrt, "Set to disable action on ACPI thermal zone critical and hot trips.\n"); + static int off; module_param(off, int, 0); MODULE_PARM_DESC(off, "Set to disable ACPI thermal support.\n"); @@ -442,7 +446,7 @@ static int acpi_thermal_get_devices(struct acpi_thermal *tz) static int acpi_thermal_critical(struct acpi_thermal *tz) { - if (!tz || !tz->trips.critical.flags.valid) + if (!tz || !tz->trips.critical.flags.valid || nocrt) return -EINVAL; if (tz->temperature >= tz->trips.critical.temperature) { @@ -464,7 +468,7 @@ static int acpi_thermal_critical(struct acpi_thermal *tz) static int acpi_thermal_hot(struct acpi_thermal *tz) { - if (!tz || !tz->trips.hot.flags.valid) + if (!tz || !tz->trips.hot.flags.valid || nocrt) return -EINVAL; if (tz->temperature >= tz->trips.hot.temperature) { @@ -839,12 +843,14 @@ static int acpi_thermal_trip_seq_show(struct seq_file *seq, void *offset) goto end; if (tz->trips.critical.flags.valid) - seq_printf(seq, "critical (S5): %ld C\n", - KELVIN_TO_CELSIUS(tz->trips.critical.temperature)); + seq_printf(seq, "critical (S5): %ld C%s", + KELVIN_TO_CELSIUS(tz->trips.critical.temperature), + nocrt ? " \n" : "\n"); if (tz->trips.hot.flags.valid) - seq_printf(seq, "hot (S4): %ld C\n", - KELVIN_TO_CELSIUS(tz->trips.hot.temperature)); + seq_printf(seq, "hot (S4): %ld C%s", + KELVIN_TO_CELSIUS(tz->trips.hot.temperature), + nocrt ? " \n" : "\n"); if (tz->trips.passive.flags.valid) { seq_printf(seq, From f8707ec9643769957065405b5090e4aa64fd8214 Mon Sep 17 00:00:00 2001 From: Len Brown Date: Sun, 12 Aug 2007 00:12:54 -0400 Subject: [PATCH 27/28] ACPI: thermal: create "thermal.act=" to disable or override active trip point thermal.act=-1 disables all active trip points in all ACPI thermal zones. thermal.act=C, where C > 0, overrides all lowest temperature active trip points in all thermal zones to C degrees Celsius. Raising this trip-point may allow you to keep your system silent up to a higher temperature. However, it will not allow you to raise the lowest temperature trip point above the next higher trip point (if there is one). Lowering this trip point may kick in the fan sooner. Note that overriding this trip-point will disable any BIOS attempts to implement hysteresis around the lowest temperature trip point. This may result in the fan starting and stopping frequently if temperature frequently crosses C. WARNING: raising trip points above the manufacturer's defaults may cause the system to run at higher temperature and shorten its life. Signed-off-by: Len Brown --- Documentation/kernel-parameters.txt | 4 ++++ drivers/acpi/thermal.c | 34 +++++++++++++++++++++++++---- 2 files changed, 34 insertions(+), 4 deletions(-) diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 4c7d2774d2a40..06db892b558a7 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -1820,6 +1820,10 @@ and is between 256 and 4096 characters. It is defined in the file thash_entries= [KNL,NET] Set number of hash buckets for TCP connection + thermal.act= [HW,ACPI] + -1: disable all active trip points in all thermal zones + : override all lowest active trip points + thermal.nocrt= [HW,ACPI] Set to disable actions on ACPI thermal zone critical and hot trip points. diff --git a/drivers/acpi/thermal.c b/drivers/acpi/thermal.c index 57d05ff44dd1a..3521c37bbd339 100644 --- a/drivers/acpi/thermal.c +++ b/drivers/acpi/thermal.c @@ -74,6 +74,10 @@ MODULE_AUTHOR("Paul Diefenbaugh"); MODULE_DESCRIPTION("ACPI Thermal Zone Driver"); MODULE_LICENSE("GPL"); +static int act; +module_param(act, int, 0644); +MODULE_PARM_DESC(act, "Disable or override all lowest active trip points.\n"); + static int tzp; module_param(tzp, int, 0444); MODULE_PARM_DESC(tzp, "Thermal zone polling frequency, in 1/10 seconds.\n"); @@ -405,11 +409,33 @@ static int acpi_thermal_get_trip_points(struct acpi_thermal *tz) char name[5] = { '_', 'A', 'C', ('0' + i), '\0' }; - status = - acpi_evaluate_integer(tz->device->handle, name, NULL, - &tz->trips.active[i].temperature); - if (ACPI_FAILURE(status)) + if (act == -1) + break; /* disable all active trip points */ + + status = acpi_evaluate_integer(tz->device->handle, + name, NULL, &tz->trips.active[i].temperature); + + if (ACPI_FAILURE(status)) { + if (i == 0) /* no active trip points */ + break; + if (act <= 0) /* no override requested */ + break; + if (i == 1) { /* 1 trip point */ + tz->trips.active[0].temperature = + CELSIUS_TO_KELVIN(act); + } else { /* multiple trips */ + /* + * Don't allow override higher than + * the next higher trip point + */ + tz->trips.active[i - 1].temperature = + (tz->trips.active[i - 2].temperature < + CELSIUS_TO_KELVIN(act) ? + tz->trips.active[i - 2].temperature : + CELSIUS_TO_KELVIN(act)); + } break; + } name[2] = 'L'; status = From 0b5bfa1cbefdc6e4c60f30ed545389b5ffe0f75f Mon Sep 17 00:00:00 2001 From: Len Brown Date: Sun, 12 Aug 2007 00:13:02 -0400 Subject: [PATCH 28/28] ACPI: thermal: add DMI hooks to handle AOpen's broken Award BIOS Use DMI to: 1. enable polling (BIOS thermal events are broken) 2. disable active trip points (BIOS fan control is broken) 3. disable passive trip point (BIOS hard-codes it too low) The actual temperature reading does work, and with the aid of polling, the critical trip point should work too. http://bugzilla.kernel.org/show_bug.cgi?id=8842 Signed-off-by: Len Brown --- drivers/acpi/thermal.c | 65 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/drivers/acpi/thermal.c b/drivers/acpi/thermal.c index 3521c37bbd339..1e06159fd9c4a 100644 --- a/drivers/acpi/thermal.c +++ b/drivers/acpi/thermal.c @@ -33,6 +33,7 @@ #include #include +#include #include #include #include @@ -1328,10 +1329,74 @@ static int acpi_thermal_resume(struct acpi_device *device) return AE_OK; } +#ifdef CONFIG_DMI +static int thermal_act(struct dmi_system_id *d) { + + if (act == 0) { + printk(KERN_NOTICE "ACPI: %s detected: " + "disabling all active thermal trip points\n", d->ident); + act = -1; + } + return 0; +} +static int thermal_tzp(struct dmi_system_id *d) { + + if (tzp == 0) { + printk(KERN_NOTICE "ACPI: %s detected: " + "enabling thermal zone polling\n", d->ident); + tzp = 300; /* 300 dS = 30 Seconds */ + } + return 0; +} +static int thermal_psv(struct dmi_system_id *d) { + + if (psv == 0) { + printk(KERN_NOTICE "ACPI: %s detected: " + "disabling all passive thermal trip points\n", d->ident); + psv = -1; + } + return 0; +} + +static struct dmi_system_id thermal_dmi_table[] __initdata = { + /* + * Award BIOS on this AOpen makes thermal control almost worthless. + * http://bugzilla.kernel.org/show_bug.cgi?id=8842 + */ + { + .callback = thermal_act, + .ident = "AOpen i915GMm-HFS", + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "AOpen"), + DMI_MATCH(DMI_BOARD_NAME, "i915GMm-HFS"), + }, + }, + { + .callback = thermal_psv, + .ident = "AOpen i915GMm-HFS", + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "AOpen"), + DMI_MATCH(DMI_BOARD_NAME, "i915GMm-HFS"), + }, + }, + { + .callback = thermal_tzp, + .ident = "AOpen i915GMm-HFS", + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "AOpen"), + DMI_MATCH(DMI_BOARD_NAME, "i915GMm-HFS"), + }, + }, + {} +}; +#endif /* CONFIG_DMI */ + static int __init acpi_thermal_init(void) { int result = 0; + dmi_check_system(thermal_dmi_table); + if (off) { printk(KERN_NOTICE "ACPI: thermal control disabled\n"); return -ENODEV;