diff --git a/drivers/acpi/blacklist.c b/drivers/acpi/blacklist.c index 3ec110ce00c8c3096b6f0ed7c379777b05e4822d..8809654d6cc965e292b2424f5f85cdecca15c1a2 100644 --- a/drivers/acpi/blacklist.c +++ b/drivers/acpi/blacklist.c @@ -3,6 +3,7 @@ * * Check to see if the given machine has a known bad ACPI BIOS * or if the BIOS is too old. + * Check given machine against acpi_osi_dmi_table[]. * * Copyright (C) 2004 Len Brown * Copyright (C) 2002 Andy Grover @@ -50,6 +51,8 @@ struct acpi_blacklist_item { u32 is_critical_error; }; +static struct dmi_system_id acpi_osi_dmi_table[] __initdata; + /* * POLICY: If *anything* doesn't work, put it on the blacklist. * If they are critical errors, mark it critical, and abort driver load. @@ -165,5 +168,383 @@ int __init acpi_blacklisted(void) blacklisted += blacklist_by_year(); + dmi_check_system(acpi_osi_dmi_table); + return blacklisted; } +#ifdef CONFIG_DMI +static int __init dmi_enable_osi_linux(const struct dmi_system_id *d) +{ + acpi_dmi_osi_linux(1, d); /* enable */ + return 0; +} +static int __init dmi_disable_osi_linux(const struct dmi_system_id *d) +{ + acpi_dmi_osi_linux(0, d); /* disable */ + return 0; +} +static int __init dmi_unknown_osi_linux(const struct dmi_system_id *d) +{ + acpi_dmi_osi_linux(-1, d); /* unknown */ + return 0; +} + +/* + * Most BIOS that invoke OSI(Linux) do nothing with it. + * But some cause Linux to break. + * Only a couple use it to make Linux run better. + * + * Thus, Linux should continue to disable OSI(Linux) by default, + * should continue to discourage BIOS writers from using it, and + * should whitelist the few existing systems that require it. + * + * If it appears clear a vendor isn't using OSI(Linux) + * for anything constructive, blacklist them by name to disable + * unnecessary dmesg warnings on all of their products. + */ + +static struct dmi_system_id acpi_osi_dmi_table[] __initdata = { + /* + * Disable OSI(Linux) warnings on all "Acer, inc." + * + * _OSI(Linux) disables the latest Windows BIOS code: + * DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5050"), + * DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5580"), + * DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 3010"), + * _OSI(Linux) effect unknown: + * DMI_MATCH(DMI_PRODUCT_NAME, "Ferrari 5000"), + */ + { + .callback = dmi_disable_osi_linux, + .ident = "Acer, inc.", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Acer, inc."), + }, + }, + /* + * Disable OSI(Linux) warnings on all "Acer" + * + * _OSI(Linux) effect unknown: + * DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5100"), + * DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5610"), + * DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 7720Z"), + * DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 5520"), + * DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 6460"), + * DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 7510"), + * DMI_MATCH(DMI_PRODUCT_NAME, "Extensa 5220"), + */ + { + .callback = dmi_unknown_osi_linux, + .ident = "Acer", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + }, + }, + /* + * Disable OSI(Linux) warnings on all "Apple Computer, Inc." + * + * _OSI(Linux) confirmed to be a NOP: + * DMI_MATCH(DMI_PRODUCT_NAME, "MacBook1,1"), + * DMI_MATCH(DMI_PRODUCT_NAME, "MacBook2,1"), + * DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro2,2"), + * _OSI(Linux) effect unknown: + * DMI_MATCH(DMI_PRODUCT_NAME, "MacPro2,1"), + * DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro1,1"), + * DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro3,1"), + */ + { + .callback = dmi_disable_osi_linux, + .ident = "Apple", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Apple Computer, Inc."), + }, + }, + /* + * Disable OSI(Linux) warnings on all "BenQ" + * + * _OSI(Linux) confirmed to be a NOP: + * DMI_MATCH(DMI_PRODUCT_NAME, "Joybook S31"), + */ + { + .callback = dmi_disable_osi_linux, + .ident = "BenQ", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "BenQ"), + }, + }, + /* + * Disable OSI(Linux) warnings on all "Clevo Co." + * + * _OSI(Linux) confirmed to be a NOP: + * DMI_MATCH(DMI_PRODUCT_NAME, "M570RU"), + */ + { + .callback = dmi_disable_osi_linux, + .ident = "Clevo", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Clevo Co."), + }, + }, + /* + * Disable OSI(Linux) warnings on all "COMPAL" + * + * _OSI(Linux) confirmed to be a NOP: + * DMI_MATCH(DMI_BOARD_NAME, "HEL8X"), + * _OSI(Linux) unknown effect: + * DMI_MATCH(DMI_BOARD_NAME, "IFL91"), + */ + { + .callback = dmi_unknown_osi_linux, + .ident = "Compal", + .matches = { + DMI_MATCH(DMI_BIOS_VENDOR, "COMPAL"), + }, + }, + { /* OSI(Linux) touches USB, breaks suspend to disk */ + .callback = dmi_disable_osi_linux, + .ident = "Dell Dimension 5150", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Dell DM051"), + }, + }, + { /* OSI(Linux) is a NOP */ + .callback = dmi_disable_osi_linux, + .ident = "Dell", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1501"), + }, + }, + { /* OSI(Linux) effect unknown */ + .callback = dmi_unknown_osi_linux, + .ident = "Dell", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Latitude D830"), + }, + }, + { /* OSI(Linux) effect unknown */ + .callback = dmi_unknown_osi_linux, + .ident = "Dell", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "OptiPlex GX620"), + }, + }, + { /* OSI(Linux) effect unknown */ + .callback = dmi_unknown_osi_linux, + .ident = "Dell", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "PowerEdge 1900"), + }, + }, + { /* OSI(Linux) touches USB */ + .callback = dmi_disable_osi_linux, + .ident = "Dell", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Precision WorkStation 390"), + }, + }, + { /* OSI(Linux) is a NOP */ + .callback = dmi_disable_osi_linux, + .ident = "Dell Vostro 1000", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 1000"), + }, + }, + { /* OSI(Linux) effect unknown */ + .callback = dmi_unknown_osi_linux, + .ident = "Dell", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "PowerEdge SC440"), + }, + }, + { /* OSI(Linux) effect unknown */ + .callback = dmi_unknown_osi_linux, + .ident = "Dialogue Flybook V5", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dialogue Technology Corporation"), + DMI_MATCH(DMI_PRODUCT_NAME, "Flybook V5"), + }, + }, + /* + * Disable OSI(Linux) warnings on all "FUJITSU SIEMENS" + * + * _OSI(Linux) disables latest Windows BIOS code: + * DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Pa 2510"), + * _OSI(Linux) confirmed to be a NOP: + * DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Pi 1536"), + * DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Pi 1556"), + * DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Xi 1546"), + * _OSI(Linux) unknown effect: + * DMI_MATCH(DMI_PRODUCT_NAME, "Amilo M1425"), + * DMI_MATCH(DMI_PRODUCT_NAME, "Amilo Si 1520"), + * DMI_MATCH(DMI_PRODUCT_NAME, "ESPRIMO Mobile V5505"), + */ + { + .callback = dmi_disable_osi_linux, + .ident = "Fujitsu Siemens", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), + }, + }, + /* + * Disable OSI(Linux) warnings on all "Hewlett-Packard" + * + * _OSI(Linux) confirmed to be a NOP: + * .ident = "HP Pavilion tx 1000" + * DMI_MATCH(DMI_BOARD_NAME, "30BF"), + * .ident = "HP Pavilion dv2000" + * DMI_MATCH(DMI_BOARD_NAME, "30B5"), + * .ident = "HP Pavilion dv5000", + * DMI_MATCH(DMI_BOARD_NAME, "30A7"), + * .ident = "HP Pavilion dv6300 30BC", + * DMI_MATCH(DMI_BOARD_NAME, "30BC"), + * .ident = "HP Pavilion dv6000", + * DMI_MATCH(DMI_BOARD_NAME, "30B7"), + * DMI_MATCH(DMI_BOARD_NAME, "30B8"), + * .ident = "HP Pavilion dv9000", + * DMI_MATCH(DMI_BOARD_NAME, "30B9"), + * .ident = "HP Pavilion dv9500", + * DMI_MATCH(DMI_BOARD_NAME, "30CB"), + * .ident = "HP/Compaq Presario C500", + * DMI_MATCH(DMI_BOARD_NAME, "30C6"), + * .ident = "HP/Compaq Presario F500", + * DMI_MATCH(DMI_BOARD_NAME, "30D3"), + * _OSI(Linux) unknown effect: + * .ident = "HP Pavilion dv6500", + * DMI_MATCH(DMI_BOARD_NAME, "30D0"), + */ + { + .callback = dmi_disable_osi_linux, + .ident = "Hewlett-Packard", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), + }, + }, + /* + * Lenovo has a mix of systems OSI(Linux) situations + * and thus we can not wildcard the vendor. + * + * _OSI(Linux) helps sound + * DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad R61"), + * DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad T61"), + * _OSI(Linux) is a NOP: + * DMI_MATCH(DMI_PRODUCT_VERSION, "3000 N100"), + */ + { + .callback = dmi_enable_osi_linux, + .ident = "Lenovo ThinkPad R61", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad R61"), + }, + }, + { + .callback = dmi_enable_osi_linux, + .ident = "Lenovo ThinkPad T61", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad T61"), + }, + }, + { + .callback = dmi_unknown_osi_linux, + .ident = "Lenovo 3000 V100", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_VERSION, "LENOVO3000 V100"), + }, + }, + { + .callback = dmi_disable_osi_linux, + .ident = "Lenovo 3000 N100", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_VERSION, "3000 N100"), + }, + }, + /* + * Disable OSI(Linux) warnings on all "LG Electronics" + * + * _OSI(Linux) confirmed to be a NOP: + * DMI_MATCH(DMI_PRODUCT_NAME, "P1-J150B"), + */ + { + .callback = dmi_disable_osi_linux, + .ident = "LG", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LG Electronics"), + }, + }, + /* NEC - OSI(Linux) effect unknown */ + { + .callback = dmi_unknown_osi_linux, + .ident = "NEC VERSA M360", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "NEC Computers SAS"), + DMI_MATCH(DMI_PRODUCT_NAME, "NEC VERSA M360"), + }, + }, + /* + * Disable OSI(Linux) warnings on all "Samsung Electronics" + * + * OSI(Linux) disables PNP0C32 and other BIOS code for Windows: + * DMI_MATCH(DMI_PRODUCT_NAME, "R40P/R41P"), + * DMI_MATCH(DMI_PRODUCT_NAME, "R59P/R60P/R61P"), + */ + { + .callback = dmi_disable_osi_linux, + .ident = "Samsung", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), + }, + }, + /* + * Disable OSI(Linux) warnings on all "Sony Corporation" + * + * _OSI(Linux) is a NOP: + * DMI_MATCH(DMI_PRODUCT_NAME, "VGN-SZ650N"), + * DMI_MATCH(DMI_PRODUCT_NAME, "VGN-SZ38GP_C"), + * DMI_MATCH(DMI_PRODUCT_NAME, "VGN-TZ21MN_N"), + * _OSI(Linux) unknown effect: + * DMI_MATCH(DMI_PRODUCT_NAME, "VGN-FZ11M"), + */ + { + .callback = dmi_unknown_osi_linux, + .ident = "Sony", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"), + }, + }, + /* + * Disable OSI(Linux) warnings on all "TOSHIBA" + * + * _OSI(Linux) breaks sound (bugzilla 7787): + * DMI_MATCH(DMI_PRODUCT_NAME, "Satellite P100"), + * DMI_MATCH(DMI_PRODUCT_NAME, "Satellite P105"), + * _OSI(Linux) is a NOP: + * DMI_MATCH(DMI_PRODUCT_NAME, "Satellite A100"), + * DMI_MATCH(DMI_PRODUCT_NAME, "Satellite A210"), + * _OSI(Linux) unknown effect: + * DMI_MATCH(DMI_PRODUCT_NAME, "Satellite A135"), + * DMI_MATCH(DMI_PRODUCT_NAME, "Satellite A200"), + * DMI_MATCH(DMI_PRODUCT_NAME, "Satellite P205"), + * DMI_MATCH(DMI_PRODUCT_NAME, "Satellite U305"), + */ + { + .callback = dmi_disable_osi_linux, + .ident = "Toshiba", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), + }, + }, + {} +}; + +#endif /* CONFIG_DMI */ diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c index d7a115c362d1b53c84a61164c51ff24036684b28..f4487c38d9f294893ebc460f5f05c27b3918b77d 100644 --- a/drivers/acpi/bus.c +++ b/drivers/acpi/bus.c @@ -200,7 +200,7 @@ int acpi_bus_set_power(acpi_handle handle, int state) * Get device's current power state */ acpi_bus_get_power(device->handle, &device->power.state); - if (state == device->power.state) { + if ((state == device->power.state) && !device->flags.force_power_state) { ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device is already at D%d\n", state)); return 0; diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index 97dc16155a556d13c373535f767bc8faba76514f..987b967c74670728831af322571e4a182fb84d7e 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -26,6 +26,9 @@ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ +/* Uncomment next line to get verbose print outs*/ +/* #define DEBUG */ + #include #include #include @@ -47,9 +50,6 @@ #undef PREFIX #define PREFIX "ACPI: EC: " -/* Uncomment next line to get verbose print outs*/ -/* #define DEBUG */ - /* EC status register */ #define ACPI_EC_FLAG_OBF 0x01 /* Output buffer full */ #define ACPI_EC_FLAG_IBF 0x02 /* Input buffer full */ @@ -82,6 +82,7 @@ enum { EC_FLAGS_ADDRESS, /* Address is being written */ EC_FLAGS_NO_WDATA_GPE, /* Don't expect WDATA GPE event */ EC_FLAGS_WDATA, /* Data is being written */ + EC_FLAGS_NO_OBF1_GPE, /* Don't expect GPE before read */ }; static int acpi_ec_remove(struct acpi_device *device, int type); @@ -138,26 +139,26 @@ static struct acpi_ec { static inline u8 acpi_ec_read_status(struct acpi_ec *ec) { u8 x = inb(ec->command_addr); - pr_debug(PREFIX "---> status = 0x%2x\n", x); + pr_debug(PREFIX "---> status = 0x%2.2x\n", x); return x; } static inline u8 acpi_ec_read_data(struct acpi_ec *ec) { u8 x = inb(ec->data_addr); - pr_debug(PREFIX "---> data = 0x%2x\n", x); + pr_debug(PREFIX "---> data = 0x%2.2x\n", x); return inb(ec->data_addr); } static inline void acpi_ec_write_cmd(struct acpi_ec *ec, u8 command) { - pr_debug(PREFIX "<--- command = 0x%2x\n", command); + pr_debug(PREFIX "<--- command = 0x%2.2x\n", command); outb(command, ec->command_addr); } static inline void acpi_ec_write_data(struct acpi_ec *ec, u8 data) { - pr_debug(PREFIX "<--- data = 0x%2x\n", data); + pr_debug(PREFIX "<--- data = 0x%2.2x\n", data); outb(data, ec->data_addr); } @@ -179,6 +180,10 @@ static inline int acpi_ec_check_status(struct acpi_ec *ec, enum ec_event event) static int acpi_ec_wait(struct acpi_ec *ec, enum ec_event event, int force_poll) { int ret = 0; + + if (unlikely(event == ACPI_EC_EVENT_OBF_1 && + test_bit(EC_FLAGS_NO_OBF1_GPE, &ec->flags))) + force_poll = 1; if (unlikely(test_bit(EC_FLAGS_ADDRESS, &ec->flags) && test_bit(EC_FLAGS_NO_ADDRESS_GPE, &ec->flags))) force_poll = 1; @@ -192,7 +197,12 @@ static int acpi_ec_wait(struct acpi_ec *ec, enum ec_event event, int force_poll) goto end; clear_bit(EC_FLAGS_WAIT_GPE, &ec->flags); if (acpi_ec_check_status(ec, event)) { - if (test_bit(EC_FLAGS_ADDRESS, &ec->flags)) { + if (event == ACPI_EC_EVENT_OBF_1) { + /* miss OBF_1 GPE, don't expect it */ + pr_info(PREFIX "missing OBF confirmation, " + "don't expect it any longer.\n"); + set_bit(EC_FLAGS_NO_OBF1_GPE, &ec->flags); + } else if (test_bit(EC_FLAGS_ADDRESS, &ec->flags)) { /* miss address GPE, don't expect it anymore */ pr_info(PREFIX "missing address confirmation, " "don't expect it any longer.\n"); diff --git a/drivers/acpi/fan.c b/drivers/acpi/fan.c index a5a5532db268084307ae31d655a04af6b9913925..a6e149d692cbd332aaf947d689815392474cda34 100644 --- a/drivers/acpi/fan.c +++ b/drivers/acpi/fan.c @@ -47,6 +47,8 @@ MODULE_LICENSE("GPL"); static int acpi_fan_add(struct acpi_device *device); static int acpi_fan_remove(struct acpi_device *device, int type); +static int acpi_fan_suspend(struct acpi_device *device, pm_message_t state); +static int acpi_fan_resume(struct acpi_device *device); static const struct acpi_device_id fan_device_ids[] = { {"PNP0C0B", 0}, @@ -61,6 +63,8 @@ static struct acpi_driver acpi_fan_driver = { .ops = { .add = acpi_fan_add, .remove = acpi_fan_remove, + .suspend = acpi_fan_suspend, + .resume = acpi_fan_resume, }, }; @@ -191,6 +195,10 @@ static int acpi_fan_add(struct acpi_device *device) goto end; } + device->flags.force_power_state = 1; + acpi_bus_set_power(device->handle, state); + device->flags.force_power_state = 0; + result = acpi_fan_add_fs(device); if (result) goto end; @@ -216,6 +224,38 @@ static int acpi_fan_remove(struct acpi_device *device, int type) return 0; } +static int acpi_fan_suspend(struct acpi_device *device, pm_message_t state) +{ + if (!device) + return -EINVAL; + + acpi_bus_set_power(device->handle, ACPI_STATE_D0); + + return AE_OK; +} + +static int acpi_fan_resume(struct acpi_device *device) +{ + int result = 0; + int power_state = 0; + + if (!device) + return -EINVAL; + + result = acpi_bus_get_power(device->handle, &power_state); + if (result) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, + "Error reading fan power state\n")); + return result; + } + + device->flags.force_power_state = 1; + acpi_bus_set_power(device->handle, power_state); + device->flags.force_power_state = 0; + + return result; +} + static int __init acpi_fan_init(void) { int result = 0; diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c index e3a673a008453dfda694aab503dad0c89b19cb1e..e53fb516f9d45538dbfdedc002ad3dcb753e200e 100644 --- a/drivers/acpi/osl.c +++ b/drivers/acpi/osl.c @@ -77,11 +77,55 @@ static struct workqueue_struct *kacpi_notify_wq; #define OSI_STRING_LENGTH_MAX 64 /* arbitrary */ static char osi_additional_string[OSI_STRING_LENGTH_MAX]; -static int osi_linux; /* disable _OSI(Linux) by default */ +/* + * "Ode to _OSI(Linux)" + * + * osi_linux -- Control response to BIOS _OSI(Linux) query. + * + * As Linux evolves, the features that it supports change. + * So an OSI string such as "Linux" is not specific enough + * to be useful across multiple versions of Linux. It + * doesn't identify any particular feature, interface, + * or even any particular version of Linux... + * + * Unfortunately, Linux-2.6.22 and earlier responded "yes" + * to a BIOS _OSI(Linux) query. When + * a reference mobile BIOS started using it, its use + * started to spread to many vendor platforms. + * As it is not supportable, we need to halt that spread. + * + * Today, most BIOS references to _OSI(Linux) are noise -- + * they have no functional effect and are just dead code + * carried over from the reference BIOS. + * + * The next most common case is that _OSI(Linux) harms Linux, + * usually by causing the BIOS to follow paths that are + * not tested during Windows validation. + * + * Finally, there is a short list of platforms + * where OSI(Linux) benefits Linux. + * + * In Linux-2.6.23, OSI(Linux) is first disabled by default. + * DMI is used to disable the dmesg warning about OSI(Linux) + * on platforms where it is known to have no effect. + * But a dmesg warning remains for systems where + * we do not know if OSI(Linux) is good or bad for the system. + * DMI is also used to enable OSI(Linux) for the machines + * that are known to need it. + * + * BIOS writers should NOT query _OSI(Linux) on future systems. + * It will be ignored by default, and to get Linux to + * not ignore it will require a kernel source update to + * add a DMI entry, or a boot-time "acpi_osi=Linux" invocation. + */ +#define OSI_LINUX_ENABLE 0 -#ifdef CONFIG_DMI -static struct __initdata dmi_system_id acpi_osl_dmi_table[]; -#endif +struct osi_linux { + unsigned int enable:1; + unsigned int dmi:1; + unsigned int cmdline:1; + unsigned int known:1; +} osi_linux = { OSI_LINUX_ENABLE, 0, 0, 0}; static void __init acpi_request_region (struct acpi_generic_address *addr, unsigned int length, char *desc) @@ -133,7 +177,6 @@ device_initcall(acpi_reserve_resources); acpi_status __init acpi_os_initialize(void) { - dmi_check_system(acpi_osl_dmi_table); return AE_OK; } @@ -964,13 +1007,37 @@ static int __init acpi_os_name_setup(char *str) __setup("acpi_os_name=", acpi_os_name_setup); -static void enable_osi_linux(int enable) { +static void __init set_osi_linux(unsigned int enable) +{ + if (osi_linux.enable != enable) { + osi_linux.enable = enable; + printk(KERN_NOTICE PREFIX "%sed _OSI(Linux)\n", + enable ? "Add": "Delet"); + } + return; +} - if (osi_linux != enable) - printk(KERN_INFO PREFIX "%sabled _OSI(Linux)\n", - enable ? "En": "Dis"); +static void __init acpi_cmdline_osi_linux(unsigned int enable) +{ + osi_linux.cmdline = 1; /* cmdline set the default */ + set_osi_linux(enable); + + return; +} + +void __init acpi_dmi_osi_linux(int enable, const struct dmi_system_id *d) +{ + osi_linux.dmi = 1; /* DMI knows that this box asks OSI(Linux) */ + + printk(KERN_NOTICE PREFIX "DMI detected: %s\n", d->ident); + + if (enable == -1) + return; + + osi_linux.known = 1; /* DMI knows which OSI(Linux) default needed */ + + set_osi_linux(enable); - osi_linux = enable; return; } @@ -987,12 +1054,12 @@ static int __init acpi_osi_setup(char *str) printk(KERN_INFO PREFIX "_OSI method disabled\n"); acpi_gbl_create_osi_method = FALSE; } else if (!strcmp("!Linux", str)) { - enable_osi_linux(0); + acpi_cmdline_osi_linux(0); /* !enable */ } else if (*str == '!') { if (acpi_osi_invalidate(++str) == AE_OK) printk(KERN_INFO PREFIX "Deleted _OSI(%s)\n", str); } else if (!strcmp("Linux", str)) { - enable_osi_linux(1); + acpi_cmdline_osi_linux(1); /* enable */ } else if (*osi_additional_string == '\0') { strncpy(osi_additional_string, str, OSI_STRING_LENGTH_MAX); printk(KERN_INFO PREFIX "Added _OSI(%s)\n", str); @@ -1141,6 +1208,34 @@ acpi_status acpi_os_release_object(acpi_cache_t * cache, void *object) return (AE_OK); } +/** + * acpi_dmi_dump - dump DMI slots needed for blacklist entry + * + * Returns 0 on success + */ +int acpi_dmi_dump(void) +{ + + if (!dmi_available) + return -1; + + printk(KERN_NOTICE PREFIX "DMI System Vendor: %s\n", + dmi_get_slot(DMI_SYS_VENDOR)); + printk(KERN_NOTICE PREFIX "DMI Product Name: %s\n", + dmi_get_slot(DMI_PRODUCT_NAME)); + printk(KERN_NOTICE PREFIX "DMI Product Version: %s\n", + dmi_get_slot(DMI_PRODUCT_VERSION)); + printk(KERN_NOTICE PREFIX "DMI Board Name: %s\n", + dmi_get_slot(DMI_BOARD_NAME)); + printk(KERN_NOTICE PREFIX "DMI BIOS Vendor: %s\n", + dmi_get_slot(DMI_BIOS_VENDOR)); + printk(KERN_NOTICE PREFIX "DMI BIOS Date: %s\n", + dmi_get_slot(DMI_BIOS_DATE)); + + return 0; +} + + /****************************************************************************** * * FUNCTION: acpi_os_validate_interface @@ -1160,13 +1255,29 @@ acpi_os_validate_interface (char *interface) if (!strncmp(osi_additional_string, interface, OSI_STRING_LENGTH_MAX)) return AE_OK; if (!strcmp("Linux", interface)) { - printk(KERN_WARNING PREFIX - "System BIOS is requesting _OSI(Linux)\n"); - printk(KERN_WARNING PREFIX - "If \"acpi_osi=Linux\" works better,\n" - "Please send dmidecode " - "to linux-acpi@vger.kernel.org\n"); - if(osi_linux) + + printk(KERN_NOTICE PREFIX + "BIOS _OSI(Linux) query %s%s\n", + osi_linux.enable ? "honored" : "ignored", + osi_linux.cmdline ? " via cmdline" : + osi_linux.dmi ? " via DMI" : ""); + + if (!osi_linux.dmi) { + if (acpi_dmi_dump()) + printk(KERN_NOTICE PREFIX + "[please extract dmidecode output]\n"); + printk(KERN_NOTICE PREFIX + "Please send DMI info above to " + "linux-acpi@vger.kernel.org\n"); + } + if (!osi_linux.known && !osi_linux.cmdline) { + printk(KERN_NOTICE PREFIX + "If \"acpi_osi=%sLinux\" works better, " + "please notify linux-acpi@vger.kernel.org\n", + osi_linux.enable ? "!" : ""); + } + + if (osi_linux.enable) return AE_OK; } return AE_SUPPORT; @@ -1198,28 +1309,4 @@ acpi_os_validate_address ( return AE_OK; } -#ifdef CONFIG_DMI -static int dmi_osi_linux(const struct dmi_system_id *d) -{ - printk(KERN_NOTICE "%s detected: enabling _OSI(Linux)\n", d->ident); - enable_osi_linux(1); - return 0; -} - -static struct dmi_system_id acpi_osl_dmi_table[] __initdata = { - /* - * Boxes that need _OSI(Linux) - */ - { - .callback = dmi_osi_linux, - .ident = "Intel Napa CRB", - .matches = { - DMI_MATCH(DMI_BOARD_VENDOR, "Intel Corporation"), - DMI_MATCH(DMI_BOARD_NAME, "MPAD-MSAE Customer Reference Boards"), - }, - }, - {} -}; -#endif /* CONFIG_DMI */ - #endif diff --git a/drivers/acpi/processor_throttling.c b/drivers/acpi/processor_throttling.c index 6742d7bc4777d8343d10aa34aa5d40a920f5f41a..1685b40abda7c2dba944939917a4993f8a4a5f3d 100644 --- a/drivers/acpi/processor_throttling.c +++ b/drivers/acpi/processor_throttling.c @@ -775,12 +775,12 @@ int acpi_processor_get_throttling_info(struct acpi_processor *pr) acpi_processor_get_throttling_states(pr) || acpi_processor_get_platform_limit(pr)) { - if (acpi_processor_get_fadt_info(pr)) - return 0; pr->throttling.acpi_processor_get_throttling = &acpi_processor_get_throttling_fadt; pr->throttling.acpi_processor_set_throttling = &acpi_processor_set_throttling_fadt; + if (acpi_processor_get_fadt_info(pr)) + return 0; } else { pr->throttling.acpi_processor_get_throttling = &acpi_processor_get_throttling_ptc; diff --git a/drivers/firmware/dmi-id.c b/drivers/firmware/dmi-id.c index bc132d8f79cb29922e2df5a6532777010cb14dc6..313c99cbdc62363632217d3061b7709b972b2127 100644 --- a/drivers/firmware/dmi-id.c +++ b/drivers/firmware/dmi-id.c @@ -173,8 +173,6 @@ static struct device *dmi_dev; if (dmi_get_system_info(_field)) \ sys_dmi_attributes[i++] = &sys_dmi_##_name##_attr.dev_attr.attr; -extern int dmi_available; - /* In a separate function to keep gcc 3.2 happy - do NOT merge this in dmi_id_init! */ static void __init dmi_id_init_attr_table(void) diff --git a/drivers/firmware/dmi_scan.c b/drivers/firmware/dmi_scan.c index 0cdadea7a40e9f58cd027697611bf0d18eafd1bd..5e596a7e36013dc2d24082726a0eab5b792fc354 100644 --- a/drivers/firmware/dmi_scan.c +++ b/drivers/firmware/dmi_scan.c @@ -470,3 +470,11 @@ int dmi_get_year(int field) return year; } +/** + * dmi_get_slot - return dmi_ident[slot] + * @slot: index into dmi_ident[] + */ +char *dmi_get_slot(int slot) +{ + return(dmi_ident[slot]); +} diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index 19c3ead2a90bb768f98e077ff91c050ae290e737..7b74b60a68a47b26d3addf541b3440f23bd947ad 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -168,7 +168,8 @@ struct acpi_device_flags { u32 power_manageable:1; u32 performance_manageable:1; u32 wake_capable:1; /* Wakeup(_PRW) supported? */ - u32 reserved:20; + u32 force_power_state:1; + u32 reserved:19; }; /* File System */ diff --git a/include/linux/acpi.h b/include/linux/acpi.h index e3c16c981e4627beca2b3bd2240d51e67b64918a..63f2e6ed698f5ba471efab1f7f5af1cc4942fb96 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -40,6 +40,7 @@ #include #include #include +#include #ifdef CONFIG_ACPI @@ -192,7 +193,9 @@ extern int ec_transaction(u8 command, #endif /*CONFIG_ACPI_EC*/ extern int acpi_blacklisted(void); -extern void acpi_bios_year(char *s); +#ifdef CONFIG_DMI +extern void acpi_dmi_osi_linux(int enable, const struct dmi_system_id *d); +#endif #ifdef CONFIG_ACPI_NUMA int acpi_get_pxm(acpi_handle handle); @@ -226,5 +229,5 @@ static inline int acpi_boot_table_init(void) return 0; } -#endif /* CONFIG_ACPI */ +#endif /* !CONFIG_ACPI */ #endif /*_LINUX_ACPI_H*/ diff --git a/include/linux/dmi.h b/include/linux/dmi.h index 00fc7a9c35eccdbb2b0b577e0e110fafb0b96668..5b42a659a308d046a886ca86fd05e260822a77b4 100644 --- a/include/linux/dmi.h +++ b/include/linux/dmi.h @@ -78,6 +78,8 @@ extern const struct dmi_device * dmi_find_device(int type, const char *name, extern void dmi_scan_machine(void); extern int dmi_get_year(int field); extern int dmi_name_in_vendors(const char *str); +extern int dmi_available; +extern char *dmi_get_slot(int slot); #else @@ -87,6 +89,8 @@ static inline const struct dmi_device * dmi_find_device(int type, const char *na const struct dmi_device *from) { return NULL; } static inline int dmi_get_year(int year) { return 0; } static inline int dmi_name_in_vendors(const char *s) { return 0; } +#define dmi_available 0 +static inline char *dmi_get_slot(int slot) { return NULL; } #endif