diff options
Diffstat (limited to 'drivers/acpi')
31 files changed, 1210 insertions, 210 deletions
diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index b9f923ef173..ccf6ea95f68 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig @@ -82,6 +82,12 @@ config ACPI_PROCFS_POWER and functions, which do not yet exist in /sys Say N to delete power /proc/acpi/ folders that have moved to /sys/ +config ACPI_SYSFS_POWER + bool "Future power /sys interface" + select POWER_SUPPLY + default y + ---help--- + Say N to disable power /sys interface config ACPI_PROC_EVENT bool "Deprecated /proc/acpi/event support" depends on PROC_FS @@ -103,7 +109,6 @@ config ACPI_PROC_EVENT config ACPI_AC tristate "AC Adapter" depends on X86 - select POWER_SUPPLY default y help This driver adds support for the AC Adapter object, which indicates @@ -113,7 +118,6 @@ config ACPI_AC config ACPI_BATTERY tristate "Battery" depends on X86 - select POWER_SUPPLY default y help This driver adds support for battery information through @@ -368,7 +372,6 @@ config ACPI_HOTPLUG_MEMORY config ACPI_SBS tristate "Smart Battery System" depends on X86 - select POWER_SUPPLY help This driver adds support for the Smart Battery System, another type of access to battery information, found on some laptops. diff --git a/drivers/acpi/ac.c b/drivers/acpi/ac.c index 76ed4f52beb..76b9bea98b6 100644 --- a/drivers/acpi/ac.c +++ b/drivers/acpi/ac.c @@ -31,7 +31,9 @@ #include <linux/proc_fs.h> #include <linux/seq_file.h> #endif +#ifdef CONFIG_ACPI_SYSFS_POWER #include <linux/power_supply.h> +#endif #include <acpi/acpi_bus.h> #include <acpi/acpi_drivers.h> @@ -79,7 +81,9 @@ static struct acpi_driver acpi_ac_driver = { }; struct acpi_ac { +#ifdef CONFIG_ACPI_SYSFS_POWER struct power_supply charger; +#endif struct acpi_device * device; unsigned long state; }; @@ -94,7 +98,7 @@ static const struct file_operations acpi_ac_fops = { .release = single_release, }; #endif - +#ifdef CONFIG_ACPI_SYSFS_POWER static int get_ac_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) @@ -113,7 +117,7 @@ static int get_ac_property(struct power_supply *psy, static enum power_supply_property ac_props[] = { POWER_SUPPLY_PROP_ONLINE, }; - +#endif /* -------------------------------------------------------------------------- AC Adapter Management -------------------------------------------------------------------------- */ @@ -241,7 +245,9 @@ static void acpi_ac_notify(acpi_handle handle, u32 event, void *data) acpi_bus_generate_netlink_event(device->pnp.device_class, device->dev.bus_id, event, (u32) ac->state); +#ifdef CONFIG_ACPI_SYSFS_POWER kobject_uevent(&ac->charger.dev->kobj, KOBJ_CHANGE); +#endif break; default: ACPI_DEBUG_PRINT((ACPI_DB_INFO, @@ -280,12 +286,14 @@ static int acpi_ac_add(struct acpi_device *device) #endif if (result) goto end; +#ifdef CONFIG_ACPI_SYSFS_POWER ac->charger.name = acpi_device_bid(device); ac->charger.type = POWER_SUPPLY_TYPE_MAINS; ac->charger.properties = ac_props; ac->charger.num_properties = ARRAY_SIZE(ac_props); ac->charger.get_property = get_ac_property; power_supply_register(&ac->device->dev, &ac->charger); +#endif status = acpi_install_notify_handler(device->handle, ACPI_ALL_NOTIFY, acpi_ac_notify, ac); @@ -319,8 +327,10 @@ static int acpi_ac_resume(struct acpi_device *device) old_state = ac->state; if (acpi_ac_get_state(ac)) return 0; +#ifdef CONFIG_ACPI_SYSFS_POWER if (old_state != ac->state) kobject_uevent(&ac->charger.dev->kobj, KOBJ_CHANGE); +#endif return 0; } @@ -337,8 +347,10 @@ static int acpi_ac_remove(struct acpi_device *device, int type) status = acpi_remove_notify_handler(device->handle, ACPI_ALL_NOTIFY, acpi_ac_notify); +#ifdef CONFIG_ACPI_SYSFS_POWER if (ac->charger.dev) power_supply_unregister(&ac->charger); +#endif #ifdef CONFIG_ACPI_PROCFS_POWER acpi_ac_remove_fs(device); #endif diff --git a/drivers/acpi/asus_acpi.c b/drivers/acpi/asus_acpi.c index d915fec9bf6..d25ef961415 100644 --- a/drivers/acpi/asus_acpi.c +++ b/drivers/acpi/asus_acpi.c @@ -142,6 +142,7 @@ struct asus_hotk { xxN, //M2400N, M3700N, M5200N, M6800N, S1300N, S5200N A4S, //Z81sp //(Centrino) + F3Sa, END_MODEL } model; //Models currently supported u16 event_count[128]; //count for each event TODO make this better @@ -405,7 +406,20 @@ static struct model_data model_conf[END_MODEL] = { .brightness_get = "GPLV", .mt_bt_switch = "BLED", .mt_wled = "WLED" - } + }, + + { + .name = "F3Sa", + .mt_bt_switch = "BLED", + .mt_wled = "WLED", + .mt_mled = "MLED", + .brightness_get = "GPLV", + .brightness_set = "SPLV", + .mt_lcd_switch = "\\_SB.PCI0.SBRG.EC0._Q10", + .lcd_status = "\\_SB.PCI0.SBRG.EC0.RPIN", + .display_get = "\\ADVG", + .display_set = "SDSP", + }, }; @@ -710,15 +724,8 @@ static int get_lcd_state(void) { int lcd = 0; - if (hotk->model != L3H) { - /* We don't have to check anything if we are here */ - if (!read_acpi_int(NULL, hotk->methods->lcd_status, &lcd)) - printk(KERN_WARNING - "Asus ACPI: Error reading LCD status\n"); - - if (hotk->model == L2D) - lcd = ~lcd; - } else { /* L3H and the like have to be handled differently */ + if (hotk->model == L3H) { + /* L3H and the like have to be handled differently */ acpi_status status = 0; struct acpi_object_list input; union acpi_object mt_params[2]; @@ -745,6 +752,32 @@ static int get_lcd_state(void) if (out_obj.type == ACPI_TYPE_INTEGER) /* That's what the AML code does */ lcd = out_obj.integer.value >> 8; + } else if (hotk->model == F3Sa) { + unsigned long tmp; + union acpi_object param; + struct acpi_object_list input; + acpi_status status; + + /* Read pin 11 */ + param.type = ACPI_TYPE_INTEGER; + param.integer.value = 0x11; + input.count = 1; + input.pointer = ¶m; + + status = acpi_evaluate_integer(NULL, hotk->methods->lcd_status, + &input, &tmp); + if (status != AE_OK) + return -1; + + lcd = tmp; + } else { + /* We don't have to check anything if we are here */ + if (!read_acpi_int(NULL, hotk->methods->lcd_status, &lcd)) + printk(KERN_WARNING + "Asus ACPI: Error reading LCD status\n"); + + if (hotk->model == L2D) + lcd = ~lcd; } return (lcd & 1); @@ -1134,6 +1167,8 @@ static int asus_model_match(char *model) return W5A; else if (strncmp(model, "A4S", 3) == 0) return A4S; + else if (strncmp(model, "F3Sa", 4) == 0) + return F3Sa; else return END_MODEL; } diff --git a/drivers/acpi/battery.c b/drivers/acpi/battery.c index 8f7505d304b..f6215e80980 100644 --- a/drivers/acpi/battery.c +++ b/drivers/acpi/battery.c @@ -40,7 +40,9 @@ #include <acpi/acpi_bus.h> #include <acpi/acpi_drivers.h> +#ifdef CONFIG_ACPI_SYSFS_POWER #include <linux/power_supply.h> +#endif #define ACPI_BATTERY_VALUE_UNKNOWN 0xFFFFFFFF @@ -86,7 +88,9 @@ MODULE_DEVICE_TABLE(acpi, battery_device_ids); struct acpi_battery { struct mutex lock; +#ifdef CONFIG_ACPI_SYSFS_POWER struct power_supply bat; +#endif struct acpi_device *device; unsigned long update_time; int current_now; @@ -117,6 +121,7 @@ inline int acpi_battery_present(struct acpi_battery *battery) return battery->device->status.battery_present; } +#ifdef CONFIG_ACPI_SYSFS_POWER static int acpi_battery_technology(struct acpi_battery *battery) { if (!strcasecmp("NiCd", battery->type)) @@ -189,6 +194,9 @@ static int acpi_battery_get_property(struct power_supply *psy, case POWER_SUPPLY_PROP_MANUFACTURER: val->strval = battery->oem_info; break; + case POWER_SUPPLY_PROP_SERIAL_NUMBER: + val->strval = battery->serial_number; + break; default: return -EINVAL; } @@ -207,6 +215,7 @@ static enum power_supply_property charge_battery_props[] = { POWER_SUPPLY_PROP_CHARGE_NOW, POWER_SUPPLY_PROP_MODEL_NAME, POWER_SUPPLY_PROP_MANUFACTURER, + POWER_SUPPLY_PROP_SERIAL_NUMBER, }; static enum power_supply_property energy_battery_props[] = { @@ -221,7 +230,9 @@ static enum power_supply_property energy_battery_props[] = { POWER_SUPPLY_PROP_ENERGY_NOW, POWER_SUPPLY_PROP_MODEL_NAME, POWER_SUPPLY_PROP_MANUFACTURER, + POWER_SUPPLY_PROP_SERIAL_NUMBER, }; +#endif #ifdef CONFIG_ACPI_PROCFS_POWER inline char *acpi_battery_units(struct acpi_battery *battery) @@ -398,6 +409,7 @@ static int acpi_battery_init_alarm(struct acpi_battery *battery) return acpi_battery_set_alarm(battery); } +#ifdef CONFIG_ACPI_SYSFS_POWER static ssize_t acpi_battery_alarm_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -429,11 +441,6 @@ static int sysfs_add_battery(struct acpi_battery *battery) { int result; - battery->update_time = 0; - result = acpi_battery_get_info(battery); - acpi_battery_init_alarm(battery); - if (result) - return result; if (battery->power_unit) { battery->bat.properties = charge_battery_props; battery->bat.num_properties = @@ -462,18 +469,31 @@ static void sysfs_remove_battery(struct acpi_battery *battery) power_supply_unregister(&battery->bat); battery->bat.dev = NULL; } +#endif static int acpi_battery_update(struct acpi_battery *battery) { - int result = acpi_battery_get_status(battery); + int result; + result = acpi_battery_get_status(battery); if (result) return result; +#ifdef CONFIG_ACPI_SYSFS_POWER if (!acpi_battery_present(battery)) { sysfs_remove_battery(battery); + battery->update_time = 0; return 0; } +#endif + if (!battery->update_time) { + result = acpi_battery_get_info(battery); + if (result) + return result; + acpi_battery_init_alarm(battery); + } +#ifdef CONFIG_ACPI_SYSFS_POWER if (!battery->bat.dev) sysfs_add_battery(battery); +#endif return acpi_battery_get_state(battery); } @@ -767,9 +787,11 @@ static void acpi_battery_notify(acpi_handle handle, u32 event, void *data) acpi_bus_generate_netlink_event(device->pnp.device_class, device->dev.bus_id, event, acpi_battery_present(battery)); +#ifdef CONFIG_ACPI_SYSFS_POWER /* acpi_batter_update could remove power_supply object */ if (battery->bat.dev) kobject_uevent(&battery->bat.dev->kobj, KOBJ_CHANGE); +#endif } static int acpi_battery_add(struct acpi_device *device) @@ -828,7 +850,9 @@ static int acpi_battery_remove(struct acpi_device *device, int type) #ifdef CONFIG_ACPI_PROCFS_POWER acpi_battery_remove_fs(device); #endif +#ifdef CONFIG_ACPI_SYSFS_POWER sysfs_remove_battery(battery); +#endif mutex_destroy(&battery->lock); kfree(battery); return 0; diff --git a/drivers/acpi/bay.c b/drivers/acpi/bay.c index 6daf6088ac8..477711435b2 100644 --- a/drivers/acpi/bay.c +++ b/drivers/acpi/bay.c @@ -46,6 +46,12 @@ MODULE_LICENSE("GPL"); printk(KERN_DEBUG PREFIX "%s: %s\n", prefix, s); } static void bay_notify(acpi_handle handle, u32 event, void *data); +static const struct acpi_device_id bay_device_ids[] = { + {"LNXIOBAY", 0}, + {"", 0}, +}; +MODULE_DEVICE_TABLE(acpi, bay_device_ids); + struct bay { acpi_handle handle; char *name; diff --git a/drivers/acpi/blacklist.c b/drivers/acpi/blacklist.c index 3ec110ce00c..3ade0168098 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 <len.brown@intel.com> * Copyright (C) 2002 Andy Grover <andrew.grover@intel.com> @@ -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. @@ -67,8 +70,6 @@ static struct acpi_blacklist_item acpi_blacklist[] __initdata = { /* IBM 600E - _ADR should return 7, but it returns 1 */ {"IBM ", "TP600E ", 0x00000105, ACPI_SIG_DSDT, less_than_or_equal, "Incorrect _ADR", 1}, - {"ASUS\0\0", "P2B-S ", 0, ACPI_SIG_DSDT, all_versions, - "Bogus PCI routing", 1}, {""} }; @@ -165,5 +166,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 49d432d0a12..1b4cf984b08 100644 --- a/drivers/acpi/bus.c +++ b/drivers/acpi/bus.c @@ -29,7 +29,6 @@ #include <linux/list.h> #include <linux/sched.h> #include <linux/pm.h> -#include <linux/pm_legacy.h> #include <linux/device.h> #include <linux/proc_fs.h> #ifdef CONFIG_X86 @@ -201,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; @@ -744,7 +743,7 @@ static int __init acpi_bus_init(void) return -ENODEV; } -decl_subsys(acpi, NULL, NULL); +struct kobject *acpi_kobj; static int __init acpi_init(void) { @@ -756,24 +755,23 @@ static int __init acpi_init(void) return -ENODEV; } - result = firmware_register(&acpi_subsys); - if (result < 0) - printk(KERN_WARNING "%s: firmware_register error: %d\n", - __FUNCTION__, result); + acpi_kobj = kobject_create_and_add("acpi", firmware_kobj); + if (!acpi_kobj) { + printk(KERN_WARNING "%s: kset create error\n", __FUNCTION__); + acpi_kobj = NULL; + } result = acpi_bus_init(); if (!result) { -#ifdef CONFIG_PM_LEGACY - if (!PM_IS_ACTIVE()) - pm_active = 1; + if (!(pm_flags & PM_APM)) + pm_flags |= PM_ACPI; else { printk(KERN_INFO PREFIX "APM is already active, exiting\n"); disable_acpi(); result = -ENODEV; } -#endif } else disable_acpi(); diff --git a/drivers/acpi/debug.c b/drivers/acpi/debug.c index bf513e07b77..6df564f4ca6 100644 --- a/drivers/acpi/debug.c +++ b/drivers/acpi/debug.c @@ -130,6 +130,63 @@ static int param_get_debug_level(char *buffer, struct kernel_param *kp) { module_param_call(debug_layer, param_set_uint, param_get_debug_layer, &acpi_dbg_layer, 0644); module_param_call(debug_level, param_set_uint, param_get_debug_level, &acpi_dbg_level, 0644); +static char trace_method_name[6]; +module_param_string(trace_method_name, trace_method_name, 6, 0644); +static unsigned int trace_debug_layer; +module_param(trace_debug_layer, uint, 0644); +static unsigned int trace_debug_level; +module_param(trace_debug_level, uint, 0644); + +static int param_set_trace_state(const char *val, struct kernel_param *kp) +{ + int result = 0; + + if (!strncmp(val, "enable", strlen("enable") - 1)) { + result = acpi_debug_trace(trace_method_name, trace_debug_level, + trace_debug_layer, 0); + if (result) + result = -EBUSY; + goto exit; + } + + if (!strncmp(val, "disable", strlen("disable") - 1)) { + int name = 0; + result = acpi_debug_trace((char *)&name, trace_debug_level, + trace_debug_layer, 0); + if (result) + result = -EBUSY; + goto exit; + } + + if (!strncmp(val, "1", 1)) { + result = acpi_debug_trace(trace_method_name, trace_debug_level, + trace_debug_layer, 1); + if (result) + result = -EBUSY; + goto exit; + } + + result = -EINVAL; +exit: + return result; +} + +static int param_get_trace_state(char *buffer, struct kernel_param *kp) +{ + if (!acpi_gbl_trace_method_name) + return sprintf(buffer, "disable"); + else { + if (acpi_gbl_trace_flags & 1) + return sprintf(buffer, "1"); + else + return sprintf(buffer, "enable"); + } + return 0; +} + +module_param_call(trace_state, param_set_trace_state, param_get_trace_state, + NULL, 0644); + /* -------------------------------------------------------------------------- FS Interface (/proc) -------------------------------------------------------------------------- */ diff --git a/drivers/acpi/dock.c b/drivers/acpi/dock.c index 1dabdf4c07b..b3dec2101e2 100644 --- a/drivers/acpi/dock.c +++ b/drivers/acpi/dock.c @@ -51,6 +51,12 @@ static struct atomic_notifier_head dock_notifier_list; static struct platform_device *dock_device; static char dock_device_name[] = "dock"; +static const struct acpi_device_id dock_device_ids[] = { + {"LNXDOCK", 0}, + {"", 0}, +}; +MODULE_DEVICE_TABLE(acpi, dock_device_ids); + struct dock_station { acpi_handle handle; unsigned long last_dock_time; diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index d411017f8c0..7222a18a031 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 <linux/kernel.h> #include <linux/module.h> #include <linux/init.h> @@ -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"); @@ -563,7 +573,7 @@ acpi_ec_space_handler(u32 function, acpi_physical_address address, void *handler_context, void *region_context) { struct acpi_ec *ec = handler_context; - int result = 0, i = 0; + int result = 0, i; u8 temp = 0; if ((address > 0xFF) || !value || !handler_context) @@ -575,7 +585,18 @@ acpi_ec_space_handler(u32 function, acpi_physical_address address, if (bits != 8 && acpi_strict) return AE_BAD_PARAMETER; - while (bits - i > 0) { + acpi_ec_burst_enable(ec); + + if (function == ACPI_READ) { + result = acpi_ec_read(ec, address, &temp); + *value = temp; + } else { + temp = 0xff & (*value); + result = acpi_ec_write(ec, address, temp); + } + + for (i = 8; unlikely(bits - i > 0); i += 8) { + ++address; if (function == ACPI_READ) { result = acpi_ec_read(ec, address, &temp); (*value) |= ((acpi_integer)temp) << i; @@ -583,10 +604,10 @@ acpi_ec_space_handler(u32 function, acpi_physical_address address, temp = 0xff & ((*value) >> i); result = acpi_ec_write(ec, address, temp); } - i += 8; - ++address; } + acpi_ec_burst_disable(ec); + switch (result) { case -EINVAL: return AE_BAD_PARAMETER; @@ -892,6 +913,17 @@ static int acpi_ec_stop(struct acpi_device *device, int type) return 0; } +int __init acpi_boot_ec_enable(void) +{ + if (!boot_ec || boot_ec->handlers_installed) + return 0; + if (!ec_install_handlers(boot_ec)) { + first_ec = boot_ec; + return 0; + } + return -EFAULT; +} + int __init acpi_ec_ecdt_probe(void) { int ret; @@ -924,9 +956,10 @@ int __init acpi_ec_ecdt_probe(void) goto error; /* We really need to limit this workaround, the only ASUS, * which needs it, has fake EC._INI method, so use it as flag. + * Keep boot_ec struct as it will be needed soon. */ if (ACPI_FAILURE(acpi_get_handle(boot_ec->handle, "_INI", &x))) - goto error; + return -ENODEV; } ret = ec_install_handlers(boot_ec); diff --git a/drivers/acpi/events/evgpe.c b/drivers/acpi/events/evgpe.c index e22f4a973c0..056b7884482 100644 --- a/drivers/acpi/events/evgpe.c +++ b/drivers/acpi/events/evgpe.c @@ -270,18 +270,18 @@ acpi_status acpi_ev_disable_gpe(struct acpi_gpe_event_info *gpe_event_info) case ACPI_GPE_TYPE_WAKE_RUN: ACPI_CLEAR_BIT(gpe_event_info->flags, ACPI_GPE_WAKE_ENABLED); - /*lint -fallthrough */ + /* fallthrough */ case ACPI_GPE_TYPE_RUNTIME: /* Disable the requested runtime GPE */ ACPI_CLEAR_BIT(gpe_event_info->flags, ACPI_GPE_RUN_ENABLED); - status = acpi_hw_write_gpe_enable_reg(gpe_event_info); - break; + + /* fallthrough */ default: - return_ACPI_STATUS(AE_BAD_PARAMETER); + acpi_hw_write_gpe_enable_reg(gpe_event_info); } return_ACPI_STATUS(AE_OK); diff --git a/drivers/acpi/events/evregion.c b/drivers/acpi/events/evregion.c index e99f0c435a4..58ad09725dd 100644 --- a/drivers/acpi/events/evregion.c +++ b/drivers/acpi/events/evregion.c @@ -344,7 +344,7 @@ acpi_ev_address_space_dispatch(union acpi_operand_object *region_obj, * setup will potentially execute control methods * (e.g., _REG method for this region) */ - acpi_ex_relinquish_interpreter(); + acpi_ex_exit_interpreter(); status = region_setup(region_obj, ACPI_REGION_ACTIVATE, handler_desc->address_space.context, @@ -352,7 +352,7 @@ acpi_ev_address_space_dispatch(union acpi_operand_object *region_obj, /* Re-enter the interpreter */ - acpi_ex_reacquire_interpreter(); + acpi_ex_enter_interpreter(); /* Check for failure of the Region Setup */ @@ -405,7 +405,7 @@ acpi_ev_address_space_dispatch(union acpi_operand_object *region_obj, * exit the interpreter because the handler *might* block -- we don't * know what it will do, so we can't hold the lock on the intepreter. */ - acpi_ex_relinquish_interpreter(); + acpi_ex_exit_interpreter(); } /* Call the handler */ @@ -426,7 +426,7 @@ acpi_ev_address_space_dispatch(union acpi_operand_object *region_obj, * We just returned from a non-default handler, we must re-enter the * interpreter */ - acpi_ex_reacquire_interpreter(); + acpi_ex_enter_interpreter(); } return_ACPI_STATUS(status); diff --git a/drivers/acpi/fan.c b/drivers/acpi/fan.c index a5a5532db26..a6e149d692c 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/hardware/hwsleep.c b/drivers/acpi/hardware/hwsleep.c index 81b24842970..fd1c4ba6336 100644 --- a/drivers/acpi/hardware/hwsleep.c +++ b/drivers/acpi/hardware/hwsleep.c @@ -192,18 +192,13 @@ acpi_status acpi_enter_sleep_state_prep(u8 sleep_state) arg.type = ACPI_TYPE_INTEGER; arg.integer.value = sleep_state; - /* Run the _PTS and _GTS methods */ + /* Run the _PTS method */ status = acpi_evaluate_object(NULL, METHOD_NAME__PTS, &arg_list, NULL); if (ACPI_FAILURE(status) && status != AE_NOT_FOUND) { return_ACPI_STATUS(status); } - status = acpi_evaluate_object(NULL, METHOD_NAME__GTS, &arg_list, NULL); - if (ACPI_FAILURE(status) && status != AE_NOT_FOUND) { - return_ACPI_STATUS(status); - } - /* Setup the argument to _SST */ switch (sleep_state) { @@ -234,10 +229,6 @@ acpi_status acpi_enter_sleep_state_prep(u8 sleep_state) "While executing method _SST")); } - /* Disable/Clear all GPEs */ - - status = acpi_hw_disable_all_gpes(); - return_ACPI_STATUS(status); } @@ -262,6 +253,8 @@ acpi_status asmlinkage acpi_enter_sleep_state(u8 sleep_state) struct acpi_bit_register_info *sleep_type_reg_info; struct acpi_bit_register_info *sleep_enable_reg_info; u32 in_value; + struct acpi_object_list arg_list; + union acpi_object arg; acpi_status status; ACPI_FUNCTION_TRACE(acpi_enter_sleep_state); @@ -307,6 +300,18 @@ acpi_status asmlinkage acpi_enter_sleep_state(u8 sleep_state) return_ACPI_STATUS(status); } + /* Execute the _GTS method */ + + arg_list.count = 1; + arg_list.pointer = &arg; + arg.type = ACPI_TYPE_INTEGER; + arg.integer.value = sleep_state; + + status = acpi_evaluate_object(NULL, METHOD_NAME__GTS, &arg_list, NULL); + if (ACPI_FAILURE(status) && status != AE_NOT_FOUND) { + return_ACPI_STATUS(status); + } + /* Get current value of PM1A control */ status = acpi_hw_register_read(ACPI_REGISTER_PM1_CONTROL, &PM1Acontrol); @@ -473,17 +478,18 @@ ACPI_EXPORT_SYMBOL(acpi_enter_sleep_state_s4bios) /******************************************************************************* * - * FUNCTION: acpi_leave_sleep_state + * FUNCTION: acpi_leave_sleep_state_prep * - * PARAMETERS: sleep_state - Which sleep state we just exited + * PARAMETERS: sleep_state - Which sleep state we are exiting * * RETURN: Status * - * DESCRIPTION: Perform OS-independent ACPI cleanup after a sleep - * Called with interrupts ENABLED. + * DESCRIPTION: Perform the first state of OS-independent ACPI cleanup after a + * sleep. + * Called with interrupts DISABLED. * ******************************************************************************/ -acpi_status acpi_leave_sleep_state(u8 sleep_state) +acpi_status acpi_leave_sleep_state_prep(u8 sleep_state) { struct acpi_object_list arg_list; union acpi_object arg; @@ -493,7 +499,7 @@ acpi_status acpi_leave_sleep_state(u8 sleep_state) u32 PM1Acontrol; u32 PM1Bcontrol; - ACPI_FUNCTION_TRACE(acpi_leave_sleep_state); + ACPI_FUNCTION_TRACE(acpi_leave_sleep_state_prep); /* * Set SLP_TYPE and SLP_EN to state S0. @@ -540,6 +546,41 @@ acpi_status acpi_leave_sleep_state(u8 sleep_state) } } + /* Execute the _BFS method */ + + arg_list.count = 1; + arg_list.pointer = &arg; + arg.type = ACPI_TYPE_INTEGER; + arg.integer.value = sleep_state; + + status = acpi_evaluate_object(NULL, METHOD_NAME__BFS, &arg_list, NULL); + if (ACPI_FAILURE(status) && status != AE_NOT_FOUND) { + ACPI_EXCEPTION((AE_INFO, status, "During Method _BFS")); + } + + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_leave_sleep_state + * + * PARAMETERS: sleep_state - Which sleep state we just exited + * + * RETURN: Status + * + * DESCRIPTION: Perform OS-independent ACPI cleanup after a sleep + * Called with interrupts ENABLED. + * + ******************************************************************************/ +acpi_status acpi_leave_sleep_state(u8 sleep_state) +{ + struct acpi_object_list arg_list; + union acpi_object arg; + acpi_status status; + + ACPI_FUNCTION_TRACE(acpi_leave_sleep_state); + /* Ensure enter_sleep_state_prep -> enter_sleep_state ordering */ acpi_gbl_sleep_type_a = ACPI_SLEEP_TYPE_INVALID; @@ -558,12 +599,6 @@ acpi_status acpi_leave_sleep_state(u8 sleep_state) ACPI_EXCEPTION((AE_INFO, status, "During Method _SST")); } - arg.integer.value = sleep_state; - status = acpi_evaluate_object(NULL, METHOD_NAME__BFS, &arg_list, NULL); - if (ACPI_FAILURE(status) && status != AE_NOT_FOUND) { - ACPI_EXCEPTION((AE_INFO, status, "During Method _BFS")); - } - /* * GPEs must be enabled before _WAK is called as GPEs * might get fired there diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c index e3a673a0084..e96f3d933f6 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; } @@ -207,8 +250,12 @@ acpi_physical_address __init acpi_os_get_root_pointer(void) "System description tables not found\n"); return 0; } - } else - return acpi_find_rsdp(); + } else { + acpi_physical_address pa = 0; + + acpi_find_root_pointer(&pa); + return pa; + } } void __iomem *acpi_os_map_memory(acpi_physical_address phys, acpi_size size) @@ -964,13 +1011,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; +} + +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 */ - if (osi_linux != enable) - printk(KERN_INFO PREFIX "%sabled _OSI(Linux)\n", - enable ? "En": "Dis"); + set_osi_linux(enable); - osi_linux = enable; return; } @@ -987,12 +1058,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 +1212,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 +1259,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 +1313,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/pci_irq.c b/drivers/acpi/pci_irq.c index dd3186abe07..62010c2481b 100644 --- a/drivers/acpi/pci_irq.c +++ b/drivers/acpi/pci_irq.c @@ -429,6 +429,15 @@ int acpi_pci_irq_enable(struct pci_dev *dev) &polarity, &link, acpi_pci_allocate_irq); + if (irq < 0) { + /* + * IDE legacy mode controller IRQs are magic. Why do compat + * extensions always make such a nasty mess. + */ + if (dev->class >> 8 == PCI_CLASS_STORAGE_IDE && + (dev->class & 0x05) == 0) + return 0; + } /* * No IRQ known to the ACPI subsystem - maybe the BIOS / * driver reported one, then use it. Exit in any case. diff --git a/drivers/acpi/pci_link.c b/drivers/acpi/pci_link.c index c9f526e5539..5400ea173f6 100644 --- a/drivers/acpi/pci_link.c +++ b/drivers/acpi/pci_link.c @@ -911,7 +911,7 @@ __setup("acpi_irq_balance", acpi_irq_balance_set); /* FIXME: we will remove this interface after all drivers call pci_disable_device */ static struct sysdev_class irqrouter_sysdev_class = { - set_kset_name("irqrouter"), + .name = "irqrouter", .resume = irqrouter_resume, }; diff --git a/drivers/acpi/processor_core.c b/drivers/acpi/processor_core.c index e48ee4f8749..c53113e1800 100644 --- a/drivers/acpi/processor_core.c +++ b/drivers/acpi/processor_core.c @@ -812,11 +812,18 @@ static int is_processor_present(acpi_handle handle) status = acpi_evaluate_integer(handle, "_STA", NULL, &sta); - if (ACPI_FAILURE(status) || !(sta & ACPI_STA_DEVICE_PRESENT)) { - ACPI_EXCEPTION((AE_INFO, status, "Processor Device is not present")); - return 0; - } - return 1; + /* + * if a processor object does not have an _STA object, + * OSPM assumes that the processor is present. + */ + if (status == AE_NOT_FOUND) + return 1; + + if (ACPI_SUCCESS(status) && (sta & ACPI_STA_DEVICE_PRESENT)) + return 1; + + ACPI_EXCEPTION((AE_INFO, status, "Processor Device is not present")); + return 0; } static diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c index 2fe34cc73c1..199ea214615 100644 --- a/drivers/acpi/processor_idle.c +++ b/drivers/acpi/processor_idle.c @@ -38,7 +38,7 @@ #include <linux/dmi.h> #include <linux/moduleparam.h> #include <linux/sched.h> /* need_resched() */ -#include <linux/latency.h> +#include <linux/pm_qos_params.h> #include <linux/clockchips.h> #include <linux/cpuidle.h> @@ -76,7 +76,11 @@ static void (*pm_idle_save) (void) __read_mostly; #define PM_TIMER_TICKS_TO_US(p) (((p) * 1000)/(PM_TIMER_FREQUENCY/1000)) static unsigned int max_cstate __read_mostly = ACPI_PROCESSOR_MAX_POWER; +#ifdef CONFIG_CPU_IDLE module_param(max_cstate, uint, 0000); +#else +module_param(max_cstate, uint, 0644); +#endif static unsigned int nocst __read_mostly; module_param(nocst, uint, 0000); @@ -353,6 +357,26 @@ int acpi_processor_resume(struct acpi_device * device) return 0; } +#if defined (CONFIG_GENERIC_TIME) && defined (CONFIG_X86_TSC) +static int tsc_halts_in_c(int state) +{ + switch (boot_cpu_data.x86_vendor) { + case X86_VENDOR_AMD: + /* + * AMD Fam10h TSC will tick in all + * C/P/S0/S1 states when this bit is set. + */ + if (boot_cpu_has(X86_FEATURE_CONSTANT_TSC)) + return 0; + /*FALL THROUGH*/ + case X86_VENDOR_INTEL: + /* Several cases known where TSC halts in C2 too */ + default: + return state > ACPI_STATE_C1; + } +} +#endif + #ifndef CONFIG_CPU_IDLE static void acpi_processor_idle(void) { @@ -512,7 +536,8 @@ static void acpi_processor_idle(void) #if defined (CONFIG_GENERIC_TIME) && defined (CONFIG_X86_TSC) /* TSC halts in C2, so notify users */ - mark_tsc_unstable("possible TSC halt in C2"); + if (tsc_halts_in_c(ACPI_STATE_C2)) + mark_tsc_unstable("possible TSC halt in C2"); #endif /* Compute time (ticks) that we were actually asleep */ sleep_ticks = ticks_elapsed(t1, t2); @@ -530,6 +555,7 @@ static void acpi_processor_idle(void) break; case ACPI_STATE_C3: + acpi_unlazy_tlb(smp_processor_id()); /* * Must be done before busmaster disable as we might * need to access HPET ! @@ -575,7 +601,8 @@ static void acpi_processor_idle(void) #if defined (CONFIG_GENERIC_TIME) && defined (CONFIG_X86_TSC) /* TSC halts in C3, so notify users */ - mark_tsc_unstable("TSC halts in C3"); + if (tsc_halts_in_c(ACPI_STATE_C3)) + mark_tsc_unstable("TSC halts in C3"); #endif /* Compute time (ticks) that we were actually asleep */ sleep_ticks = ticks_elapsed(t1, t2); @@ -621,7 +648,8 @@ static void acpi_processor_idle(void) 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.state->latency <= + pm_qos_requirement(PM_QOS_CPU_DMA_LATENCY)) { cx->promotion.count++; cx->demotion.count = 0; if (cx->promotion.count >= @@ -665,7 +693,8 @@ static void acpi_processor_idle(void) * 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()) { + pr->power.state->latency > + pm_qos_requirement(PM_QOS_CPU_DMA_LATENCY)) { if (cx->demotion.state) next_state = cx->demotion.state; } @@ -1173,7 +1202,7 @@ static int acpi_processor_power_seq_show(struct seq_file *seq, void *offset) "maximum allowed latency: %d usec\n", pr->power.state ? pr->power.state - pr->power.states : 0, max_cstate, (unsigned)pr->power.bm_activity, - system_latency_constraint()); + pm_qos_requirement(PM_QOS_CPU_DMA_LATENCY)); seq_puts(seq, "states:\n"); @@ -1419,6 +1448,7 @@ static int acpi_idle_enter_simple(struct cpuidle_device *dev, return 0; } + acpi_unlazy_tlb(smp_processor_id()); /* * Must be done before busmaster disable as we might need to * access HPET ! @@ -1439,7 +1469,8 @@ static int acpi_idle_enter_simple(struct cpuidle_device *dev, #if defined (CONFIG_GENERIC_TIME) && defined (CONFIG_X86_TSC) /* TSC could halt in idle, so notify users */ - mark_tsc_unstable("TSC halts in idle");; + if (tsc_halts_in_c(cx->type)) + mark_tsc_unstable("TSC halts in idle");; #endif sleep_ticks = ticks_elapsed(t1, t2); @@ -1550,7 +1581,8 @@ static int acpi_idle_enter_bm(struct cpuidle_device *dev, #if defined (CONFIG_GENERIC_TIME) && defined (CONFIG_X86_TSC) /* TSC could halt in idle, so notify users */ - mark_tsc_unstable("TSC halts in idle"); + if (tsc_halts_in_c(ACPI_STATE_C3)) + mark_tsc_unstable("TSC halts in idle"); #endif sleep_ticks = ticks_elapsed(t1, t2); /* Tell the scheduler how much we idled: */ @@ -1688,8 +1720,9 @@ int __cpuinit acpi_processor_power_init(struct acpi_processor *pr, "ACPI: processor limited to max C-state %d\n", max_cstate); first_run++; -#if !defined (CONFIG_CPU_IDLE) && defined (CONFIG_SMP) - register_latency_notifier(&acpi_processor_latency_notifier); +#if !defined(CONFIG_CPU_IDLE) && defined(CONFIG_SMP) + pm_qos_add_notifier(PM_QOS_CPU_DMA_LATENCY, + &acpi_processor_latency_notifier); #endif } @@ -1776,7 +1809,8 @@ int acpi_processor_power_exit(struct acpi_processor *pr, */ cpu_idle_wait(); #ifdef CONFIG_SMP - unregister_latency_notifier(&acpi_processor_latency_notifier); + pm_qos_remove_notifier(PM_QOS_CPU_DMA_LATENCY, + &acpi_processor_latency_notifier); #endif } #endif diff --git a/drivers/acpi/processor_throttling.c b/drivers/acpi/processor_throttling.c index 6742d7bc477..1685b40abda 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/acpi/sbs.c b/drivers/acpi/sbs.c index 22cb95b349e..f136c7d3b3c 100644 --- a/drivers/acpi/sbs.c +++ b/drivers/acpi/sbs.c @@ -40,7 +40,9 @@ #include <linux/jiffies.h> #include <linux/delay.h> +#ifdef CONFIG_ACPI_SYSFS_POWER #include <linux/power_supply.h> +#endif #include "sbshc.h" @@ -80,7 +82,9 @@ static const struct acpi_device_id sbs_device_ids[] = { MODULE_DEVICE_TABLE(acpi, sbs_device_ids); struct acpi_battery { +#ifdef CONFIG_ACPI_SYSFS_POWER struct power_supply bat; +#endif struct acpi_sbs *sbs; #ifdef CONFIG_ACPI_PROCFS_POWER struct proc_dir_entry *proc_entry; @@ -113,7 +117,9 @@ struct acpi_battery { #define to_acpi_battery(x) container_of(x, struct acpi_battery, bat); struct acpi_sbs { +#ifdef CONFIG_ACPI_SYSFS_POWER struct power_supply charger; +#endif struct acpi_device *device; struct acpi_smb_hc *hc; struct mutex lock; @@ -157,6 +163,7 @@ static inline int acpi_battery_scale(struct acpi_battery *battery) acpi_battery_ipscale(battery); } +#ifdef CONFIG_ACPI_SYSFS_POWER static int sbs_get_ac_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) @@ -294,6 +301,7 @@ static enum power_supply_property sbs_energy_battery_props[] = { POWER_SUPPLY_PROP_MODEL_NAME, POWER_SUPPLY_PROP_MANUFACTURER, }; +#endif /* -------------------------------------------------------------------------- Smart Battery System Management @@ -429,6 +437,7 @@ static int acpi_ac_get_present(struct acpi_sbs *sbs) return result; } +#ifdef CONFIG_ACPI_SYSFS_POWER static ssize_t acpi_battery_alarm_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -458,6 +467,7 @@ static struct device_attribute alarm_attr = { .show = acpi_battery_alarm_show, .store = acpi_battery_alarm_store, }; +#endif /* -------------------------------------------------------------------------- FS Interface (/proc/acpi) @@ -793,6 +803,7 @@ static int acpi_battery_add(struct acpi_sbs *sbs, int id) &acpi_battery_state_fops, &acpi_battery_alarm_fops, battery); #endif +#ifdef CONFIG_ACPI_SYSFS_POWER battery->bat.name = battery->name; battery->bat.type = POWER_SUPPLY_TYPE_BATTERY; if (!acpi_battery_mode(battery)) { @@ -813,6 +824,7 @@ static int acpi_battery_add(struct acpi_sbs *sbs, int id) goto end; battery->have_sysfs_alarm = 1; end: +#endif printk(KERN_INFO PREFIX "%s [%s]: Battery Slot [%s] (battery %s)\n", ACPI_SBS_DEVICE_NAME, acpi_device_bid(sbs->device), battery->name, sbs->battery->present ? "present" : "absent"); @@ -822,12 +834,13 @@ static int acpi_battery_add(struct acpi_sbs *sbs, int id) static void acpi_battery_remove(struct acpi_sbs *sbs, int id) { struct acpi_battery *battery = &sbs->battery[id]; - +#ifdef CONFIG_ACPI_SYSFS_POWER if (battery->bat.dev) { if (battery->have_sysfs_alarm) device_remove_file(battery->bat.dev, &alarm_attr); power_supply_unregister(&battery->bat); } +#endif #ifdef CONFIG_ACPI_PROCFS_POWER if (battery->proc_entry) acpi_sbs_remove_fs(&battery->proc_entry, acpi_battery_dir); @@ -848,12 +861,14 @@ static int acpi_charger_add(struct acpi_sbs *sbs) if (result) goto end; #endif +#ifdef CONFIG_ACPI_SYSFS_POWER sbs->charger.name = "sbs-charger"; sbs->charger.type = POWER_SUPPLY_TYPE_MAINS; sbs->charger.properties = sbs_ac_props; sbs->charger.num_properties = ARRAY_SIZE(sbs_ac_props); sbs->charger.get_property = sbs_get_ac_property; power_supply_register(&sbs->device->dev, &sbs->charger); +#endif printk(KERN_INFO PREFIX "%s [%s]: AC Adapter [%s] (%s)\n", ACPI_SBS_DEVICE_NAME, acpi_device_bid(sbs->device), ACPI_AC_DIR_NAME, sbs->charger_present ? "on-line" : "off-line"); @@ -863,8 +878,10 @@ static int acpi_charger_add(struct acpi_sbs *sbs) static void acpi_charger_remove(struct acpi_sbs *sbs) { +#ifdef CONFIG_ACPI_SYSFS_POWER if (sbs->charger.dev) power_supply_unregister(&sbs->charger); +#endif #ifdef CONFIG_ACPI_PROCFS_POWER if (sbs->charger_entry) acpi_sbs_remove_fs(&sbs->charger_entry, acpi_ac_dir); @@ -885,7 +902,9 @@ void acpi_sbs_callback(void *context) ACPI_SBS_NOTIFY_STATUS, sbs->charger_present); #endif +#ifdef CONFIG_ACPI_SYSFS_POWER kobject_uevent(&sbs->charger.dev->kobj, KOBJ_CHANGE); +#endif } if (sbs->manager_present) { for (id = 0; id < MAX_SBS_BAT; ++id) { @@ -902,7 +921,9 @@ void acpi_sbs_callback(void *context) ACPI_SBS_NOTIFY_STATUS, bat->present); #endif +#ifdef CONFIG_ACPI_SYSFS_POWER kobject_uevent(&bat->bat.dev->kobj, KOBJ_CHANGE); +#endif } } } diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 5b4d462117c..c7b0aa52dd2 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -830,7 +830,7 @@ static int acpi_bus_get_flags(struct acpi_device *device) if (ACPI_SUCCESS(status)) device->flags.wake_capable = 1; - /* TBD: Peformance management */ + /* TBD: Performance management */ return 0; } @@ -941,6 +941,15 @@ static int acpi_bay_match(struct acpi_device *device){ return -ENODEV; } +/* + * acpi_dock_match - see if a device has a _DCK method + */ +static int acpi_dock_match(struct acpi_device *device) +{ + acpi_handle tmp; + return acpi_get_handle(device->handle, "_DCK", &tmp); +} + static void acpi_device_set_id(struct acpi_device *device, struct acpi_device *parent, acpi_handle handle, int type) @@ -950,6 +959,7 @@ static void acpi_device_set_id(struct acpi_device *device, char *hid = NULL; char *uid = NULL; struct acpi_compatible_id_list *cid_list = NULL; + const char *cid_add = NULL; acpi_status status; switch (type) { @@ -972,15 +982,18 @@ static void acpi_device_set_id(struct acpi_device *device, device->flags.bus_address = 1; } - if(!(info->valid & (ACPI_VALID_HID | ACPI_VALID_CID))){ - status = acpi_video_bus_match(device); - if(ACPI_SUCCESS(status)) - hid = ACPI_VIDEO_HID; + /* If we have a video/bay/dock device, add our selfdefined + HID to the CID list. Like that the video/bay/dock drivers + will get autoloaded and the device might still match + against another driver. + */ + if (ACPI_SUCCESS(acpi_video_bus_match(device))) + cid_add = ACPI_VIDEO_HID; + else if (ACPI_SUCCESS(acpi_bay_match(device))) + cid_add = ACPI_BAY_HID; + else if (ACPI_SUCCESS(acpi_dock_match(device))) + cid_add = ACPI_DOCK_HID; - status = acpi_bay_match(device); - if (ACPI_SUCCESS(status)) - hid = ACPI_BAY_HID; - } break; case ACPI_BUS_TYPE_POWER: hid = ACPI_POWER_HID; @@ -1021,11 +1034,44 @@ static void acpi_device_set_id(struct acpi_device *device, strcpy(device->pnp.unique_id, uid); device->flags.unique_id = 1; } - if (cid_list) { - device->pnp.cid_list = kmalloc(cid_list->size, GFP_KERNEL); - if (device->pnp.cid_list) - memcpy(device->pnp.cid_list, cid_list, cid_list->size); - else + if (cid_list || cid_add) { + struct acpi_compatible_id_list *list; + int size = 0; + int count = 0; + + if (cid_list) { + size = cid_list->size; + } else if (cid_add) { + size = sizeof(struct acpi_compatible_id_list); + cid_list = ACPI_ALLOCATE_ZEROED((acpi_size) size); + if (!cid_list) { + printk(KERN_ERR "Memory allocation error\n"); + kfree(buffer.pointer); + return; + } else { + cid_list->count = 0; + cid_list->size = size; + } + } + if (cid_add) + size += sizeof(struct acpi_compatible_id); + list = kmalloc(size, GFP_KERNEL); + + if (list) { + if (cid_list) { + memcpy(list, cid_list, cid_list->size); + count = cid_list->count; + } + if (cid_add) { + strncpy(list->id[count].value, cid_add, + ACPI_MAX_CID_LENGTH); + count++; + device->flags.compatible_ids = 1; + } + list->size = size; + list->count = count; + device->pnp.cid_list = list; + } else printk(KERN_ERR "Memory allocation error\n"); } @@ -1081,6 +1127,20 @@ static int acpi_bus_remove(struct acpi_device *dev, int rmdevice) } static int +acpi_is_child_device(struct acpi_device *device, + int (*matcher)(struct acpi_device *)) +{ + int result = -ENODEV; + + do { + if (ACPI_SUCCESS(matcher(device))) + return AE_OK; + } while ((device = device->parent)); + + return result; +} + +static int acpi_add_single_object(struct acpi_device **child, struct acpi_device *parent, acpi_handle handle, int type, struct acpi_bus_ops *ops) @@ -1131,10 +1191,20 @@ acpi_add_single_object(struct acpi_device **child, case ACPI_BUS_TYPE_PROCESSOR: case ACPI_BUS_TYPE_DEVICE: result = acpi_bus_get_status(device); - if (ACPI_FAILURE(result) || !device->status.present) { - result = -ENOENT; + if (ACPI_FAILURE(result)) { + result = -ENODEV; goto end; } + if (!device->status.present) { + /* Bay and dock should be handled even if absent */ + if (!ACPI_SUCCESS( + acpi_is_child_device(device, acpi_bay_match)) && + !ACPI_SUCCESS( + acpi_is_child_device(device, acpi_dock_match))) { + result = -ENODEV; + goto end; + } + } break; default: STRUCT_TO_INT(device->status) = @@ -1449,6 +1519,8 @@ static int acpi_bus_scan_fixed(struct acpi_device *root) return result; } +int __init acpi_boot_ec_enable(void); + static int __init acpi_scan_init(void) { int result; @@ -1480,6 +1552,10 @@ static int __init acpi_scan_init(void) * Enumerate devices in the ACPI namespace. */ result = acpi_bus_scan_fixed(acpi_root); + + /* EC region might be needed at bus_scan, so enable it now */ + acpi_boot_ec_enable(); + if (!result) result = acpi_bus_scan(acpi_root, &ops); diff --git a/drivers/acpi/sleep/main.c b/drivers/acpi/sleep/main.c index 2c0b6630f8b..7f97e32fc33 100644 --- a/drivers/acpi/sleep/main.c +++ b/drivers/acpi/sleep/main.c @@ -26,9 +26,24 @@ u8 sleep_states[ACPI_S_STATE_COUNT]; #ifdef CONFIG_PM_SLEEP static u32 acpi_target_sleep_state = ACPI_STATE_S0; +static bool acpi_sleep_finish_wake_up; + +/* + * ACPI 2.0 and later want us to execute _PTS after suspending devices, so we + * allow the user to request that behavior by using the 'acpi_new_pts_ordering' + * kernel command line option that causes the following variable to be set. + */ +static bool new_pts_ordering; + +static int __init acpi_new_pts_ordering(char *str) +{ + new_pts_ordering = true; + return 1; +} +__setup("acpi_new_pts_ordering", acpi_new_pts_ordering); #endif -int acpi_sleep_prepare(u32 acpi_state) +static int acpi_sleep_prepare(u32 acpi_state) { #ifdef CONFIG_ACPI_SLEEP /* do we have a wakeup address for S2 and S3? */ @@ -44,6 +59,8 @@ int acpi_sleep_prepare(u32 acpi_state) ACPI_FLUSH_CPU_CACHE(); acpi_enable_wakeup_device_prep(acpi_state); #endif + printk(KERN_INFO PREFIX "Preparing to enter system sleep state S%d\n", + acpi_state); acpi_enter_sleep_state_prep(acpi_state); return 0; } @@ -63,17 +80,25 @@ static u32 acpi_suspend_states[] = { static int init_8259A_after_S1; /** - * acpi_pm_set_target - Set the target system sleep state to the state + * acpi_pm_begin - Set the target system sleep state to the state * associated with given @pm_state, if supported. */ -static int acpi_pm_set_target(suspend_state_t pm_state) +static int acpi_pm_begin(suspend_state_t pm_state) { u32 acpi_state = acpi_suspend_states[pm_state]; int error = 0; if (sleep_states[acpi_state]) { acpi_target_sleep_state = acpi_state; + if (new_pts_ordering) + return 0; + + error = acpi_sleep_prepare(acpi_state); + if (error) + acpi_target_sleep_state = ACPI_STATE_S0; + else + acpi_sleep_finish_wake_up = true; } else { printk(KERN_ERR "ACPI does not support this state: %d\n", pm_state); @@ -91,12 +116,17 @@ static int acpi_pm_set_target(suspend_state_t pm_state) static int acpi_pm_prepare(void) { - int error = acpi_sleep_prepare(acpi_target_sleep_state); + if (new_pts_ordering) { + int error = acpi_sleep_prepare(acpi_target_sleep_state); - if (error) - acpi_target_sleep_state = ACPI_STATE_S0; + if (error) { + acpi_target_sleep_state = ACPI_STATE_S0; + return error; + } + acpi_sleep_finish_wake_up = true; + } - return error; + return ACPI_SUCCESS(acpi_hw_disable_all_gpes()) ? 0 : -EFAULT; } /** @@ -120,10 +150,8 @@ static int acpi_pm_enter(suspend_state_t pm_state) if (acpi_state == ACPI_STATE_S3) { int error = acpi_save_state_mem(); - if (error) { - acpi_target_sleep_state = ACPI_STATE_S0; + if (error) return error; - } } local_irq_save(flags); @@ -139,6 +167,9 @@ static int acpi_pm_enter(suspend_state_t pm_state) break; } + /* Reprogram control registers and execute _BFS */ + acpi_leave_sleep_state_prep(acpi_state); + /* ACPI 3.0 specs (P62) says that it's the responsabilty * of the OSPM to clear the status bit [ implying that the * POWER_BUTTON event should not reach userspace ] @@ -146,6 +177,13 @@ static int acpi_pm_enter(suspend_state_t pm_state) if (ACPI_SUCCESS(status) && (acpi_state == ACPI_STATE_S3)) acpi_clear_event(ACPI_EVENT_POWER_BUTTON); + /* + * Disable and clear GPE status before interrupt is enabled. Some GPEs + * (like wakeup GPE) haven't handler, this can avoid such GPE misfire. + * acpi_leave_sleep_state will reenable specific GPEs later + */ + acpi_hw_disable_all_gpes(); + local_irq_restore(flags); printk(KERN_DEBUG "Back to C!\n"); @@ -157,7 +195,7 @@ static int acpi_pm_enter(suspend_state_t pm_state) } /** - * acpi_pm_finish - Finish up suspend sequence. + * acpi_pm_finish - Instruct the platform to leave a sleep state. * * This is called after we wake back up (or if entering the sleep state * failed). @@ -174,6 +212,7 @@ static void acpi_pm_finish(void) acpi_set_firmware_waking_vector((acpi_physical_address) 0); acpi_target_sleep_state = ACPI_STATE_S0; + acpi_sleep_finish_wake_up = false; #ifdef CONFIG_X86 if (init_8259A_after_S1) { @@ -183,6 +222,20 @@ static void acpi_pm_finish(void) #endif } +/** + * acpi_pm_end - Finish up suspend sequence. + */ + +static void acpi_pm_end(void) +{ + /* + * This is necessary in case acpi_pm_finish() is not called directly + * during a failing transition to a sleep state. + */ + if (acpi_sleep_finish_wake_up) + acpi_pm_finish(); +} + static int acpi_pm_state_valid(suspend_state_t pm_state) { u32 acpi_state; @@ -201,10 +254,11 @@ static int acpi_pm_state_valid(suspend_state_t pm_state) static struct platform_suspend_ops acpi_pm_ops = { .valid = acpi_pm_state_valid, - .set_target = acpi_pm_set_target, + .begin = acpi_pm_begin, .prepare = acpi_pm_prepare, .enter = acpi_pm_enter, .finish = acpi_pm_finish, + .end = acpi_pm_end, }; /* @@ -229,15 +283,36 @@ static struct dmi_system_id __initdata acpisleep_dmi_table[] = { #endif /* CONFIG_SUSPEND */ #ifdef CONFIG_HIBERNATION -static int acpi_hibernation_start(void) +static int acpi_hibernation_begin(void) { + int error; + acpi_target_sleep_state = ACPI_STATE_S4; - return 0; + if (new_pts_ordering) + return 0; + + error = acpi_sleep_prepare(ACPI_STATE_S4); + if (error) + acpi_target_sleep_state = ACPI_STATE_S0; + else + acpi_sleep_finish_wake_up = true; + + return error; } static int acpi_hibernation_prepare(void) { - return acpi_sleep_prepare(ACPI_STATE_S4); + if (new_pts_ordering) { + int error = acpi_sleep_prepare(ACPI_STATE_S4); + + if (error) { + acpi_target_sleep_state = ACPI_STATE_S0; + return error; + } + acpi_sleep_finish_wake_up = true; + } + + return ACPI_SUCCESS(acpi_hw_disable_all_gpes()) ? 0 : -EFAULT; } static int acpi_hibernation_enter(void) @@ -251,6 +326,8 @@ static int acpi_hibernation_enter(void) acpi_enable_wakeup_device(ACPI_STATE_S4); /* This shouldn't return. If it returns, we have a problem */ status = acpi_enter_sleep_state(ACPI_STATE_S4); + /* Reprogram control registers and execute _BFS */ + acpi_leave_sleep_state_prep(ACPI_STATE_S4); local_irq_restore(flags); return ACPI_SUCCESS(status) ? 0 : -EFAULT; @@ -263,15 +340,12 @@ static void acpi_hibernation_leave(void) * enable it here. */ acpi_enable(); + /* Reprogram control registers and execute _BFS */ + acpi_leave_sleep_state_prep(ACPI_STATE_S4); } static void acpi_hibernation_finish(void) { - /* - * If ACPI is not enabled by the BIOS and the boot kernel, we need to - * enable it here. - */ - acpi_enable(); acpi_disable_wakeup_device(ACPI_STATE_S4); acpi_leave_sleep_state(ACPI_STATE_S4); @@ -279,6 +353,17 @@ static void acpi_hibernation_finish(void) acpi_set_firmware_waking_vector((acpi_physical_address) 0); acpi_target_sleep_state = ACPI_STATE_S0; + acpi_sleep_finish_wake_up = false; +} + +static void acpi_hibernation_end(void) +{ + /* + * This is necessary in case acpi_hibernation_finish() is not called + * directly during a failing transition to the sleep state. + */ + if (acpi_sleep_finish_wake_up) + acpi_hibernation_finish(); } static int acpi_hibernation_pre_restore(void) @@ -296,7 +381,8 @@ static void acpi_hibernation_restore_cleanup(void) } static struct platform_hibernation_ops acpi_hibernation_ops = { - .start = acpi_hibernation_start, + .begin = acpi_hibernation_begin, + .end = acpi_hibernation_end, .pre_snapshot = acpi_hibernation_prepare, .finish = acpi_hibernation_finish, .prepare = acpi_hibernation_prepare, @@ -386,11 +472,20 @@ int acpi_pm_device_sleep_state(struct device *dev, int wake, int *d_min_p) if (acpi_target_sleep_state == ACPI_STATE_S0 || (wake && adev->wakeup.state.enabled && adev->wakeup.sleep_state <= acpi_target_sleep_state)) { + acpi_status status; + acpi_method[3] = 'W'; - acpi_evaluate_integer(handle, acpi_method, NULL, &d_max); - /* Sanity check */ - if (d_max < d_min) + status = acpi_evaluate_integer(handle, acpi_method, NULL, + &d_max); + if (ACPI_FAILURE(status)) { + d_max = d_min; + } else if (d_max < d_min) { + /* Warn the user of the broken DSDT */ + printk(KERN_WARNING "ACPI: Wrong value from %s\n", + acpi_method); + /* Sanitize it */ d_min = d_max; + } } if (d_min_p) @@ -403,6 +498,7 @@ static void acpi_power_off_prepare(void) { /* Prepare to power off the system */ acpi_sleep_prepare(ACPI_STATE_S5); + acpi_hw_disable_all_gpes(); } static void acpi_power_off(void) diff --git a/drivers/acpi/sleep/proc.c b/drivers/acpi/sleep/proc.c index e19eb0c25e6..f8df5217d47 100644 --- a/drivers/acpi/sleep/proc.c +++ b/drivers/acpi/sleep/proc.c @@ -178,6 +178,9 @@ static int get_date_field(char **p, u32 * value) * Try to find delimeter, only to insert null. The end of the * string won't have one, but is still valid. */ + if (*p == NULL) + return result; + next = strpbrk(*p, "- :"); if (next) *next++ = '\0'; @@ -190,6 +193,8 @@ static int get_date_field(char **p, u32 * value) if (next) *p = next; + else + *p = NULL; return result; } diff --git a/drivers/acpi/sleep/sleep.h b/drivers/acpi/sleep/sleep.h index a2ea125ae2d..cfaf8f5b0a1 100644 --- a/drivers/acpi/sleep/sleep.h +++ b/drivers/acpi/sleep/sleep.h @@ -5,5 +5,3 @@ extern int acpi_suspend (u32 state); extern void acpi_enable_wakeup_device_prep(u8 sleep_state); extern void acpi_enable_wakeup_device(u8 sleep_state); extern void acpi_disable_wakeup_device(u8 sleep_state); - -extern int acpi_sleep_prepare(u32 acpi_state); diff --git a/drivers/acpi/system.c b/drivers/acpi/system.c index edee2806e37..5ffe0ea1896 100644 --- a/drivers/acpi/system.c +++ b/drivers/acpi/system.c @@ -58,7 +58,7 @@ module_param_call(acpica_version, NULL, param_get_acpica_version, NULL, 0444); FS Interface (/sys) -------------------------------------------------------------------------- */ static LIST_HEAD(acpi_table_attr_list); -static struct kobject tables_kobj; +static struct kobject *tables_kobj; struct acpi_table_attr { struct bin_attribute attr; @@ -135,11 +135,9 @@ static int acpi_system_sysfs_init(void) int table_index = 0; int result; - tables_kobj.parent = &acpi_subsys.kobj; - kobject_set_name(&tables_kobj, "tables"); - result = kobject_register(&tables_kobj); - if (result) - return result; + tables_kobj = kobject_create_and_add("tables", acpi_kobj); + if (!tables_kobj) + return -ENOMEM; do { result = acpi_get_table_by_index(table_index, &table_header); @@ -153,7 +151,7 @@ static int acpi_system_sysfs_init(void) acpi_table_attr_init(table_attr, table_header); result = - sysfs_create_bin_file(&tables_kobj, + sysfs_create_bin_file(tables_kobj, &table_attr->attr); if (result) { kfree(table_attr); @@ -163,6 +161,7 @@ static int acpi_system_sysfs_init(void) &acpi_table_attr_list); } } while (!result); + kobject_uevent(tables_kobj, KOBJ_ADD); return 0; } diff --git a/drivers/acpi/tables/Makefile b/drivers/acpi/tables/Makefile index 0a7d7afac25..7385efa6162 100644 --- a/drivers/acpi/tables/Makefile +++ b/drivers/acpi/tables/Makefile @@ -2,6 +2,6 @@ # Makefile for all Linux ACPI interpreter subdirectories # -obj-y := tbxface.o tbinstal.o tbutils.o tbfind.o tbfadt.o +obj-y := tbxface.o tbinstal.o tbutils.o tbfind.o tbfadt.o tbxfroot.o EXTRA_CFLAGS += $(ACPI_CFLAGS) diff --git a/drivers/acpi/tables/tbxfroot.c b/drivers/acpi/tables/tbxfroot.c index cf8fa514189..9ecb4b6c1e7 100644 --- a/drivers/acpi/tables/tbxfroot.c +++ b/drivers/acpi/tables/tbxfroot.c @@ -100,7 +100,7 @@ static acpi_status acpi_tb_validate_rsdp(struct acpi_table_rsdp *rsdp) /******************************************************************************* * - * FUNCTION: acpi_tb_find_rsdp + * FUNCTION: acpi_find_root_pointer * * PARAMETERS: table_address - Where the table pointer is returned * @@ -219,8 +219,6 @@ acpi_status acpi_find_root_pointer(acpi_native_uint * table_address) return_ACPI_STATUS(AE_NOT_FOUND); } -ACPI_EXPORT_SYMBOL(acpi_find_root_pointer) - /******************************************************************************* * * FUNCTION: acpi_tb_scan_memory_for_rsdp diff --git a/drivers/acpi/thermal.c b/drivers/acpi/thermal.c index 5f79b445121..3a0af9a8cd2 100644 --- a/drivers/acpi/thermal.c +++ b/drivers/acpi/thermal.c @@ -492,7 +492,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 || nocrt) + if (!tz || !tz->trips.critical.flags.valid) return -EINVAL; if (tz->temperature >= tz->trips.critical.temperature) { @@ -501,9 +501,6 @@ static int acpi_thermal_critical(struct acpi_thermal *tz) } else if (tz->trips.critical.flags.enabled) tz->trips.critical.flags.enabled = 0; - printk(KERN_EMERG - "Critical temperature reached (%ld C), shutting down.\n", - KELVIN_TO_CELSIUS(tz->temperature)); acpi_bus_generate_proc_event(tz->device, ACPI_THERMAL_NOTIFY_CRITICAL, tz->trips.critical.flags.enabled); acpi_bus_generate_netlink_event(tz->device->pnp.device_class, @@ -511,14 +508,20 @@ static int acpi_thermal_critical(struct acpi_thermal *tz) ACPI_THERMAL_NOTIFY_CRITICAL, tz->trips.critical.flags.enabled); - orderly_poweroff(true); + /* take no action if nocrt is set */ + if(!nocrt) { + printk(KERN_EMERG + "Critical temperature reached (%ld C), shutting down.\n", + KELVIN_TO_CELSIUS(tz->temperature)); + orderly_poweroff(true); + } return 0; } static int acpi_thermal_hot(struct acpi_thermal *tz) { - if (!tz || !tz->trips.hot.flags.valid || nocrt) + if (!tz || !tz->trips.hot.flags.valid) return -EINVAL; if (tz->temperature >= tz->trips.hot.temperature) { @@ -534,7 +537,7 @@ static int acpi_thermal_hot(struct acpi_thermal *tz) ACPI_THERMAL_NOTIFY_HOT, tz->trips.hot.flags.enabled); - /* TBD: Call user-mode "sleep(S4)" function */ + /* TBD: Call user-mode "sleep(S4)" function if nocrt is cleared */ return 0; } diff --git a/drivers/acpi/utilities/utresrc.c b/drivers/acpi/utilities/utresrc.c index cbbd3315a1e..b630ee137ee 100644 --- a/drivers/acpi/utilities/utresrc.c +++ b/drivers/acpi/utilities/utresrc.c @@ -1,6 +1,6 @@ /******************************************************************************* * - * Module Name: utresrc - Resource managment utilities + * Module Name: utresrc - Resource management utilities * ******************************************************************************/ diff --git a/drivers/acpi/video.c b/drivers/acpi/video.c index bd77e81e81c..a54ff6bce8f 100644 --- a/drivers/acpi/video.c +++ b/drivers/acpi/video.c @@ -292,18 +292,26 @@ static int acpi_video_device_set_state(struct acpi_video_device *device, int sta static int acpi_video_get_brightness(struct backlight_device *bd) { unsigned long cur_level; + int i; struct acpi_video_device *vd = (struct acpi_video_device *)bl_get_data(bd); acpi_video_device_lcd_get_level_current(vd, &cur_level); - return (int) cur_level; + for (i = 2; i < vd->brightness->count; i++) { + if (vd->brightness->levels[i] == cur_level) + /* The first two entries are special - see page 575 + of the ACPI spec 3.0 */ + return i-2; + } + return 0; } static int acpi_video_set_brightness(struct backlight_device *bd) { - int request_level = bd->props.brightness; + int request_level = bd->props.brightness+2; struct acpi_video_device *vd = (struct acpi_video_device *)bl_get_data(bd); - acpi_video_device_lcd_set_level(vd, request_level); + acpi_video_device_lcd_set_level(vd, + vd->brightness->levels[request_level]); return 0; } @@ -652,7 +660,6 @@ static void acpi_video_device_find_cap(struct acpi_video_device *device) kfree(obj); if (device->cap._BCL && device->cap._BCM && device->cap._BQC && max_level > 0){ - unsigned long tmp; static int count = 0; char *name; name = kzalloc(MAX_NAME_LEN, GFP_KERNEL); @@ -660,11 +667,10 @@ static void acpi_video_device_find_cap(struct acpi_video_device *device) return; sprintf(name, "acpi_video%d", count++); - acpi_video_device_lcd_get_level_current(device, &tmp); device->backlight = backlight_device_register(name, NULL, device, &acpi_backlight_ops); - device->backlight->props.max_brightness = max_level; - device->backlight->props.brightness = (int)tmp; + device->backlight->props.max_brightness = device->brightness->count-3; + device->backlight->props.brightness = acpi_video_get_brightness(device->backlight); backlight_update_status(device->backlight); kfree(name); @@ -1256,8 +1262,37 @@ acpi_video_bus_write_DOS(struct file *file, static int acpi_video_bus_add_fs(struct acpi_device *device) { + long device_id; + int status; struct proc_dir_entry *entry = NULL; struct acpi_video_bus *video; + struct device *dev; + + status = + acpi_evaluate_integer(device->handle, "_ADR", NULL, &device_id); + + if (!ACPI_SUCCESS(status)) + return -ENODEV; + + /* We need to attempt to determine whether the _ADR refers to a + PCI device or not. There's no terribly good way to do this, + so the best we can hope for is to assume that there'll never + be a video device in the host bridge */ + if (device_id >= 0x10000) { + /* It looks like a PCI device. Does it exist? */ + dev = acpi_get_physical_device(device->handle); + } else { + /* It doesn't look like a PCI device. Does its parent + exist? */ + acpi_handle phandle; + if (acpi_get_parent(device->handle, &phandle)) + return -ENODEV; + dev = acpi_get_physical_device(phandle); + } + if (!dev) + return -ENODEV; + put_device(dev); + video = acpi_driver_data(device); |