diff options
Diffstat (limited to 'drivers/acpi')
35 files changed, 2839 insertions, 436 deletions
diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index 95588c2a77d..d141bdb3a7d 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig @@ -188,6 +188,7 @@ config ACPI_HOTPLUG_CPU config ACPI_THERMAL tristate "Thermal Zone" depends on ACPI_PROCESSOR + select THERMAL default y help This driver adds support for ACPI thermal zones. Most mobile and @@ -201,6 +202,16 @@ config ACPI_NUMA depends on (X86 || IA64) default y if IA64_GENERIC || IA64_SGI_SN2 +config ACPI_WMI + tristate "WMI (EXPERIMENTAL)" + depends on EXPERIMENTAL + help + This driver adds support for the ACPI-WMI mapper device (PNP0C14) + found on some systems. + + NOTE: You will need another driver or userspace application on top of + this to actually use anything defined in the ACPI-WMI mapper. + config ACPI_ASUS tristate "ASUS/Medion Laptop Extras" depends on X86 diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile index 456446f9007..f29812a8653 100644 --- a/drivers/acpi/Makefile +++ b/drivers/acpi/Makefile @@ -55,6 +55,7 @@ obj-$(CONFIG_ACPI_THERMAL) += thermal.o obj-$(CONFIG_ACPI_SYSTEM) += system.o event.o obj-$(CONFIG_ACPI_DEBUG) += debug.o obj-$(CONFIG_ACPI_NUMA) += numa.o +obj-$(CONFIG_ACPI_WMI) += wmi.o obj-$(CONFIG_ACPI_ASUS) += asus_acpi.o obj-$(CONFIG_ACPI_TOSHIBA) += toshiba_acpi.o obj-$(CONFIG_ACPI_HOTPLUG_MEMORY) += acpi_memhotplug.o 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 c4a769d1ba8..f6215e80980 100644 --- a/drivers/acpi/battery.c +++ b/drivers/acpi/battery.c @@ -194,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; } @@ -212,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[] = { @@ -226,6 +230,7 @@ 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 diff --git a/drivers/acpi/bay.c b/drivers/acpi/bay.c index 0c81294ac48..1fa86811b8e 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 8809654d6cc..6dbaa2d15fe 100644 --- a/drivers/acpi/blacklist.c +++ b/drivers/acpi/blacklist.c @@ -70,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}, {""} }; @@ -208,33 +206,35 @@ 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 3100"), * DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5050"), + * DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5100"), * 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."), - }, - }, + /* + * note that dmi_check_system() uses strstr() + * to match sub-strings rather than !strcmp(), + * so "Acer" below matches "Acer, inc." above. + */ /* * 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"), + * + * _OSI(Linux) is a NOP: + * DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5315"), */ { - .callback = dmi_unknown_osi_linux, + .callback = dmi_disable_osi_linux, .ident = "Acer", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Acer"), @@ -242,21 +242,22 @@ static struct dmi_system_id acpi_osi_dmi_table[] __initdata = { }, /* * Disable OSI(Linux) warnings on all "Apple Computer, Inc." + * Disable OSI(Linux) warnings on all "Apple 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"), + * DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro3,1"), * _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."), + DMI_MATCH(DMI_SYS_VENDOR, "Apple"), }, }, /* @@ -294,13 +295,13 @@ static struct dmi_system_id acpi_osi_dmi_table[] __initdata = { * DMI_MATCH(DMI_BOARD_NAME, "IFL91"), */ { - .callback = dmi_unknown_osi_linux, + .callback = dmi_disable_osi_linux, .ident = "Compal", .matches = { DMI_MATCH(DMI_BIOS_VENDOR, "COMPAL"), }, }, - { /* OSI(Linux) touches USB, breaks suspend to disk */ + { /* OSI(Linux) touches USB, unknown side-effect */ .callback = dmi_disable_osi_linux, .ident = "Dell Dimension 5150", .matches = { @@ -310,7 +311,7 @@ static struct dmi_system_id acpi_osi_dmi_table[] __initdata = { }, { /* OSI(Linux) is a NOP */ .callback = dmi_disable_osi_linux, - .ident = "Dell", + .ident = "Dell i1501", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1501"), @@ -318,7 +319,7 @@ static struct dmi_system_id acpi_osi_dmi_table[] __initdata = { }, { /* OSI(Linux) effect unknown */ .callback = dmi_unknown_osi_linux, - .ident = "Dell", + .ident = "Dell Latitude D830", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), DMI_MATCH(DMI_PRODUCT_NAME, "Latitude D830"), @@ -326,7 +327,7 @@ static struct dmi_system_id acpi_osi_dmi_table[] __initdata = { }, { /* OSI(Linux) effect unknown */ .callback = dmi_unknown_osi_linux, - .ident = "Dell", + .ident = "Dell OP GX620", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), DMI_MATCH(DMI_PRODUCT_NAME, "OptiPlex GX620"), @@ -334,15 +335,23 @@ static struct dmi_system_id acpi_osi_dmi_table[] __initdata = { }, { /* OSI(Linux) effect unknown */ .callback = dmi_unknown_osi_linux, - .ident = "Dell", + .ident = "Dell PE 1900", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), DMI_MATCH(DMI_PRODUCT_NAME, "PowerEdge 1900"), }, }, + { /* OSI(Linux) is a NOP */ + .callback = dmi_disable_osi_linux, + .ident = "Dell PE R200", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "PowerEdge R200"), + }, + }, { /* OSI(Linux) touches USB */ .callback = dmi_disable_osi_linux, - .ident = "Dell", + .ident = "Dell PR 390", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), DMI_MATCH(DMI_PRODUCT_NAME, "Precision WorkStation 390"), @@ -358,7 +367,7 @@ static struct dmi_system_id acpi_osi_dmi_table[] __initdata = { }, { /* OSI(Linux) effect unknown */ .callback = dmi_unknown_osi_linux, - .ident = "Dell", + .ident = "Dell PE SC440", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), DMI_MATCH(DMI_PRODUCT_NAME, "PowerEdge SC440"), @@ -474,6 +483,11 @@ static struct dmi_system_id acpi_osi_dmi_table[] __initdata = { * * _OSI(Linux) confirmed to be a NOP: * DMI_MATCH(DMI_PRODUCT_NAME, "P1-J150B"), + * with DMI_MATCH(DMI_BOARD_NAME, "ROCKY"), + * + * unknown: + * DMI_MATCH(DMI_PRODUCT_NAME, "S1-MDGDG"), + * with DMI_MATCH(DMI_BOARD_NAME, "ROCKY"), */ { .callback = dmi_disable_osi_linux, @@ -516,7 +530,7 @@ static struct dmi_system_id acpi_osi_dmi_table[] __initdata = { * DMI_MATCH(DMI_PRODUCT_NAME, "VGN-FZ11M"), */ { - .callback = dmi_unknown_osi_linux, + .callback = dmi_disable_osi_linux, .ident = "Sony", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"), diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c index 2994bb2b22e..8b0d4b7d188 100644 --- a/drivers/acpi/bus.c +++ b/drivers/acpi/bus.c @@ -122,6 +122,31 @@ int acpi_bus_get_status(struct acpi_device *device) EXPORT_SYMBOL(acpi_bus_get_status); +void acpi_bus_private_data_handler(acpi_handle handle, + u32 function, void *context) +{ + return; +} +EXPORT_SYMBOL(acpi_bus_private_data_handler); + +int acpi_bus_get_private_data(acpi_handle handle, void **data) +{ + acpi_status status = AE_OK; + + if (!*data) + return -EINVAL; + + status = acpi_get_data(handle, acpi_bus_private_data_handler, data); + if (ACPI_FAILURE(status) || !*data) { + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "No context for object [%p]\n", + handle)); + return -ENODEV; + } + + return 0; +} +EXPORT_SYMBOL(acpi_bus_get_private_data); + /* -------------------------------------------------------------------------- Power Management -------------------------------------------------------------------------- */ 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/dispatcher/dsopcode.c b/drivers/acpi/dispatcher/dsopcode.c index fc9da4879cb..f501e083aac 100644 --- a/drivers/acpi/dispatcher/dsopcode.c +++ b/drivers/acpi/dispatcher/dsopcode.c @@ -359,7 +359,9 @@ acpi_status acpi_ds_get_region_arguments(union acpi_operand_object *obj_desc) status = acpi_os_validate_address(obj_desc->region.space_id, obj_desc->region.address, - (acpi_size) obj_desc->region.length); + (acpi_size) obj_desc->region.length, + acpi_ut_get_node_name(node)); + if (ACPI_FAILURE(status)) { /* * Invalid address/length. We will emit an error message and mark diff --git a/drivers/acpi/dock.c b/drivers/acpi/dock.c index 7e47a981cc2..307cef65c24 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 987b967c746..7222a18a031 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -573,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) @@ -585,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; @@ -593,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; diff --git a/drivers/acpi/event.c b/drivers/acpi/event.c index 5c95863f8fa..5479dc0eeee 100644 --- a/drivers/acpi/event.c +++ b/drivers/acpi/event.c @@ -109,6 +109,34 @@ static const struct file_operations acpi_system_event_ops = { }; #endif /* CONFIG_ACPI_PROC_EVENT */ +/* ACPI notifier chain */ +BLOCKING_NOTIFIER_HEAD(acpi_chain_head); + +int acpi_notifier_call_chain(struct acpi_device *dev, u32 type, u32 data) +{ + struct acpi_bus_event event; + + strcpy(event.device_class, dev->pnp.device_class); + strcpy(event.bus_id, dev->pnp.bus_id); + event.type = type; + event.data = data; + return (blocking_notifier_call_chain(&acpi_chain_head, 0, (void *)&event) + == NOTIFY_BAD) ? -EINVAL : 0; +} +EXPORT_SYMBOL(acpi_notifier_call_chain); + +int register_acpi_notifier(struct notifier_block *nb) +{ + return blocking_notifier_chain_register(&acpi_chain_head, nb); +} +EXPORT_SYMBOL(register_acpi_notifier); + +int unregister_acpi_notifier(struct notifier_block *nb) +{ + return blocking_notifier_chain_unregister(&acpi_chain_head, nb); +} +EXPORT_SYMBOL(unregister_acpi_notifier); + #ifdef CONFIG_NET static unsigned int acpi_event_seqnum; struct acpi_genl_event { diff --git a/drivers/acpi/events/evevent.c b/drivers/acpi/events/evevent.c index e41287815ea..3048801a37b 100644 --- a/drivers/acpi/events/evevent.c +++ b/drivers/acpi/events/evevent.c @@ -259,7 +259,7 @@ u32 acpi_ev_fixed_event_detect(void) enable_bit_mask)) { /* Found an active (signalled) event */ - + acpi_os_fixed_event_count(i); int_status |= acpi_ev_fixed_event_dispatch((u32) i); } } diff --git a/drivers/acpi/events/evgpe.c b/drivers/acpi/events/evgpe.c index e22f4a973c0..0dadd2adc80 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); @@ -501,6 +501,7 @@ u32 acpi_ev_gpe_detect(struct acpi_gpe_xrupt_info * gpe_xrupt_list) * an interrupt handler. * ******************************************************************************/ +static void acpi_ev_asynch_enable_gpe(void *context); static void ACPI_SYSTEM_XFACE acpi_ev_asynch_execute_gpe_method(void *context) { @@ -576,22 +577,30 @@ static void ACPI_SYSTEM_XFACE acpi_ev_asynch_execute_gpe_method(void *context) method_node))); } } + /* Defer enabling of GPE until all notify handlers are done */ + acpi_os_execute(OSL_NOTIFY_HANDLER, acpi_ev_asynch_enable_gpe, + gpe_event_info); + return_VOID; +} - if ((local_gpe_event_info.flags & ACPI_GPE_XRUPT_TYPE_MASK) == +static void acpi_ev_asynch_enable_gpe(void *context) +{ + struct acpi_gpe_event_info *gpe_event_info = context; + acpi_status status; + if ((gpe_event_info->flags & ACPI_GPE_XRUPT_TYPE_MASK) == ACPI_GPE_LEVEL_TRIGGERED) { /* * GPE is level-triggered, we clear the GPE status bit after * handling the event. */ - status = acpi_hw_clear_gpe(&local_gpe_event_info); + status = acpi_hw_clear_gpe(gpe_event_info); if (ACPI_FAILURE(status)) { return_VOID; } } /* Enable this GPE */ - - (void)acpi_hw_write_gpe_enable_reg(&local_gpe_event_info); + (void)acpi_hw_write_gpe_enable_reg(gpe_event_info); return_VOID; } @@ -618,7 +627,7 @@ acpi_ev_gpe_dispatch(struct acpi_gpe_event_info *gpe_event_info, u32 gpe_number) ACPI_FUNCTION_TRACE(ev_gpe_dispatch); - acpi_gpe_count++; + acpi_os_gpe_count(gpe_number); /* * If edge-triggered, clear the GPE status bit now. Note that diff --git a/drivers/acpi/fan.c b/drivers/acpi/fan.c index a6e149d692c..48cb705b274 100644 --- a/drivers/acpi/fan.c +++ b/drivers/acpi/fan.c @@ -30,7 +30,7 @@ #include <linux/proc_fs.h> #include <linux/seq_file.h> #include <asm/uaccess.h> - +#include <linux/thermal.h> #include <acpi/acpi_bus.h> #include <acpi/acpi_drivers.h> @@ -68,9 +68,55 @@ static struct acpi_driver acpi_fan_driver = { }, }; +/* thermal cooling device callbacks */ +static int fan_get_max_state(struct thermal_cooling_device *cdev, char *buf) +{ + /* ACPI fan device only support two states: ON/OFF */ + return sprintf(buf, "1\n"); +} + +static int fan_get_cur_state(struct thermal_cooling_device *cdev, char *buf) +{ + struct acpi_device *device = cdev->devdata; + int state; + int result; + + if (!device) + return -EINVAL; + + result = acpi_bus_get_power(device->handle, &state); + if (result) + return result; + + return sprintf(buf, "%s\n", state == ACPI_STATE_D3 ? "0" : + (state == ACPI_STATE_D0 ? "1" : "unknown")); +} + +static int +fan_set_cur_state(struct thermal_cooling_device *cdev, unsigned int state) +{ + struct acpi_device *device = cdev->devdata; + int result; + + if (!device || (state != 0 && state != 1)) + return -EINVAL; + + result = acpi_bus_set_power(device->handle, + state ? ACPI_STATE_D0 : ACPI_STATE_D3); + + return result; +} + +static struct thermal_cooling_device_ops fan_cooling_ops = { + .get_max_state = fan_get_max_state, + .get_cur_state = fan_get_cur_state, + .set_cur_state = fan_set_cur_state, +}; + /* -------------------------------------------------------------------------- FS Interface (/proc) -------------------------------------------------------------------------- */ +#ifdef CONFIG_ACPI_PROCFS static struct proc_dir_entry *acpi_fan_dir; @@ -171,7 +217,17 @@ static int acpi_fan_remove_fs(struct acpi_device *device) return 0; } +#else +static int acpi_fan_add_fs(struct acpi_device *device) +{ + return 0; +} +static int acpi_fan_remove_fs(struct acpi_device *device) +{ + return 0; +} +#endif /* -------------------------------------------------------------------------- Driver Interface -------------------------------------------------------------------------- */ @@ -179,9 +235,8 @@ static int acpi_fan_remove_fs(struct acpi_device *device) static int acpi_fan_add(struct acpi_device *device) { int result = 0; - struct acpi_fan *fan = NULL; int state = 0; - + struct thermal_cooling_device *cdev; if (!device) return -EINVAL; @@ -199,6 +254,25 @@ static int acpi_fan_add(struct acpi_device *device) acpi_bus_set_power(device->handle, state); device->flags.force_power_state = 0; + cdev = thermal_cooling_device_register("Fan", device, + &fan_cooling_ops); + if (cdev) + printk(KERN_INFO PREFIX + "%s is registered as cooling_device%d\n", + device->dev.bus_id, cdev->id); + else + goto end; + acpi_driver_data(device) = cdev; + result = sysfs_create_link(&device->dev.kobj, &cdev->device.kobj, + "thermal_cooling"); + if (result) + return result; + + result = sysfs_create_link(&cdev->device.kobj, &device->dev.kobj, + "device"); + if (result) + return result; + result = acpi_fan_add_fs(device); if (result) goto end; @@ -208,18 +282,20 @@ static int acpi_fan_add(struct acpi_device *device) !device->power.state ? "on" : "off"); end: - if (result) - kfree(fan); - return result; } static int acpi_fan_remove(struct acpi_device *device, int type) { - if (!device || !acpi_driver_data(device)) + struct thermal_cooling_device *cdev = acpi_driver_data(device); + + if (!device || !cdev) return -EINVAL; acpi_fan_remove_fs(device); + sysfs_remove_link(&device->dev.kobj, "thermal_cooling"); + sysfs_remove_link(&cdev->device.kobj, "device"); + thermal_cooling_device_unregister(cdev); return 0; } @@ -261,10 +337,12 @@ static int __init acpi_fan_init(void) int result = 0; +#ifdef CONFIG_ACPI_PROCFS acpi_fan_dir = proc_mkdir(ACPI_FAN_CLASS, acpi_root_dir); if (!acpi_fan_dir) return -ENODEV; acpi_fan_dir->owner = THIS_MODULE; +#endif result = acpi_bus_register_driver(&acpi_fan_driver); if (result < 0) { diff --git a/drivers/acpi/hardware/hwsleep.c b/drivers/acpi/hardware/hwsleep.c index fd1c4ba6336..058d0be5cbe 100644 --- a/drivers/acpi/hardware/hwsleep.c +++ b/drivers/acpi/hardware/hwsleep.c @@ -286,13 +286,13 @@ acpi_status asmlinkage acpi_enter_sleep_state(u8 sleep_state) } /* + * 1) Disable/Clear all GPEs * 2) Enable all wakeup GPEs */ status = acpi_hw_disable_all_gpes(); if (ACPI_FAILURE(status)) { return_ACPI_STATUS(status); } - acpi_gbl_system_awake_and_running = FALSE; status = acpi_hw_enable_all_wakeup_gpes(); diff --git a/drivers/acpi/namespace/nsxfeval.c b/drivers/acpi/namespace/nsxfeval.c index f39fbc6b923..b92133faf5b 100644 --- a/drivers/acpi/namespace/nsxfeval.c +++ b/drivers/acpi/namespace/nsxfeval.c @@ -443,6 +443,7 @@ acpi_ns_get_device_callback(acpi_handle obj_handle, struct acpica_device_id hid; struct acpi_compatible_id_list *cid; acpi_native_uint i; + int found; status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE); if (ACPI_FAILURE(status)) { @@ -496,16 +497,19 @@ acpi_ns_get_device_callback(acpi_handle obj_handle, /* Walk the CID list */ + found = 0; for (i = 0; i < cid->count; i++) { if (ACPI_STRNCMP(cid->id[i].value, info->hid, sizeof(struct - acpi_compatible_id)) != + acpi_compatible_id)) == 0) { - ACPI_FREE(cid); - return (AE_OK); + found = 1; + break; } } ACPI_FREE(cid); + if (!found) + return (AE_OK); } } diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c index 7fcacc5ed82..c2c585366fa 100644 --- a/drivers/acpi/osl.c +++ b/drivers/acpi/osl.c @@ -44,6 +44,8 @@ #include <asm/uaccess.h> #include <linux/efi.h> +#include <linux/ioport.h> +#include <linux/list.h> #define _COMPONENT ACPI_OS_SERVICES ACPI_MODULE_NAME("osl"); @@ -74,6 +76,18 @@ static void *acpi_irq_context; static struct workqueue_struct *kacpid_wq; static struct workqueue_struct *kacpi_notify_wq; +struct acpi_res_list { + resource_size_t start; + resource_size_t end; + acpi_adr_space_type resource_type; /* IO port, System memory, ...*/ + char name[5]; /* only can have a length of 4 chars, make use of this + one instead of res->name, no need to kalloc then */ + struct list_head resource_list; +}; + +static LIST_HEAD(resource_list_head); +static DEFINE_SPINLOCK(acpi_res_lock); + #define OSI_STRING_LENGTH_MAX 64 /* arbitrary */ static char osi_additional_string[OSI_STRING_LENGTH_MAX]; @@ -120,7 +134,7 @@ static char osi_additional_string[OSI_STRING_LENGTH_MAX]; */ #define OSI_LINUX_ENABLE 0 -struct osi_linux { +static struct osi_linux { unsigned int enable:1; unsigned int dmi:1; unsigned int cmdline:1; @@ -248,11 +262,16 @@ 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) +void __iomem *__init_refok +acpi_os_map_memory(acpi_physical_address phys, acpi_size size) { if (phys > ULONG_MAX) { printk(KERN_ERR PREFIX "Cannot map memory that high\n"); @@ -330,7 +349,15 @@ acpi_os_table_override(struct acpi_table_header * existing_table, static irqreturn_t acpi_irq(int irq, void *dev_id) { - return (*acpi_irq_handler) (acpi_irq_context) ? IRQ_HANDLED : IRQ_NONE; + u32 handled; + + handled = (*acpi_irq_handler) (acpi_irq_context); + + if (handled) { + acpi_irq_handled++; + return IRQ_HANDLED; + } else + return IRQ_NONE; } acpi_status @@ -339,6 +366,8 @@ acpi_os_install_interrupt_handler(u32 gsi, acpi_osd_handler handler, { unsigned int irq; + acpi_irq_stats_init(); + /* * Ignore the GSI from the core, and use the value in our copy of the * FADT. It may not be the same if an interrupt source override exists @@ -653,25 +682,6 @@ static void acpi_os_execute_deferred(struct work_struct *work) dpc->function(dpc->context); kfree(dpc); - /* Yield cpu to notify thread */ - cond_resched(); - - return; -} - -static void acpi_os_execute_notify(struct work_struct *work) -{ - struct acpi_os_dpc *dpc = container_of(work, struct acpi_os_dpc, work); - - if (!dpc) { - printk(KERN_ERR PREFIX "Invalid (NULL) context\n"); - return; - } - - dpc->function(dpc->context); - - kfree(dpc); - return; } @@ -695,7 +705,7 @@ acpi_status acpi_os_execute(acpi_execute_type type, { acpi_status status = AE_OK; struct acpi_os_dpc *dpc; - + struct workqueue_struct *queue; ACPI_DEBUG_PRINT((ACPI_DB_EXEC, "Scheduling function [%p(%p)] for deferred execution.\n", function, context)); @@ -719,20 +729,13 @@ acpi_status acpi_os_execute(acpi_execute_type type, dpc->function = function; dpc->context = context; - if (type == OSL_NOTIFY_HANDLER) { - INIT_WORK(&dpc->work, acpi_os_execute_notify); - if (!queue_work(kacpi_notify_wq, &dpc->work)) { - status = AE_ERROR; - kfree(dpc); - } - } else { - INIT_WORK(&dpc->work, acpi_os_execute_deferred); - if (!queue_work(kacpid_wq, &dpc->work)) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "Call to queue_work() failed.\n")); - status = AE_ERROR; - kfree(dpc); - } + INIT_WORK(&dpc->work, acpi_os_execute_deferred); + queue = (type == OSL_NOTIFY_HANDLER) ? kacpi_notify_wq : kacpid_wq; + if (!queue_work(queue, &dpc->work)) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, + "Call to queue_work() failed.\n")); + status = AE_ERROR; + kfree(dpc); } return_ACPI_STATUS(status); } @@ -1084,6 +1087,128 @@ static int __init acpi_wake_gpes_always_on_setup(char *str) __setup("acpi_wake_gpes_always_on", acpi_wake_gpes_always_on_setup); +/* Check of resource interference between native drivers and ACPI + * OperationRegions (SystemIO and System Memory only). + * IO ports and memory declared in ACPI might be used by the ACPI subsystem + * in arbitrary AML code and can interfere with legacy drivers. + * acpi_enforce_resources= can be set to: + * + * - strict (2) + * -> further driver trying to access the resources will not load + * - lax (default) (1) + * -> further driver trying to access the resources will load, but you + * get a system message that something might go wrong... + * + * - no (0) + * -> ACPI Operation Region resources will not be registered + * + */ +#define ENFORCE_RESOURCES_STRICT 2 +#define ENFORCE_RESOURCES_LAX 1 +#define ENFORCE_RESOURCES_NO 0 + +static unsigned int acpi_enforce_resources = ENFORCE_RESOURCES_LAX; + +static int __init acpi_enforce_resources_setup(char *str) +{ + if (str == NULL || *str == '\0') + return 0; + + if (!strcmp("strict", str)) + acpi_enforce_resources = ENFORCE_RESOURCES_STRICT; + else if (!strcmp("lax", str)) + acpi_enforce_resources = ENFORCE_RESOURCES_LAX; + else if (!strcmp("no", str)) + acpi_enforce_resources = ENFORCE_RESOURCES_NO; + + return 1; +} + +__setup("acpi_enforce_resources=", acpi_enforce_resources_setup); + +/* Check for resource conflicts between ACPI OperationRegions and native + * drivers */ +int acpi_check_resource_conflict(struct resource *res) +{ + struct acpi_res_list *res_list_elem; + int ioport; + int clash = 0; + + if (acpi_enforce_resources == ENFORCE_RESOURCES_NO) + return 0; + if (!(res->flags & IORESOURCE_IO) && !(res->flags & IORESOURCE_MEM)) + return 0; + + ioport = res->flags & IORESOURCE_IO; + + spin_lock(&acpi_res_lock); + list_for_each_entry(res_list_elem, &resource_list_head, + resource_list) { + if (ioport && (res_list_elem->resource_type + != ACPI_ADR_SPACE_SYSTEM_IO)) + continue; + if (!ioport && (res_list_elem->resource_type + != ACPI_ADR_SPACE_SYSTEM_MEMORY)) + continue; + + if (res->end < res_list_elem->start + || res_list_elem->end < res->start) + continue; + clash = 1; + break; + } + spin_unlock(&acpi_res_lock); + + if (clash) { + if (acpi_enforce_resources != ENFORCE_RESOURCES_NO) { + printk(KERN_INFO "%sACPI: %s resource %s [0x%llx-0x%llx]" + " conflicts with ACPI region %s" + " [0x%llx-0x%llx]\n", + acpi_enforce_resources == ENFORCE_RESOURCES_LAX + ? KERN_WARNING : KERN_ERR, + ioport ? "I/O" : "Memory", res->name, + (long long) res->start, (long long) res->end, + res_list_elem->name, + (long long) res_list_elem->start, + (long long) res_list_elem->end); + printk(KERN_INFO "ACPI: Device needs an ACPI driver\n"); + } + if (acpi_enforce_resources == ENFORCE_RESOURCES_STRICT) + return -EBUSY; + } + return 0; +} +EXPORT_SYMBOL(acpi_check_resource_conflict); + +int acpi_check_region(resource_size_t start, resource_size_t n, + const char *name) +{ + struct resource res = { + .start = start, + .end = start + n - 1, + .name = name, + .flags = IORESOURCE_IO, + }; + + return acpi_check_resource_conflict(&res); +} +EXPORT_SYMBOL(acpi_check_region); + +int acpi_check_mem_region(resource_size_t start, resource_size_t n, + const char *name) +{ + struct resource res = { + .start = start, + .end = start + n - 1, + .name = name, + .flags = IORESOURCE_MEM, + }; + + return acpi_check_resource_conflict(&res); + +} +EXPORT_SYMBOL(acpi_check_mem_region); + /* * Acquire a spinlock. * @@ -1195,24 +1320,24 @@ acpi_status acpi_os_release_object(acpi_cache_t * cache, void *object) * * Returns 0 on success */ -int acpi_dmi_dump(void) +static 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)); + dmi_get_system_info(DMI_SYS_VENDOR)); printk(KERN_NOTICE PREFIX "DMI Product Name: %s\n", - dmi_get_slot(DMI_PRODUCT_NAME)); + dmi_get_system_info(DMI_PRODUCT_NAME)); printk(KERN_NOTICE PREFIX "DMI Product Version: %s\n", - dmi_get_slot(DMI_PRODUCT_VERSION)); + dmi_get_system_info(DMI_PRODUCT_VERSION)); printk(KERN_NOTICE PREFIX "DMI Board Name: %s\n", - dmi_get_slot(DMI_BOARD_NAME)); + dmi_get_system_info(DMI_BOARD_NAME)); printk(KERN_NOTICE PREFIX "DMI BIOS Vendor: %s\n", - dmi_get_slot(DMI_BIOS_VENDOR)); + dmi_get_system_info(DMI_BIOS_VENDOR)); printk(KERN_NOTICE PREFIX "DMI BIOS Date: %s\n", - dmi_get_slot(DMI_BIOS_DATE)); + dmi_get_system_info(DMI_BIOS_DATE)); return 0; } @@ -1285,10 +1410,46 @@ acpi_status acpi_os_validate_address ( u8 space_id, acpi_physical_address address, - acpi_size length) + acpi_size length, + char *name) { + struct acpi_res_list *res; + if (acpi_enforce_resources == ENFORCE_RESOURCES_NO) + return AE_OK; - return AE_OK; + switch (space_id) { + case ACPI_ADR_SPACE_SYSTEM_IO: + case ACPI_ADR_SPACE_SYSTEM_MEMORY: + /* Only interference checks against SystemIO and SytemMemory + are needed */ + res = kzalloc(sizeof(struct acpi_res_list), GFP_KERNEL); + if (!res) + return AE_OK; + /* ACPI names are fixed to 4 bytes, still better use strlcpy */ + strlcpy(res->name, name, 5); + res->start = address; + res->end = address + length - 1; + res->resource_type = space_id; + spin_lock(&acpi_res_lock); + list_add(&res->resource_list, &resource_list_head); + spin_unlock(&acpi_res_lock); + pr_debug("Added %s resource: start: 0x%llx, end: 0x%llx, " + "name: %s\n", (space_id == ACPI_ADR_SPACE_SYSTEM_IO) + ? "SystemIO" : "System Memory", + (unsigned long long)res->start, + (unsigned long long)res->end, + res->name); + break; + case ACPI_ADR_SPACE_PCI_CONFIG: + case ACPI_ADR_SPACE_EC: + case ACPI_ADR_SPACE_SMBUS: + case ACPI_ADR_SPACE_CMOS: + case ACPI_ADR_SPACE_PCI_BAR_TARGET: + case ACPI_ADR_SPACE_DATA_TABLE: + case ACPI_ADR_SPACE_FIXED_HARDWARE: + break; + } + return AE_OK; } #endif diff --git a/drivers/acpi/pci_irq.c b/drivers/acpi/pci_irq.c index 822947339df..7f19859580c 100644 --- a/drivers/acpi/pci_irq.c +++ b/drivers/acpi/pci_irq.c @@ -51,10 +51,8 @@ static struct acpi_prt_entry *acpi_pci_irq_find_prt_entry(int segment, int bus, int device, int pin) { - struct list_head *node = NULL; struct acpi_prt_entry *entry = NULL; - if (!acpi_prt.count) return NULL; @@ -64,8 +62,7 @@ static struct acpi_prt_entry *acpi_pci_irq_find_prt_entry(int segment, * */ spin_lock(&acpi_prt_lock); - list_for_each(node, &acpi_prt.entries) { - entry = list_entry(node, struct acpi_prt_entry, node); + list_for_each_entry(entry, &acpi_prt.entries, node) { if ((segment == entry->id.segment) && (bus == entry->id.bus) && (device == entry->id.device) diff --git a/drivers/acpi/power.c b/drivers/acpi/power.c index af1769a20c7..76bf6d90c70 100644 --- a/drivers/acpi/power.c +++ b/drivers/acpi/power.c @@ -458,11 +458,9 @@ int acpi_power_transition(struct acpi_device *device, int state) } end: - if (result) { + if (result) device->power.state = ACPI_STATE_UNKNOWN; - printk(KERN_WARNING PREFIX "Transitioning device [%s] to D%d\n", - device->pnp.bus_id, state); - } else { + else { /* We shouldn't change the state till all above operations succeed */ device->power.state = state; } diff --git a/drivers/acpi/processor_core.c b/drivers/acpi/processor_core.c index e48ee4f8749..75ccf5d18bf 100644 --- a/drivers/acpi/processor_core.c +++ b/drivers/acpi/processor_core.c @@ -668,6 +668,24 @@ static int __cpuinit acpi_processor_start(struct acpi_device *device) acpi_processor_power_init(pr, device); + pr->cdev = thermal_cooling_device_register("Processor", device, + &processor_cooling_ops); + if (pr->cdev) + printk(KERN_INFO PREFIX + "%s is registered as cooling_device%d\n", + device->dev.bus_id, pr->cdev->id); + else + goto end; + + result = sysfs_create_link(&device->dev.kobj, &pr->cdev->device.kobj, + "thermal_cooling"); + if (result) + return result; + result = sysfs_create_link(&pr->cdev->device.kobj, &device->dev.kobj, + "device"); + if (result) + return result; + if (pr->flags.throttling) { printk(KERN_INFO PREFIX "%s [%s] (supports", acpi_device_name(device), acpi_device_bid(device)); @@ -791,6 +809,11 @@ static int acpi_processor_remove(struct acpi_device *device, int type) acpi_processor_remove_fs(device); + sysfs_remove_link(&device->dev.kobj, "thermal_cooling"); + sysfs_remove_link(&pr->cdev->device.kobj, "device"); + thermal_cooling_device_unregister(pr->cdev); + pr->cdev = NULL; + processors[pr->id] = NULL; kfree(pr); @@ -812,11 +835,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 @@ -1061,6 +1091,8 @@ static int __init acpi_processor_init(void) acpi_processor_ppc_init(); + acpi_processor_throttling_init(); + return 0; out_cpuidle: diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c index 199ea214615..32003fdc91e 100644 --- a/drivers/acpi/processor_idle.c +++ b/drivers/acpi/processor_idle.c @@ -98,6 +98,9 @@ module_param(bm_history, uint, 0644); static int acpi_processor_set_power_policy(struct acpi_processor *pr); +#else /* CONFIG_CPU_IDLE */ +static unsigned int latency_factor __read_mostly = 2; +module_param(latency_factor, uint, 0644); #endif /* @@ -201,6 +204,10 @@ static inline u32 ticks_elapsed_in_us(u32 t1, u32 t2) return PM_TIMER_TICKS_TO_US((0xFFFFFFFF - t1) + t2); } +/* + * Callers should disable interrupts before the call and enable + * interrupts after return. + */ static void acpi_safe_halt(void) { current_thread_info()->status &= ~TS_POLLING; @@ -261,7 +268,7 @@ static atomic_t c3_cpu_count; /* Common C-state entry for C2, C3, .. */ static void acpi_cstate_enter(struct acpi_processor_cx *cstate) { - if (cstate->space_id == ACPI_CSTATE_FFH) { + if (cstate->entry_method == ACPI_CSTATE_FFH) { /* Call into architectural FFH based C-state */ acpi_processor_ffh_cstate_enter(cstate); } else { @@ -413,6 +420,8 @@ static void acpi_processor_idle(void) pm_idle_save(); else acpi_safe_halt(); + + local_irq_enable(); return; } @@ -521,6 +530,7 @@ static void acpi_processor_idle(void) * skew otherwise. */ sleep_ticks = 0xFFFFFFFF; + local_irq_enable(); break; case ACPI_STATE_C2: @@ -922,20 +932,20 @@ static int acpi_processor_get_power_info_cst(struct acpi_processor *pr) cx.address = reg->address; cx.index = current_count + 1; - cx.space_id = ACPI_CSTATE_SYSTEMIO; + cx.entry_method = ACPI_CSTATE_SYSTEMIO; if (reg->space_id == ACPI_ADR_SPACE_FIXED_HARDWARE) { if (acpi_processor_ffh_cstate_probe (pr->id, &cx, reg) == 0) { - cx.space_id = ACPI_CSTATE_FFH; - } else if (cx.type != ACPI_STATE_C1) { + cx.entry_method = ACPI_CSTATE_FFH; + } else if (cx.type == ACPI_STATE_C1) { /* * C1 is a special case where FIXED_HARDWARE * can be handled in non-MWAIT way as well. * In that case, save this _CST entry info. - * That is, we retain space_id of SYSTEM_IO for - * halt based C1. * Otherwise, ignore this info and continue. */ + cx.entry_method = ACPI_CSTATE_HALT; + } else { continue; } } @@ -1369,12 +1379,16 @@ static inline void acpi_idle_update_bm_rld(struct acpi_processor *pr, /** * acpi_idle_do_entry - a helper function that does C2 and C3 type entry * @cx: cstate data + * + * Caller disables interrupt before call and enables interrupt after return. */ static inline void acpi_idle_do_entry(struct acpi_processor_cx *cx) { - if (cx->space_id == ACPI_CSTATE_FFH) { + if (cx->entry_method == ACPI_CSTATE_FFH) { /* Call into architectural FFH based C-state */ acpi_processor_ffh_cstate_enter(cx); + } else if (cx->entry_method == ACPI_CSTATE_HALT) { + acpi_safe_halt(); } else { int unused; /* IO port based C-state */ @@ -1396,21 +1410,27 @@ static inline void acpi_idle_do_entry(struct acpi_processor_cx *cx) static int acpi_idle_enter_c1(struct cpuidle_device *dev, struct cpuidle_state *state) { + u32 t1, t2; struct acpi_processor *pr; struct acpi_processor_cx *cx = cpuidle_get_statedata(state); + pr = processors[smp_processor_id()]; if (unlikely(!pr)) return 0; + local_irq_disable(); if (pr->flags.bm_check) acpi_idle_update_bm_rld(pr, cx); - acpi_safe_halt(); + t1 = inl(acpi_gbl_FADT.xpm_timer_block.address); + acpi_idle_do_entry(cx); + t2 = inl(acpi_gbl_FADT.xpm_timer_block.address); + local_irq_enable(); cx->usage++; - return 0; + return ticks_elapsed_in_us(t1, t2); } /** @@ -1517,7 +1537,9 @@ static int acpi_idle_enter_bm(struct cpuidle_device *dev, if (dev->safe_state) { return dev->safe_state->enter(dev, dev->safe_state); } else { + local_irq_disable(); acpi_safe_halt(); + local_irq_enable(); return 0; } } @@ -1609,7 +1631,7 @@ struct cpuidle_driver acpi_idle_driver = { */ static int acpi_processor_setup_cpuidle(struct acpi_processor *pr) { - int i, count = 0; + int i, count = CPUIDLE_DRIVER_STATE_START; struct acpi_processor_cx *cx; struct cpuidle_state *state; struct cpuidle_device *dev = &pr->power.dev; @@ -1638,13 +1660,14 @@ static int acpi_processor_setup_cpuidle(struct acpi_processor *pr) snprintf(state->name, CPUIDLE_NAME_LEN, "C%d", i); state->exit_latency = cx->latency; - state->target_residency = cx->latency * 6; + state->target_residency = cx->latency * latency_factor; state->power_usage = cx->power; state->flags = 0; switch (cx->type) { case ACPI_STATE_C1: state->flags |= CPUIDLE_FLAG_SHALLOW; + state->flags |= CPUIDLE_FLAG_TIME_VALID; state->enter = acpi_idle_enter_c1; dev->safe_state = state; break; @@ -1667,6 +1690,8 @@ static int acpi_processor_setup_cpuidle(struct acpi_processor *pr) } count++; + if (count == CPUIDLE_STATE_MAX) + break; } dev->state_count = count; diff --git a/drivers/acpi/processor_perflib.c b/drivers/acpi/processor_perflib.c index 463b0247cbc..f32010bee4d 100644 --- a/drivers/acpi/processor_perflib.c +++ b/drivers/acpi/processor_perflib.c @@ -60,6 +60,11 @@ static DEFINE_MUTEX(performance_mutex); * policy is adjusted accordingly. */ +static unsigned int ignore_ppc = 0; +module_param(ignore_ppc, uint, 0644); +MODULE_PARM_DESC(ignore_ppc, "If the frequency of your machine gets wrongly" \ + "limited by BIOS, this should help"); + #define PPC_REGISTERED 1 #define PPC_IN_USE 2 @@ -72,6 +77,9 @@ static int acpi_processor_ppc_notifier(struct notifier_block *nb, struct acpi_processor *pr; unsigned int ppc = 0; + if (ignore_ppc) + return 0; + mutex_lock(&performance_mutex); if (event != CPUFREQ_INCOMPATIBLE) @@ -130,7 +138,13 @@ static int acpi_processor_get_platform_limit(struct acpi_processor *pr) int acpi_processor_ppc_has_changed(struct acpi_processor *pr) { - int ret = acpi_processor_get_platform_limit(pr); + int ret; + + if (ignore_ppc) + return 0; + + ret = acpi_processor_get_platform_limit(pr); + if (ret < 0) return (ret); else diff --git a/drivers/acpi/processor_thermal.c b/drivers/acpi/processor_thermal.c index 06e6f3fb882..9cb43f52f7b 100644 --- a/drivers/acpi/processor_thermal.c +++ b/drivers/acpi/processor_thermal.c @@ -32,6 +32,7 @@ #include <linux/cpufreq.h> #include <linux/proc_fs.h> #include <linux/seq_file.h> +#include <linux/sysdev.h> #include <asm/uaccess.h> @@ -93,6 +94,9 @@ static int acpi_processor_apply_limit(struct acpi_processor *pr) * _any_ cpufreq driver and not only the acpi-cpufreq driver. */ +#define CPUFREQ_THERMAL_MIN_STEP 0 +#define CPUFREQ_THERMAL_MAX_STEP 3 + static unsigned int cpufreq_thermal_reduction_pctg[NR_CPUS]; static unsigned int acpi_thermal_cpufreq_is_init = 0; @@ -109,8 +113,9 @@ static int acpi_thermal_cpufreq_increase(unsigned int cpu) if (!cpu_has_cpufreq(cpu)) return -ENODEV; - if (cpufreq_thermal_reduction_pctg[cpu] < 60) { - cpufreq_thermal_reduction_pctg[cpu] += 20; + if (cpufreq_thermal_reduction_pctg[cpu] < + CPUFREQ_THERMAL_MAX_STEP) { + cpufreq_thermal_reduction_pctg[cpu]++; cpufreq_update_policy(cpu); return 0; } @@ -123,8 +128,9 @@ static int acpi_thermal_cpufreq_decrease(unsigned int cpu) if (!cpu_has_cpufreq(cpu)) return -ENODEV; - if (cpufreq_thermal_reduction_pctg[cpu] > 20) - cpufreq_thermal_reduction_pctg[cpu] -= 20; + if (cpufreq_thermal_reduction_pctg[cpu] > + (CPUFREQ_THERMAL_MIN_STEP + 1)) + cpufreq_thermal_reduction_pctg[cpu]--; else cpufreq_thermal_reduction_pctg[cpu] = 0; cpufreq_update_policy(cpu); @@ -143,7 +149,7 @@ static int acpi_thermal_cpufreq_notifier(struct notifier_block *nb, max_freq = (policy->cpuinfo.max_freq * - (100 - cpufreq_thermal_reduction_pctg[policy->cpu])) / 100; + (100 - cpufreq_thermal_reduction_pctg[policy->cpu] * 20)) / 100; cpufreq_verify_within_limits(policy, 0, max_freq); @@ -155,6 +161,32 @@ static struct notifier_block acpi_thermal_cpufreq_notifier_block = { .notifier_call = acpi_thermal_cpufreq_notifier, }; +static int cpufreq_get_max_state(unsigned int cpu) +{ + if (!cpu_has_cpufreq(cpu)) + return 0; + + return CPUFREQ_THERMAL_MAX_STEP; +} + +static int cpufreq_get_cur_state(unsigned int cpu) +{ + if (!cpu_has_cpufreq(cpu)) + return 0; + + return cpufreq_thermal_reduction_pctg[cpu]; +} + +static int cpufreq_set_cur_state(unsigned int cpu, int state) +{ + if (!cpu_has_cpufreq(cpu)) + return 0; + + cpufreq_thermal_reduction_pctg[cpu] = state; + cpufreq_update_policy(cpu); + return 0; +} + void acpi_thermal_cpufreq_init(void) { int i; @@ -179,6 +211,20 @@ void acpi_thermal_cpufreq_exit(void) } #else /* ! CONFIG_CPU_FREQ */ +static int cpufreq_get_max_state(unsigned int cpu) +{ + return 0; +} + +static int cpufreq_get_cur_state(unsigned int cpu) +{ + return 0; +} + +static int cpufreq_set_cur_state(unsigned int cpu, int state) +{ + return 0; +} static int acpi_thermal_cpufreq_increase(unsigned int cpu) { @@ -310,6 +356,84 @@ int acpi_processor_get_limit_info(struct acpi_processor *pr) return 0; } +/* thermal coolign device callbacks */ +static int acpi_processor_max_state(struct acpi_processor *pr) +{ + int max_state = 0; + + /* + * There exists four states according to + * cpufreq_thermal_reduction_ptg. 0, 1, 2, 3 + */ + max_state += cpufreq_get_max_state(pr->id); + if (pr->flags.throttling) + max_state += (pr->throttling.state_count -1); + + return max_state; +} +static int +processor_get_max_state(struct thermal_cooling_device *cdev, char *buf) +{ + struct acpi_device *device = cdev->devdata; + struct acpi_processor *pr = acpi_driver_data(device); + + if (!device || !pr) + return -EINVAL; + + return sprintf(buf, "%d\n", acpi_processor_max_state(pr)); +} + +static int +processor_get_cur_state(struct thermal_cooling_device *cdev, char *buf) +{ + struct acpi_device *device = cdev->devdata; + struct acpi_processor *pr = acpi_driver_data(device); + int cur_state; + + if (!device || !pr) + return -EINVAL; + + cur_state = cpufreq_get_cur_state(pr->id); + if (pr->flags.throttling) + cur_state += pr->throttling.state; + + return sprintf(buf, "%d\n", cur_state); +} + +static int +processor_set_cur_state(struct thermal_cooling_device *cdev, unsigned int state) +{ + struct acpi_device *device = cdev->devdata; + struct acpi_processor *pr = acpi_driver_data(device); + int result = 0; + int max_pstate; + + if (!device || !pr) + return -EINVAL; + + max_pstate = cpufreq_get_max_state(pr->id); + + if (state > acpi_processor_max_state(pr)) + return -EINVAL; + + if (state <= max_pstate) { + if (pr->flags.throttling && pr->throttling.state) + result = acpi_processor_set_throttling(pr, 0); + cpufreq_set_cur_state(pr->id, state); + } else { + cpufreq_set_cur_state(pr->id, max_pstate); + result = acpi_processor_set_throttling(pr, + state - max_pstate); + } + return result; +} + +struct thermal_cooling_device_ops processor_cooling_ops = { + .get_max_state = processor_get_max_state, + .get_cur_state = processor_get_cur_state, + .set_cur_state = processor_set_cur_state, +}; + /* /proc interface */ static int acpi_processor_limit_seq_show(struct seq_file *seq, void *offset) diff --git a/drivers/acpi/processor_throttling.c b/drivers/acpi/processor_throttling.c index 1685b40abda..1b8e592a824 100644 --- a/drivers/acpi/processor_throttling.c +++ b/drivers/acpi/processor_throttling.c @@ -45,9 +45,229 @@ #define _COMPONENT ACPI_PROCESSOR_COMPONENT ACPI_MODULE_NAME("processor_throttling"); +struct throttling_tstate { + unsigned int cpu; /* cpu nr */ + int target_state; /* target T-state */ +}; + +#define THROTTLING_PRECHANGE (1) +#define THROTTLING_POSTCHANGE (2) + static int acpi_processor_get_throttling(struct acpi_processor *pr); int acpi_processor_set_throttling(struct acpi_processor *pr, int state); +static int acpi_processor_update_tsd_coord(void) +{ + int count, count_target; + int retval = 0; + unsigned int i, j; + cpumask_t covered_cpus; + struct acpi_processor *pr, *match_pr; + struct acpi_tsd_package *pdomain, *match_pdomain; + struct acpi_processor_throttling *pthrottling, *match_pthrottling; + + /* + * Now that we have _TSD data from all CPUs, lets setup T-state + * coordination between all CPUs. + */ + for_each_possible_cpu(i) { + pr = processors[i]; + if (!pr) + continue; + + /* Basic validity check for domain info */ + pthrottling = &(pr->throttling); + + /* + * If tsd package for one cpu is invalid, the coordination + * among all CPUs is thought as invalid. + * Maybe it is ugly. + */ + if (!pthrottling->tsd_valid_flag) { + retval = -EINVAL; + break; + } + } + if (retval) + goto err_ret; + + cpus_clear(covered_cpus); + for_each_possible_cpu(i) { + pr = processors[i]; + if (!pr) + continue; + + if (cpu_isset(i, covered_cpus)) + continue; + pthrottling = &pr->throttling; + + pdomain = &(pthrottling->domain_info); + cpu_set(i, pthrottling->shared_cpu_map); + cpu_set(i, covered_cpus); + /* + * If the number of processor in the TSD domain is 1, it is + * unnecessary to parse the coordination for this CPU. + */ + if (pdomain->num_processors <= 1) + continue; + + /* Validate the Domain info */ + count_target = pdomain->num_processors; + count = 1; + + for_each_possible_cpu(j) { + if (i == j) + continue; + + match_pr = processors[j]; + if (!match_pr) + continue; + + match_pthrottling = &(match_pr->throttling); + match_pdomain = &(match_pthrottling->domain_info); + if (match_pdomain->domain != pdomain->domain) + continue; + + /* Here i and j are in the same domain. + * If two TSD packages have the same domain, they + * should have the same num_porcessors and + * coordination type. Otherwise it will be regarded + * as illegal. + */ + if (match_pdomain->num_processors != count_target) { + retval = -EINVAL; + goto err_ret; + } + + if (pdomain->coord_type != match_pdomain->coord_type) { + retval = -EINVAL; + goto err_ret; + } + + cpu_set(j, covered_cpus); + cpu_set(j, pthrottling->shared_cpu_map); + count++; + } + for_each_possible_cpu(j) { + if (i == j) + continue; + + match_pr = processors[j]; + if (!match_pr) + continue; + + match_pthrottling = &(match_pr->throttling); + match_pdomain = &(match_pthrottling->domain_info); + if (match_pdomain->domain != pdomain->domain) + continue; + + /* + * If some CPUS have the same domain, they + * will have the same shared_cpu_map. + */ + match_pthrottling->shared_cpu_map = + pthrottling->shared_cpu_map; + } + } + +err_ret: + for_each_possible_cpu(i) { + pr = processors[i]; + if (!pr) + continue; + + /* + * Assume no coordination on any error parsing domain info. + * The coordination type will be forced as SW_ALL. + */ + if (retval) { + pthrottling = &(pr->throttling); + cpus_clear(pthrottling->shared_cpu_map); + cpu_set(i, pthrottling->shared_cpu_map); + pthrottling->shared_type = DOMAIN_COORD_TYPE_SW_ALL; + } + } + + return retval; +} + +/* + * Update the T-state coordination after the _TSD + * data for all cpus is obtained. + */ +void acpi_processor_throttling_init(void) +{ + if (acpi_processor_update_tsd_coord()) + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Assume no T-state coordination\n")); + + return; +} + +static int acpi_processor_throttling_notifier(unsigned long event, void *data) +{ + struct throttling_tstate *p_tstate = data; + struct acpi_processor *pr; + unsigned int cpu ; + int target_state; + struct acpi_processor_limit *p_limit; + struct acpi_processor_throttling *p_throttling; + + cpu = p_tstate->cpu; + pr = processors[cpu]; + if (!pr) { + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Invalid pr pointer\n")); + return 0; + } + if (!pr->flags.throttling) { + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Throttling control is " + "unsupported on CPU %d\n", cpu)); + return 0; + } + target_state = p_tstate->target_state; + p_throttling = &(pr->throttling); + switch (event) { + case THROTTLING_PRECHANGE: + /* + * Prechange event is used to choose one proper t-state, + * which meets the limits of thermal, user and _TPC. + */ + p_limit = &pr->limit; + if (p_limit->thermal.tx > target_state) + target_state = p_limit->thermal.tx; + if (p_limit->user.tx > target_state) + target_state = p_limit->user.tx; + if (pr->throttling_platform_limit > target_state) + target_state = pr->throttling_platform_limit; + if (target_state >= p_throttling->state_count) { + printk(KERN_WARNING + "Exceed the limit of T-state \n"); + target_state = p_throttling->state_count - 1; + } + p_tstate->target_state = target_state; + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "PreChange Event:" + "target T-state of CPU %d is T%d\n", + cpu, target_state)); + break; + case THROTTLING_POSTCHANGE: + /* + * Postchange event is only used to update the + * T-state flag of acpi_processor_throttling. + */ + p_throttling->state = target_state; + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "PostChange Event:" + "CPU %d is switched to T%d\n", + cpu, target_state)); + break; + default: + printk(KERN_WARNING + "Unsupported Throttling notifier event\n"); + break; + } + + return 0; +} + /* * _TPC - Throttling Present Capabilities */ @@ -293,6 +513,10 @@ static int acpi_processor_get_tsd(struct acpi_processor *pr) struct acpi_buffer state = { 0, NULL }; union acpi_object *tsd = NULL; struct acpi_tsd_package *pdomain; + struct acpi_processor_throttling *pthrottling; + + pthrottling = &pr->throttling; + pthrottling->tsd_valid_flag = 0; status = acpi_evaluate_object(pr->handle, "_TSD", NULL, &buffer); if (ACPI_FAILURE(status)) { @@ -340,6 +564,22 @@ static int acpi_processor_get_tsd(struct acpi_processor *pr) goto end; } + pthrottling = &pr->throttling; + pthrottling->tsd_valid_flag = 1; + pthrottling->shared_type = pdomain->coord_type; + cpu_set(pr->id, pthrottling->shared_cpu_map); + /* + * If the coordination type is not defined in ACPI spec, + * the tsd_valid_flag will be clear and coordination type + * will be forecd as DOMAIN_COORD_TYPE_SW_ALL. + */ + if (pdomain->coord_type != DOMAIN_COORD_TYPE_SW_ALL && + pdomain->coord_type != DOMAIN_COORD_TYPE_SW_ANY && + pdomain->coord_type != DOMAIN_COORD_TYPE_HW_ALL) { + pthrottling->tsd_valid_flag = 0; + pthrottling->shared_type = DOMAIN_COORD_TYPE_SW_ALL; + } + end: kfree(buffer.pointer); return result; @@ -589,6 +829,11 @@ static int acpi_processor_get_throttling(struct acpi_processor *pr) cpumask_t saved_mask; int ret; + if (!pr) + return -EINVAL; + + if (!pr->flags.throttling) + return -ENODEV; /* * Migrate task to the cpu pointed by pr. */ @@ -742,13 +987,92 @@ static int acpi_processor_set_throttling_ptc(struct acpi_processor *pr, int acpi_processor_set_throttling(struct acpi_processor *pr, int state) { cpumask_t saved_mask; - int ret; + int ret = 0; + unsigned int i; + struct acpi_processor *match_pr; + struct acpi_processor_throttling *p_throttling; + struct throttling_tstate t_state; + cpumask_t online_throttling_cpus; + + if (!pr) + return -EINVAL; + + if (!pr->flags.throttling) + return -ENODEV; + + if ((state < 0) || (state > (pr->throttling.state_count - 1))) + return -EINVAL; + + saved_mask = current->cpus_allowed; + t_state.target_state = state; + p_throttling = &(pr->throttling); + cpus_and(online_throttling_cpus, cpu_online_map, + p_throttling->shared_cpu_map); /* - * Migrate task to the cpu pointed by pr. + * The throttling notifier will be called for every + * affected cpu in order to get one proper T-state. + * The notifier event is THROTTLING_PRECHANGE. */ - saved_mask = current->cpus_allowed; - set_cpus_allowed(current, cpumask_of_cpu(pr->id)); - ret = pr->throttling.acpi_processor_set_throttling(pr, state); + for_each_cpu_mask(i, online_throttling_cpus) { + t_state.cpu = i; + acpi_processor_throttling_notifier(THROTTLING_PRECHANGE, + &t_state); + } + /* + * The function of acpi_processor_set_throttling will be called + * to switch T-state. If the coordination type is SW_ALL or HW_ALL, + * it is necessary to call it for every affected cpu. Otherwise + * it can be called only for the cpu pointed by pr. + */ + if (p_throttling->shared_type == DOMAIN_COORD_TYPE_SW_ANY) { + set_cpus_allowed(current, cpumask_of_cpu(pr->id)); + ret = p_throttling->acpi_processor_set_throttling(pr, + t_state.target_state); + } else { + /* + * When the T-state coordination is SW_ALL or HW_ALL, + * it is necessary to set T-state for every affected + * cpus. + */ + for_each_cpu_mask(i, online_throttling_cpus) { + match_pr = processors[i]; + /* + * If the pointer is invalid, we will report the + * error message and continue. + */ + if (!match_pr) { + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Invalid Pointer for CPU %d\n", i)); + continue; + } + /* + * If the throttling control is unsupported on CPU i, + * we will report the error message and continue. + */ + if (!match_pr->flags.throttling) { + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Throttling Controll is unsupported " + "on CPU %d\n", i)); + continue; + } + t_state.cpu = i; + set_cpus_allowed(current, cpumask_of_cpu(i)); + ret = match_pr->throttling. + acpi_processor_set_throttling( + match_pr, t_state.target_state); + } + } + /* + * After the set_throttling is called, the + * throttling notifier is called for every + * affected cpu to update the T-states. + * The notifier event is THROTTLING_POSTCHANGE + */ + for_each_cpu_mask(i, online_throttling_cpus) { + t_state.cpu = i; + acpi_processor_throttling_notifier(THROTTLING_POSTCHANGE, + &t_state); + } /* restore the previous state */ set_cpus_allowed(current, saved_mask); return ret; @@ -757,6 +1081,7 @@ int acpi_processor_set_throttling(struct acpi_processor *pr, int state) int acpi_processor_get_throttling_info(struct acpi_processor *pr) { int result = 0; + struct acpi_processor_throttling *pthrottling; ACPI_DEBUG_PRINT((ACPI_DB_INFO, "pblk_address[0x%08x] duty_offset[%d] duty_width[%d]\n", @@ -788,7 +1113,16 @@ int acpi_processor_get_throttling_info(struct acpi_processor *pr) &acpi_processor_set_throttling_ptc; } - acpi_processor_get_tsd(pr); + /* + * If TSD package for one CPU can't be parsed successfully, it means + * that this CPU will have no coordination with other CPUs. + */ + if (acpi_processor_get_tsd(pr)) { + pthrottling = &pr->throttling; + pthrottling->tsd_valid_flag = 0; + cpu_set(pr->id, pthrottling->shared_cpu_map); + pthrottling->shared_type = DOMAIN_COORD_TYPE_SW_ALL; + } /* * PIIX4 Errata: We don't support throttling on the original PIIX4. diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 78a82b3c7b6..3fac011f9cf 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -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 PREFIX "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) = diff --git a/drivers/acpi/sleep/main.c b/drivers/acpi/sleep/main.c index 485de134707..293a1cbb47c 100644 --- a/drivers/acpi/sleep/main.c +++ b/drivers/acpi/sleep/main.c @@ -170,7 +170,7 @@ static int acpi_pm_enter(suspend_state_t pm_state) /* Reprogram control registers and execute _BFS */ acpi_leave_sleep_state_prep(acpi_state); - /* ACPI 3.0 specs (P62) says that it's the responsabilty + /* ACPI 3.0 specs (P62) says that it's the responsibility * of the OSPM to clear the status bit [ implying that the * POWER_BUTTON event should not reach userspace ] */ @@ -472,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) diff --git a/drivers/acpi/sleep/proc.c b/drivers/acpi/sleep/proc.c index 1538355c266..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; } @@ -251,27 +256,6 @@ acpi_system_write_alarm(struct file *file, if ((result = get_date_field(&p, &sec))) goto end; - if (sec > 59) { - min += 1; - sec -= 60; - } - if (min > 59) { - hr += 1; - min -= 60; - } - if (hr > 23) { - day += 1; - hr -= 24; - } - if (day > 31) { - mo += 1; - day -= 31; - } - if (mo > 12) { - yr += 1; - mo -= 12; - } - spin_lock_irq(&rtc_lock); rtc_control = CMOS_READ(RTC_CONTROL); @@ -288,24 +272,24 @@ acpi_system_write_alarm(struct file *file, spin_unlock_irq(&rtc_lock); if (sec > 59) { - min++; - sec -= 60; + min += sec/60; + sec = sec%60; } if (min > 59) { - hr++; - min -= 60; + hr += min/60; + min = min%60; } if (hr > 23) { - day++; - hr -= 24; + day += hr/24; + hr = hr%24; } if (day > 31) { - mo++; - day -= 31; + mo += day/32; + day = day%32; } if (mo > 12) { - yr++; - mo -= 12; + yr += mo/13; + mo = mo%13; } spin_lock_irq(&rtc_lock); diff --git a/drivers/acpi/system.c b/drivers/acpi/system.c index 5ffe0ea1896..ce881713f7a 100644 --- a/drivers/acpi/system.c +++ b/drivers/acpi/system.c @@ -40,6 +40,8 @@ ACPI_MODULE_NAME("system"); #define ACPI_SYSTEM_CLASS "system" #define ACPI_SYSTEM_DEVICE_NAME "System" +u32 acpi_irq_handled; + /* * Make ACPICA version work as module param */ @@ -166,6 +168,212 @@ static int acpi_system_sysfs_init(void) return 0; } +/* + * Detailed ACPI IRQ counters in /sys/firmware/acpi/interrupts/ + * See Documentation/ABI/testing/sysfs-firmware-acpi + */ + +#define COUNT_GPE 0 +#define COUNT_SCI 1 /* acpi_irq_handled */ +#define COUNT_ERROR 2 /* other */ +#define NUM_COUNTERS_EXTRA 3 + +static u32 *all_counters; +static u32 num_gpes; +static u32 num_counters; +static struct attribute **all_attrs; +static u32 acpi_gpe_count; + +static struct attribute_group interrupt_stats_attr_group = { + .name = "interrupts", +}; +static struct kobj_attribute *counter_attrs; + +static int count_num_gpes(void) +{ + int count = 0; + struct acpi_gpe_xrupt_info *gpe_xrupt_info; + struct acpi_gpe_block_info *gpe_block; + acpi_cpu_flags flags; + + flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock); + + gpe_xrupt_info = acpi_gbl_gpe_xrupt_list_head; + while (gpe_xrupt_info) { + gpe_block = gpe_xrupt_info->gpe_block_list_head; + while (gpe_block) { + count += gpe_block->register_count * + ACPI_GPE_REGISTER_WIDTH; + gpe_block = gpe_block->next; + } + gpe_xrupt_info = gpe_xrupt_info->next; + } + acpi_os_release_lock(acpi_gbl_gpe_lock, flags); + + return count; +} + +static void delete_gpe_attr_array(void) +{ + u32 *tmp = all_counters; + + all_counters = NULL; + kfree(tmp); + + if (counter_attrs) { + int i; + + for (i = 0; i < num_gpes; i++) + kfree(counter_attrs[i].attr.name); + + kfree(counter_attrs); + } + kfree(all_attrs); + + return; +} + +void acpi_os_gpe_count(u32 gpe_number) +{ + acpi_gpe_count++; + + if (!all_counters) + return; + + if (gpe_number < num_gpes) + all_counters[gpe_number]++; + else + all_counters[num_gpes + ACPI_NUM_FIXED_EVENTS + COUNT_ERROR]++; + + return; +} + +void acpi_os_fixed_event_count(u32 event_number) +{ + if (!all_counters) + return; + + if (event_number < ACPI_NUM_FIXED_EVENTS) + all_counters[num_gpes + event_number]++; + else + all_counters[num_gpes + ACPI_NUM_FIXED_EVENTS + COUNT_ERROR]++; + + return; +} + +static ssize_t counter_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + all_counters[num_gpes + ACPI_NUM_FIXED_EVENTS + COUNT_SCI] = + acpi_irq_handled; + all_counters[num_gpes + ACPI_NUM_FIXED_EVENTS + COUNT_GPE] = + acpi_gpe_count; + + return sprintf(buf, "%d\n", all_counters[attr - counter_attrs]); +} + +/* + * counter_set() sets the specified counter. + * setting the total "sci" file to any value clears all counters. + */ +static ssize_t counter_set(struct kobject *kobj, + struct kobj_attribute *attr, const char *buf, size_t size) +{ + int index = attr - counter_attrs; + + if (index == num_gpes + ACPI_NUM_FIXED_EVENTS + COUNT_SCI) { + int i; + for (i = 0; i < num_counters; ++i) + all_counters[i] = 0; + acpi_gpe_count = 0; + acpi_irq_handled = 0; + + } else + all_counters[index] = strtoul(buf, NULL, 0); + + return size; +} + +void acpi_irq_stats_init(void) +{ + int i; + + if (all_counters) + return; + + num_gpes = count_num_gpes(); + num_counters = num_gpes + ACPI_NUM_FIXED_EVENTS + NUM_COUNTERS_EXTRA; + + all_attrs = kzalloc(sizeof(struct attribute *) * (num_counters + 1), + GFP_KERNEL); + if (all_attrs == NULL) + return; + + all_counters = kzalloc(sizeof(u32) * (num_counters), GFP_KERNEL); + if (all_counters == NULL) + goto fail; + + counter_attrs = kzalloc(sizeof(struct kobj_attribute) * (num_counters), + GFP_KERNEL); + if (counter_attrs == NULL) + goto fail; + + for (i = 0; i < num_counters; ++i) { + char buffer[10]; + char *name; + + if (i < num_gpes) + sprintf(buffer, "gpe%02X", i); + else if (i == num_gpes + ACPI_EVENT_PMTIMER) + sprintf(buffer, "ff_pmtimer"); + else if (i == num_gpes + ACPI_EVENT_GLOBAL) + sprintf(buffer, "ff_gbl_lock"); + else if (i == num_gpes + ACPI_EVENT_POWER_BUTTON) + sprintf(buffer, "ff_pwr_btn"); + else if (i == num_gpes + ACPI_EVENT_SLEEP_BUTTON) + sprintf(buffer, "ff_slp_btn"); + else if (i == num_gpes + ACPI_EVENT_RTC) + sprintf(buffer, "ff_rt_clk"); + else if (i == num_gpes + ACPI_NUM_FIXED_EVENTS + COUNT_GPE) + sprintf(buffer, "gpe_all"); + else if (i == num_gpes + ACPI_NUM_FIXED_EVENTS + COUNT_SCI) + sprintf(buffer, "sci"); + else if (i == num_gpes + ACPI_NUM_FIXED_EVENTS + COUNT_ERROR) + sprintf(buffer, "error"); + else + sprintf(buffer, "bug%02X", i); + + name = kzalloc(strlen(buffer) + 1, GFP_KERNEL); + if (name == NULL) + goto fail; + strncpy(name, buffer, strlen(buffer) + 1); + + counter_attrs[i].attr.name = name; + counter_attrs[i].attr.mode = 0644; + counter_attrs[i].show = counter_show; + counter_attrs[i].store = counter_set; + + all_attrs[i] = &counter_attrs[i].attr; + } + + interrupt_stats_attr_group.attrs = all_attrs; + sysfs_create_group(acpi_kobj, &interrupt_stats_attr_group); + return; + +fail: + delete_gpe_attr_array(); + return; +} + +static void __exit interrupt_stats_exit(void) +{ + sysfs_remove_group(acpi_kobj, &interrupt_stats_attr_group); + + delete_gpe_attr_array(); + + return; +} + /* -------------------------------------------------------------------------- FS Interface (/proc) -------------------------------------------------------------------------- */ 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..8d4b79b4f93 100644 --- a/drivers/acpi/thermal.c +++ b/drivers/acpi/thermal.c @@ -43,7 +43,7 @@ #include <linux/seq_file.h> #include <linux/reboot.h> #include <asm/uaccess.h> - +#include <linux/thermal.h> #include <acpi/acpi_bus.h> #include <acpi/acpi_drivers.h> @@ -65,9 +65,6 @@ #define ACPI_THERMAL_MAX_ACTIVE 10 #define ACPI_THERMAL_MAX_LIMIT_STR_LEN 65 -#define KELVIN_TO_CELSIUS(t) (long)(((long)t-2732>=0) ? ((long)t-2732+5)/10 : ((long)t-2732-5)/10) -#define CELSIUS_TO_KELVIN(t) ((t+273)*10) - #define _COMPONENT ACPI_THERMAL_COMPONENT ACPI_MODULE_NAME("thermal"); @@ -195,6 +192,8 @@ struct acpi_thermal { struct acpi_thermal_trips trips; struct acpi_handle_list devices; struct timer_list timer; + struct thermal_zone_device *thermal_zone; + int tz_enabled; struct mutex lock; }; @@ -321,178 +320,226 @@ static int acpi_thermal_set_cooling_mode(struct acpi_thermal *tz, int mode) return 0; } -static int acpi_thermal_get_trip_points(struct acpi_thermal *tz) -{ - acpi_status status = AE_OK; - int i = 0; +#define ACPI_TRIPS_CRITICAL 0x01 +#define ACPI_TRIPS_HOT 0x02 +#define ACPI_TRIPS_PASSIVE 0x04 +#define ACPI_TRIPS_ACTIVE 0x08 +#define ACPI_TRIPS_DEVICES 0x10 +#define ACPI_TRIPS_REFRESH_THRESHOLDS (ACPI_TRIPS_PASSIVE | ACPI_TRIPS_ACTIVE) +#define ACPI_TRIPS_REFRESH_DEVICES ACPI_TRIPS_DEVICES - if (!tz) - return -EINVAL; +#define ACPI_TRIPS_INIT (ACPI_TRIPS_CRITICAL | ACPI_TRIPS_HOT | \ + ACPI_TRIPS_PASSIVE | ACPI_TRIPS_ACTIVE | \ + ACPI_TRIPS_DEVICES) - /* Critical Shutdown (required) */ - - status = acpi_evaluate_integer(tz->device->handle, "_CRT", NULL, - &tz->trips.critical.temperature); - if (ACPI_FAILURE(status)) { - tz->trips.critical.flags.valid = 0; - ACPI_EXCEPTION((AE_INFO, status, "No critical threshold")); - return -ENODEV; - } else { - tz->trips.critical.flags.valid = 1; - ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "Found critical threshold [%lu]\n", - tz->trips.critical.temperature)); - } +/* + * This exception is thrown out in two cases: + * 1.An invalid trip point becomes invalid or a valid trip point becomes invalid + * when re-evaluating the AML code. + * 2.TODO: Devices listed in _PSL, _ALx, _TZD may change. + * We need to re-bind the cooling devices of a thermal zone when this occurs. + */ +#define ACPI_THERMAL_TRIPS_EXCEPTION(flags, str) \ +do { \ + if (flags != ACPI_TRIPS_INIT) \ + ACPI_EXCEPTION((AE_INFO, AE_ERROR, \ + "ACPI thermal trip point %s changed\n" \ + "Please send acpidump to linux-acpi@vger.kernel.org\n", str)); \ +} while (0) + +static int acpi_thermal_trips_update(struct acpi_thermal *tz, int flag) +{ + acpi_status status = AE_OK; + struct acpi_handle_list devices; + int valid = 0; + int i; - if (tz->trips.critical.flags.valid == 1) { - if (crt == -1) { + /* Critical Shutdown (required) */ + if (flag & ACPI_TRIPS_CRITICAL) { + status = acpi_evaluate_integer(tz->device->handle, + "_CRT", NULL, &tz->trips.critical.temperature); + if (ACPI_FAILURE(status)) { tz->trips.critical.flags.valid = 0; - } else if (crt > 0) { - unsigned long crt_k = CELSIUS_TO_KELVIN(crt); - - /* - * Allow override to lower critical threshold - */ - if (crt_k < tz->trips.critical.temperature) - tz->trips.critical.temperature = crt_k; + ACPI_EXCEPTION((AE_INFO, status, + "No critical threshold")); + return -ENODEV; + } else { + tz->trips.critical.flags.valid = 1; + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Found critical threshold [%lu]\n", + tz->trips.critical.temperature)); + } + if (tz->trips.critical.flags.valid == 1) { + if (crt == -1) { + tz->trips.critical.flags.valid = 0; + } else if (crt > 0) { + unsigned long crt_k = CELSIUS_TO_KELVIN(crt); + /* + * Allow override to lower critical threshold + */ + if (crt_k < tz->trips.critical.temperature) + tz->trips.critical.temperature = crt_k; + } } } /* Critical Sleep (optional) */ - - status = - acpi_evaluate_integer(tz->device->handle, "_HOT", NULL, - &tz->trips.hot.temperature); - if (ACPI_FAILURE(status)) { - tz->trips.hot.flags.valid = 0; - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "No hot threshold\n")); - } else { - tz->trips.hot.flags.valid = 1; - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found hot threshold [%lu]\n", - tz->trips.hot.temperature)); - } - - /* Passive: Processors (optional) */ - - if (psv == -1) { - status = AE_SUPPORT; - } else if (psv > 0) { - tz->trips.passive.temperature = CELSIUS_TO_KELVIN(psv); - status = AE_OK; - } else { + if (flag & ACPI_TRIPS_HOT) { status = acpi_evaluate_integer(tz->device->handle, - "_PSV", NULL, &tz->trips.passive.temperature); + "_HOT", NULL, &tz->trips.hot.temperature); + if (ACPI_FAILURE(status)) { + tz->trips.hot.flags.valid = 0; + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "No hot threshold\n")); + } else { + tz->trips.hot.flags.valid = 1; + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Found hot threshold [%lu]\n", + tz->trips.critical.temperature)); + } } - if (ACPI_FAILURE(status)) { - tz->trips.passive.flags.valid = 0; - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "No passive threshold\n")); - } else { - tz->trips.passive.flags.valid = 1; - - status = - acpi_evaluate_integer(tz->device->handle, "_TC1", NULL, - &tz->trips.passive.tc1); - if (ACPI_FAILURE(status)) - tz->trips.passive.flags.valid = 0; - - status = - acpi_evaluate_integer(tz->device->handle, "_TC2", NULL, - &tz->trips.passive.tc2); - if (ACPI_FAILURE(status)) - tz->trips.passive.flags.valid = 0; + /* Passive (optional) */ + if (flag & ACPI_TRIPS_PASSIVE) { + valid = tz->trips.passive.flags.valid; + if (psv == -1) { + status = AE_SUPPORT; + } else if (psv > 0) { + tz->trips.passive.temperature = CELSIUS_TO_KELVIN(psv); + status = AE_OK; + } else { + status = acpi_evaluate_integer(tz->device->handle, + "_PSV", NULL, &tz->trips.passive.temperature); + } - status = - acpi_evaluate_integer(tz->device->handle, "_TSP", NULL, - &tz->trips.passive.tsp); if (ACPI_FAILURE(status)) tz->trips.passive.flags.valid = 0; - - status = - acpi_evaluate_reference(tz->device->handle, "_PSL", NULL, - &tz->trips.passive.devices); + else { + tz->trips.passive.flags.valid = 1; + if (flag == ACPI_TRIPS_INIT) { + status = acpi_evaluate_integer( + tz->device->handle, "_TC1", + NULL, &tz->trips.passive.tc1); + if (ACPI_FAILURE(status)) + tz->trips.passive.flags.valid = 0; + status = acpi_evaluate_integer( + tz->device->handle, "_TC2", + NULL, &tz->trips.passive.tc2); + if (ACPI_FAILURE(status)) + tz->trips.passive.flags.valid = 0; + status = acpi_evaluate_integer( + tz->device->handle, "_TSP", + NULL, &tz->trips.passive.tsp); + if (ACPI_FAILURE(status)) + tz->trips.passive.flags.valid = 0; + } + } + } + if ((flag & ACPI_TRIPS_DEVICES) && tz->trips.passive.flags.valid) { + memset(&devices, 0, sizeof(struct acpi_handle_list)); + status = acpi_evaluate_reference(tz->device->handle, "_PSL", + NULL, &devices); if (ACPI_FAILURE(status)) tz->trips.passive.flags.valid = 0; - - if (!tz->trips.passive.flags.valid) - printk(KERN_WARNING PREFIX "Invalid passive threshold\n"); else - ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "Found passive threshold [%lu]\n", - tz->trips.passive.temperature)); - } + tz->trips.passive.flags.valid = 1; - /* Active: Fans, etc. (optional) */ + if (memcmp(&tz->trips.passive.devices, &devices, + sizeof(struct acpi_handle_list))) { + memcpy(&tz->trips.passive.devices, &devices, + sizeof(struct acpi_handle_list)); + ACPI_THERMAL_TRIPS_EXCEPTION(flag, "device"); + } + } + if ((flag & ACPI_TRIPS_PASSIVE) || (flag & ACPI_TRIPS_DEVICES)) { + if (valid != tz->trips.passive.flags.valid) + ACPI_THERMAL_TRIPS_EXCEPTION(flag, "state"); + } + /* Active (optional) */ for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE; i++) { - char name[5] = { '_', 'A', 'C', ('0' + i), '\0' }; + valid = tz->trips.active[i].flags.valid; if (act == -1) - break; /* disable all active trip points */ - - status = acpi_evaluate_integer(tz->device->handle, - name, NULL, &tz->trips.active[i].temperature); - - if (ACPI_FAILURE(status)) { - if (i == 0) /* no active trip points */ + break; /* disable all active trip points */ + + if (flag & ACPI_TRIPS_ACTIVE) { + status = acpi_evaluate_integer(tz->device->handle, + name, NULL, &tz->trips.active[i].temperature); + if (ACPI_FAILURE(status)) { + tz->trips.active[i].flags.valid = 0; + if (i == 0) + break; + if (act <= 0) + break; + if (i == 1) + tz->trips.active[0].temperature = + CELSIUS_TO_KELVIN(act); + else + /* + * Don't allow override higher than + * the next higher trip point + */ + tz->trips.active[i - 1].temperature = + (tz->trips.active[i - 2].temperature < + CELSIUS_TO_KELVIN(act) ? + tz->trips.active[i - 2].temperature : + CELSIUS_TO_KELVIN(act)); break; - if (act <= 0) /* no override requested */ - break; - if (i == 1) { /* 1 trip point */ - tz->trips.active[0].temperature = - CELSIUS_TO_KELVIN(act); - } else { /* multiple trips */ - /* - * Don't allow override higher than - * the next higher trip point - */ - tz->trips.active[i - 1].temperature = - (tz->trips.active[i - 2].temperature < - CELSIUS_TO_KELVIN(act) ? - tz->trips.active[i - 2].temperature : - CELSIUS_TO_KELVIN(act)); - } - break; + } else + tz->trips.active[i].flags.valid = 1; } name[2] = 'L'; - status = - acpi_evaluate_reference(tz->device->handle, name, NULL, - &tz->trips.active[i].devices); - if (ACPI_SUCCESS(status)) { - tz->trips.active[i].flags.valid = 1; - ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "Found active threshold [%d]:[%lu]\n", - i, tz->trips.active[i].temperature)); - } else - ACPI_EXCEPTION((AE_INFO, status, - "Invalid active threshold [%d]", i)); + if ((flag & ACPI_TRIPS_DEVICES) && tz->trips.active[i].flags.valid ) { + memset(&devices, 0, sizeof(struct acpi_handle_list)); + status = acpi_evaluate_reference(tz->device->handle, + name, NULL, &devices); + if (ACPI_FAILURE(status)) + tz->trips.active[i].flags.valid = 0; + else + tz->trips.active[i].flags.valid = 1; + + if (memcmp(&tz->trips.active[i].devices, &devices, + sizeof(struct acpi_handle_list))) { + memcpy(&tz->trips.active[i].devices, &devices, + sizeof(struct acpi_handle_list)); + ACPI_THERMAL_TRIPS_EXCEPTION(flag, "device"); + } + } + if ((flag & ACPI_TRIPS_ACTIVE) || (flag & ACPI_TRIPS_DEVICES)) + if (valid != tz->trips.active[i].flags.valid) + ACPI_THERMAL_TRIPS_EXCEPTION(flag, "state"); + + if (!tz->trips.active[i].flags.valid) + break; + } + + if (flag & ACPI_TRIPS_DEVICES) { + memset(&devices, 0, sizeof(struct acpi_handle_list)); + status = acpi_evaluate_reference(tz->device->handle, "_TZD", + NULL, &devices); + if (memcmp(&tz->devices, &devices, + sizeof(struct acpi_handle_list))) { + memcpy(&tz->devices, &devices, + sizeof(struct acpi_handle_list)); + ACPI_THERMAL_TRIPS_EXCEPTION(flag, "device"); + } } return 0; } -static int acpi_thermal_get_devices(struct acpi_thermal *tz) +static int acpi_thermal_get_trip_points(struct acpi_thermal *tz) { - acpi_status status = AE_OK; - - - if (!tz) - return -EINVAL; - - status = - acpi_evaluate_reference(tz->device->handle, "_TZD", NULL, &tz->devices); - if (ACPI_FAILURE(status)) - return -ENODEV; - - return 0; + return acpi_thermal_trips_update(tz, ACPI_TRIPS_INIT); } 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 +548,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 +555,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 +584,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; } @@ -732,6 +782,9 @@ static void acpi_thermal_check(void *data) if (result) goto unlock; + if (!tz->tz_enabled) + goto unlock; + memset(&tz->state, 0, sizeof(tz->state)); /* @@ -825,6 +878,290 @@ static void acpi_thermal_check(void *data) mutex_unlock(&tz->lock); } +/* sys I/F for generic thermal sysfs support */ +static int thermal_get_temp(struct thermal_zone_device *thermal, char *buf) +{ + struct acpi_thermal *tz = thermal->devdata; + + if (!tz) + return -EINVAL; + + return sprintf(buf, "%ld\n", KELVIN_TO_CELSIUS(tz->temperature)); +} + +static const char enabled[] = "kernel"; +static const char disabled[] = "user"; +static int thermal_get_mode(struct thermal_zone_device *thermal, + char *buf) +{ + struct acpi_thermal *tz = thermal->devdata; + + if (!tz) + return -EINVAL; + + return sprintf(buf, "%s\n", tz->tz_enabled ? + enabled : disabled); +} + +static int thermal_set_mode(struct thermal_zone_device *thermal, + const char *buf) +{ + struct acpi_thermal *tz = thermal->devdata; + int enable; + + if (!tz) + return -EINVAL; + + /* + * enable/disable thermal management from ACPI thermal driver + */ + if (!strncmp(buf, enabled, sizeof enabled - 1)) + enable = 1; + else if (!strncmp(buf, disabled, sizeof disabled - 1)) + enable = 0; + else + return -EINVAL; + + if (enable != tz->tz_enabled) { + tz->tz_enabled = enable; + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "%s ACPI thermal control\n", + tz->tz_enabled ? enabled : disabled)); + acpi_thermal_check(tz); + } + return 0; +} + +static int thermal_get_trip_type(struct thermal_zone_device *thermal, + int trip, char *buf) +{ + struct acpi_thermal *tz = thermal->devdata; + int i; + + if (!tz || trip < 0) + return -EINVAL; + + if (tz->trips.critical.flags.valid) { + if (!trip) + return sprintf(buf, "critical\n"); + trip--; + } + + if (tz->trips.hot.flags.valid) { + if (!trip) + return sprintf(buf, "hot\n"); + trip--; + } + + if (tz->trips.passive.flags.valid) { + if (!trip) + return sprintf(buf, "passive\n"); + trip--; + } + + for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE && + tz->trips.active[i].flags.valid; i++) { + if (!trip) + return sprintf(buf, "active%d\n", i); + trip--; + } + + return -EINVAL; +} + +static int thermal_get_trip_temp(struct thermal_zone_device *thermal, + int trip, char *buf) +{ + struct acpi_thermal *tz = thermal->devdata; + int i; + + if (!tz || trip < 0) + return -EINVAL; + + if (tz->trips.critical.flags.valid) { + if (!trip) + return sprintf(buf, "%ld\n", KELVIN_TO_CELSIUS( + tz->trips.critical.temperature)); + trip--; + } + + if (tz->trips.hot.flags.valid) { + if (!trip) + return sprintf(buf, "%ld\n", KELVIN_TO_CELSIUS( + tz->trips.hot.temperature)); + trip--; + } + + if (tz->trips.passive.flags.valid) { + if (!trip) + return sprintf(buf, "%ld\n", KELVIN_TO_CELSIUS( + tz->trips.passive.temperature)); + trip--; + } + + for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE && + tz->trips.active[i].flags.valid; i++) { + if (!trip) + return sprintf(buf, "%ld\n", KELVIN_TO_CELSIUS( + tz->trips.active[i].temperature)); + trip--; + } + + return -EINVAL; +} + +typedef int (*cb)(struct thermal_zone_device *, int, + struct thermal_cooling_device *); +static int acpi_thermal_cooling_device_cb(struct thermal_zone_device *thermal, + struct thermal_cooling_device *cdev, + cb action) +{ + struct acpi_device *device = cdev->devdata; + struct acpi_thermal *tz = thermal->devdata; + struct acpi_device *dev; + acpi_status status; + acpi_handle handle; + int i; + int j; + int trip = -1; + int result = 0; + + if (tz->trips.critical.flags.valid) + trip++; + + if (tz->trips.hot.flags.valid) + trip++; + + if (tz->trips.passive.flags.valid) { + trip++; + for (i = 0; i < tz->trips.passive.devices.count; + i++) { + handle = tz->trips.passive.devices.handles[i]; + status = acpi_bus_get_device(handle, &dev); + if (ACPI_SUCCESS(status) && (dev == device)) { + result = action(thermal, trip, cdev); + if (result) + goto failed; + } + } + } + + for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE; i++) { + if (!tz->trips.active[i].flags.valid) + break; + trip++; + for (j = 0; + j < tz->trips.active[i].devices.count; + j++) { + handle = tz->trips.active[i].devices.handles[j]; + status = acpi_bus_get_device(handle, &dev); + if (ACPI_SUCCESS(status) && (dev == device)) { + result = action(thermal, trip, cdev); + if (result) + goto failed; + } + } + } + + for (i = 0; i < tz->devices.count; i++) { + handle = tz->devices.handles[i]; + status = acpi_bus_get_device(handle, &dev); + if (ACPI_SUCCESS(status) && (dev == device)) { + result = action(thermal, -1, cdev); + if (result) + goto failed; + } + } + +failed: + return result; +} + +static int +acpi_thermal_bind_cooling_device(struct thermal_zone_device *thermal, + struct thermal_cooling_device *cdev) +{ + return acpi_thermal_cooling_device_cb(thermal, cdev, + thermal_zone_bind_cooling_device); +} + +static int +acpi_thermal_unbind_cooling_device(struct thermal_zone_device *thermal, + struct thermal_cooling_device *cdev) +{ + return acpi_thermal_cooling_device_cb(thermal, cdev, + thermal_zone_unbind_cooling_device); +} + +static struct thermal_zone_device_ops acpi_thermal_zone_ops = { + .bind = acpi_thermal_bind_cooling_device, + .unbind = acpi_thermal_unbind_cooling_device, + .get_temp = thermal_get_temp, + .get_mode = thermal_get_mode, + .set_mode = thermal_set_mode, + .get_trip_type = thermal_get_trip_type, + .get_trip_temp = thermal_get_trip_temp, +}; + +static int acpi_thermal_register_thermal_zone(struct acpi_thermal *tz) +{ + int trips = 0; + int result; + acpi_status status; + int i; + + if (tz->trips.critical.flags.valid) + trips++; + + if (tz->trips.hot.flags.valid) + trips++; + + if (tz->trips.passive.flags.valid) + trips++; + + for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE && + tz->trips.active[i].flags.valid; i++, trips++); + tz->thermal_zone = thermal_zone_device_register("ACPI thermal zone", + trips, tz, &acpi_thermal_zone_ops); + if (!tz->thermal_zone) + return -ENODEV; + + result = sysfs_create_link(&tz->device->dev.kobj, + &tz->thermal_zone->device.kobj, "thermal_zone"); + if (result) + return result; + + result = sysfs_create_link(&tz->thermal_zone->device.kobj, + &tz->device->dev.kobj, "device"); + if (result) + return result; + + status = acpi_attach_data(tz->device->handle, + acpi_bus_private_data_handler, + tz->thermal_zone); + if (ACPI_FAILURE(status)) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, + "Error attaching device data\n")); + return -ENODEV; + } + + tz->tz_enabled = 1; + + printk(KERN_INFO PREFIX "%s is registered as thermal_zone%d\n", + tz->device->dev.bus_id, tz->thermal_zone->id); + return 0; +} + +static void acpi_thermal_unregister_thermal_zone(struct acpi_thermal *tz) +{ + sysfs_remove_link(&tz->device->dev.kobj, "thermal_zone"); + sysfs_remove_link(&tz->thermal_zone->device.kobj, "device"); + thermal_zone_device_unregister(tz->thermal_zone); + tz->thermal_zone = NULL; + acpi_detach_data(tz->device->handle, acpi_bus_private_data_handler); +} + + /* -------------------------------------------------------------------------- FS Interface (/proc) -------------------------------------------------------------------------- */ @@ -1181,15 +1518,15 @@ static void acpi_thermal_notify(acpi_handle handle, u32 event, void *data) acpi_thermal_check(tz); break; case ACPI_THERMAL_NOTIFY_THRESHOLDS: - acpi_thermal_get_trip_points(tz); + acpi_thermal_trips_update(tz, ACPI_TRIPS_REFRESH_THRESHOLDS); acpi_thermal_check(tz); acpi_bus_generate_proc_event(device, event, 0); acpi_bus_generate_netlink_event(device->pnp.device_class, device->dev.bus_id, event, 0); break; case ACPI_THERMAL_NOTIFY_DEVICES: - if (tz->flags.devices) - acpi_thermal_get_devices(tz); + acpi_thermal_trips_update(tz, ACPI_TRIPS_REFRESH_DEVICES); + acpi_thermal_check(tz); acpi_bus_generate_proc_event(device, event, 0); acpi_bus_generate_netlink_event(device->pnp.device_class, device->dev.bus_id, event, 0); @@ -1232,11 +1569,6 @@ static int acpi_thermal_get_info(struct acpi_thermal *tz) else acpi_thermal_get_polling_frequency(tz); - /* Get devices in this thermal zone [_TZD] (optional) */ - result = acpi_thermal_get_devices(tz); - if (!result) - tz->flags.devices = 1; - return 0; } @@ -1260,13 +1592,19 @@ static int acpi_thermal_add(struct acpi_device *device) strcpy(acpi_device_class(device), ACPI_THERMAL_CLASS); acpi_driver_data(device) = tz; mutex_init(&tz->lock); + + result = acpi_thermal_get_info(tz); if (result) - goto end; + goto free_memory; + + result = acpi_thermal_register_thermal_zone(tz); + if (result) + goto free_memory; result = acpi_thermal_add_fs(device); if (result) - goto end; + goto unregister_thermal_zone; init_timer(&tz->timer); @@ -1277,19 +1615,21 @@ static int acpi_thermal_add(struct acpi_device *device) acpi_thermal_notify, tz); if (ACPI_FAILURE(status)) { result = -ENODEV; - goto end; + goto remove_fs; } printk(KERN_INFO PREFIX "%s [%s] (%ld C)\n", acpi_device_name(device), acpi_device_bid(device), KELVIN_TO_CELSIUS(tz->temperature)); + goto end; - end: - if (result) { - acpi_thermal_remove_fs(device); - kfree(tz); - } - +remove_fs: + acpi_thermal_remove_fs(device); +unregister_thermal_zone: + thermal_zone_device_unregister(tz->thermal_zone); +free_memory: + kfree(tz); +end: return result; } @@ -1329,6 +1669,7 @@ static int acpi_thermal_remove(struct acpi_device *device, int type) } acpi_thermal_remove_fs(device); + acpi_thermal_unregister_thermal_zone(tz); mutex_destroy(&tz->lock); kfree(tz); return 0; diff --git a/drivers/acpi/utilities/utglobal.c b/drivers/acpi/utilities/utglobal.c index 93ea8290b4f..630c9a2c5b7 100644 --- a/drivers/acpi/utilities/utglobal.c +++ b/drivers/acpi/utilities/utglobal.c @@ -671,7 +671,6 @@ void acpi_ut_init_globals(void) /* GPE support */ - acpi_gpe_count = 0; acpi_gbl_gpe_xrupt_list_head = NULL; acpi_gbl_gpe_fadt_blocks[0] = NULL; acpi_gbl_gpe_fadt_blocks[1] = NULL; @@ -735,4 +734,3 @@ void acpi_ut_init_globals(void) ACPI_EXPORT_SYMBOL(acpi_dbg_level) ACPI_EXPORT_SYMBOL(acpi_dbg_layer) - ACPI_EXPORT_SYMBOL(acpi_gpe_count) diff --git a/drivers/acpi/video.c b/drivers/acpi/video.c index bd77e81e81c..7f714fa2a45 100644 --- a/drivers/acpi/video.c +++ b/drivers/acpi/video.c @@ -34,6 +34,7 @@ #include <linux/seq_file.h> #include <linux/input.h> #include <linux/backlight.h> +#include <linux/thermal.h> #include <linux/video_output.h> #include <asm/uaccess.h> @@ -72,8 +73,12 @@ MODULE_AUTHOR("Bruno Ducrot"); MODULE_DESCRIPTION("ACPI Video Driver"); MODULE_LICENSE("GPL"); +static int brightness_switch_enabled = 1; +module_param(brightness_switch_enabled, bool, 0644); + static int acpi_video_bus_add(struct acpi_device *device); static int acpi_video_bus_remove(struct acpi_device *device, int type); +static int acpi_video_resume(struct acpi_device *device); static const struct acpi_device_id video_device_ids[] = { {ACPI_VIDEO_HID, 0}, @@ -88,6 +93,7 @@ static struct acpi_driver acpi_video_bus = { .ops = { .add = acpi_video_bus_add, .remove = acpi_video_bus_remove, + .resume = acpi_video_resume, }, }; @@ -179,6 +185,7 @@ struct acpi_video_device { struct acpi_device *dev; struct acpi_video_device_brightness *brightness; struct backlight_device *backlight; + struct thermal_cooling_device *cdev; struct output_device *output_dev; }; @@ -273,7 +280,6 @@ static void acpi_video_device_rebind(struct acpi_video_bus *video); static void acpi_video_device_bind(struct acpi_video_bus *video, struct acpi_video_device *device); static int acpi_video_device_enumerate(struct acpi_video_bus *video); -static int acpi_video_switch_output(struct acpi_video_bus *video, int event); static int acpi_video_device_lcd_set_level(struct acpi_video_device *device, int level); static int acpi_video_device_lcd_get_level_current( @@ -292,18 +298,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; } @@ -334,6 +348,54 @@ static struct output_properties acpi_output_properties = { .set_state = acpi_video_output_set, .get_status = acpi_video_output_get, }; + + +/* thermal cooling device callbacks */ +static int video_get_max_state(struct thermal_cooling_device *cdev, char *buf) +{ + struct acpi_device *device = cdev->devdata; + struct acpi_video_device *video = acpi_driver_data(device); + + return sprintf(buf, "%d\n", video->brightness->count - 3); +} + +static int video_get_cur_state(struct thermal_cooling_device *cdev, char *buf) +{ + struct acpi_device *device = cdev->devdata; + struct acpi_video_device *video = acpi_driver_data(device); + unsigned long level; + int state; + + acpi_video_device_lcd_get_level_current(video, &level); + for (state = 2; state < video->brightness->count; state++) + if (level == video->brightness->levels[state]) + return sprintf(buf, "%d\n", + video->brightness->count - state - 1); + + return -EINVAL; +} + +static int +video_set_cur_state(struct thermal_cooling_device *cdev, unsigned int state) +{ + struct acpi_device *device = cdev->devdata; + struct acpi_video_device *video = acpi_driver_data(device); + int level; + + if ( state >= video->brightness->count - 2) + return -EINVAL; + + state = video->brightness->count - state; + level = video->brightness->levels[state -1]; + return acpi_video_device_lcd_set_level(video, level); +} + +static struct thermal_cooling_device_ops video_cooling_ops = { + .get_max_state = video_get_max_state, + .get_cur_state = video_get_cur_state, + .set_cur_state = video_set_cur_state, +}; + /* -------------------------------------------------------------------------- Video Management -------------------------------------------------------------------------- */ @@ -652,7 +714,7 @@ 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; + int result; static int count = 0; char *name; name = kzalloc(MAX_NAME_LEN, GFP_KERNEL); @@ -660,14 +722,30 @@ 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); + + device->cdev = thermal_cooling_device_register("LCD", + device->dev, &video_cooling_ops); + if (device->cdev) { + printk(KERN_INFO PREFIX + "%s is registered as cooling_device%d\n", + device->dev->dev.bus_id, device->cdev->id); + result = sysfs_create_link(&device->dev->dev.kobj, + &device->cdev->device.kobj, + "thermal_cooling"); + if (result) + printk(KERN_ERR PREFIX "Create sysfs link\n"); + result = sysfs_create_link(&device->cdev->device.kobj, + &device->dev->dev.kobj, + "device"); + if (result) + printk(KERN_ERR PREFIX "Create sysfs link\n"); + } } if (device->cap._DCS && device->cap._DSS){ static int count = 0; @@ -726,11 +804,40 @@ static void acpi_video_bus_find_cap(struct acpi_video_bus *video) static int acpi_video_bus_check(struct acpi_video_bus *video) { acpi_status status = -ENOENT; - + long device_id; + struct device *dev; + struct acpi_device *device; if (!video) return -EINVAL; + device = video->device; + + 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); + /* Since there is no HID, CID and so on for VGA driver, we have * to check well known required nodes. */ @@ -1256,8 +1363,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); @@ -1580,64 +1716,6 @@ static int acpi_video_device_enumerate(struct acpi_video_bus *video) return status; } -/* - * Arg: - * video : video bus device - * event : notify event - * - * Return: - * < 0 : error - * - * 1. Find out the current active output device. - * 2. Identify the next output device to switch to. - * 3. call _DSS to do actual switch. - */ - -static int acpi_video_switch_output(struct acpi_video_bus *video, int event) -{ - struct list_head *node; - struct acpi_video_device *dev = NULL; - struct acpi_video_device *dev_next = NULL; - struct acpi_video_device *dev_prev = NULL; - unsigned long state; - int status = 0; - - mutex_lock(&video->device_list_lock); - - list_for_each(node, &video->video_device_list) { - dev = container_of(node, struct acpi_video_device, entry); - status = acpi_video_device_get_state(dev, &state); - if (state & 0x2) { - dev_next = container_of(node->next, - struct acpi_video_device, entry); - dev_prev = container_of(node->prev, - struct acpi_video_device, entry); - goto out; - } - } - - dev_next = container_of(node->next, struct acpi_video_device, entry); - dev_prev = container_of(node->prev, struct acpi_video_device, entry); - - out: - mutex_unlock(&video->device_list_lock); - - switch (event) { - case ACPI_VIDEO_NOTIFY_CYCLE: - case ACPI_VIDEO_NOTIFY_NEXT_OUTPUT: - acpi_video_device_set_state(dev, 0); - acpi_video_device_set_state(dev_next, 0x80000001); - break; - case ACPI_VIDEO_NOTIFY_PREV_OUTPUT: - acpi_video_device_set_state(dev, 0); - acpi_video_device_set_state(dev_prev, 0x80000001); - default: - break; - } - - return status; -} - static int acpi_video_get_next_level(struct acpi_video_device *device, u32 level_current, u32 event) @@ -1729,6 +1807,14 @@ static int acpi_video_bus_put_one_device(struct acpi_video_device *device) ACPI_DEVICE_NOTIFY, acpi_video_device_notify); backlight_device_unregister(device->backlight); + if (device->cdev) { + sysfs_remove_link(&device->dev->dev.kobj, + "thermal_cooling"); + sysfs_remove_link(&device->cdev->device.kobj, + "device"); + thermal_cooling_device_unregister(device->cdev); + device->cdev = NULL; + } video_output_unregister(device->output_dev); return 0; @@ -1797,23 +1883,19 @@ static void acpi_video_bus_notify(acpi_handle handle, u32 event, void *data) * connector. */ acpi_video_device_enumerate(video); acpi_video_device_rebind(video); - acpi_video_switch_output(video, event); acpi_bus_generate_proc_event(device, event, 0); keycode = KEY_SWITCHVIDEOMODE; break; case ACPI_VIDEO_NOTIFY_CYCLE: /* Cycle Display output hotkey pressed. */ - acpi_video_switch_output(video, event); acpi_bus_generate_proc_event(device, event, 0); keycode = KEY_SWITCHVIDEOMODE; break; case ACPI_VIDEO_NOTIFY_NEXT_OUTPUT: /* Next Display output hotkey pressed. */ - acpi_video_switch_output(video, event); acpi_bus_generate_proc_event(device, event, 0); keycode = KEY_VIDEO_NEXT; break; case ACPI_VIDEO_NOTIFY_PREV_OUTPUT: /* previous Display output hotkey pressed. */ - acpi_video_switch_output(video, event); acpi_bus_generate_proc_event(device, event, 0); keycode = KEY_VIDEO_PREV; break; @@ -1825,6 +1907,7 @@ static void acpi_video_bus_notify(acpi_handle handle, u32 event, void *data) break; } + acpi_notifier_call_chain(device, event, 0); input_report_key(input, keycode, 1); input_sync(input); input_report_key(input, keycode, 0); @@ -1850,27 +1933,32 @@ static void acpi_video_device_notify(acpi_handle handle, u32 event, void *data) switch (event) { case ACPI_VIDEO_NOTIFY_CYCLE_BRIGHTNESS: /* Cycle brightness */ - acpi_video_switch_brightness(video_device, event); + if (brightness_switch_enabled) + acpi_video_switch_brightness(video_device, event); acpi_bus_generate_proc_event(device, event, 0); keycode = KEY_BRIGHTNESS_CYCLE; break; case ACPI_VIDEO_NOTIFY_INC_BRIGHTNESS: /* Increase brightness */ - acpi_video_switch_brightness(video_device, event); + if (brightness_switch_enabled) + acpi_video_switch_brightness(video_device, event); acpi_bus_generate_proc_event(device, event, 0); keycode = KEY_BRIGHTNESSUP; break; case ACPI_VIDEO_NOTIFY_DEC_BRIGHTNESS: /* Decrease brightness */ - acpi_video_switch_brightness(video_device, event); + if (brightness_switch_enabled) + acpi_video_switch_brightness(video_device, event); acpi_bus_generate_proc_event(device, event, 0); keycode = KEY_BRIGHTNESSDOWN; break; case ACPI_VIDEO_NOTIFY_ZERO_BRIGHTNESS: /* zero brightnesss */ - acpi_video_switch_brightness(video_device, event); + if (brightness_switch_enabled) + acpi_video_switch_brightness(video_device, event); acpi_bus_generate_proc_event(device, event, 0); keycode = KEY_BRIGHTNESS_ZERO; break; case ACPI_VIDEO_NOTIFY_DISPLAY_OFF: /* display device off */ - acpi_video_switch_brightness(video_device, event); + if (brightness_switch_enabled) + acpi_video_switch_brightness(video_device, event); acpi_bus_generate_proc_event(device, event, 0); keycode = KEY_DISPLAY_OFF; break; @@ -1881,6 +1969,7 @@ static void acpi_video_device_notify(acpi_handle handle, u32 event, void *data) break; } + acpi_notifier_call_chain(device, event, 0); input_report_key(input, keycode, 1); input_sync(input); input_report_key(input, keycode, 0); @@ -1890,6 +1979,25 @@ static void acpi_video_device_notify(acpi_handle handle, u32 event, void *data) } static int instance; +static int acpi_video_resume(struct acpi_device *device) +{ + struct acpi_video_bus *video; + struct acpi_video_device *video_device; + int i; + + if (!device || !acpi_driver_data(device)) + return -EINVAL; + + video = acpi_driver_data(device); + + for (i = 0; i < video->attached_count; i++) { + video_device = video->attached_array[i].bind_info; + if (video_device && video_device->backlight) + acpi_video_set_brightness(video_device->backlight); + } + return AE_OK; +} + static int acpi_video_bus_add(struct acpi_device *device) { acpi_status status; diff --git a/drivers/acpi/wmi.c b/drivers/acpi/wmi.c new file mode 100644 index 00000000000..36b84ab418d --- /dev/null +++ b/drivers/acpi/wmi.c @@ -0,0 +1,710 @@ +/* + * ACPI-WMI mapping driver + * + * Copyright (C) 2007-2008 Carlos Corbacho <carlos@strangeworlds.co.uk> + * + * GUID parsing code from ldm.c is: + * Copyright (C) 2001,2002 Richard Russon <ldm@flatcap.org> + * Copyright (c) 2001-2007 Anton Altaparmakov + * Copyright (C) 2001,2002 Jakob Kemi <jakob.kemi@telia.com> + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/types.h> +#include <linux/list.h> +#include <linux/acpi.h> +#include <acpi/acpi_bus.h> +#include <acpi/acpi_drivers.h> + +ACPI_MODULE_NAME("wmi"); +MODULE_AUTHOR("Carlos Corbacho"); +MODULE_DESCRIPTION("ACPI-WMI Mapping Driver"); +MODULE_LICENSE("GPL"); + +#define ACPI_WMI_CLASS "wmi" + +#undef PREFIX +#define PREFIX "ACPI: WMI: " + +static DEFINE_MUTEX(wmi_data_lock); + +struct guid_block { + char guid[16]; + union { + char object_id[2]; + struct { + unsigned char notify_id; + unsigned char reserved; + }; + }; + u8 instance_count; + u8 flags; +}; + +struct wmi_block { + struct list_head list; + struct guid_block gblock; + acpi_handle handle; + wmi_notify_handler handler; + void *handler_data; +}; + +static struct wmi_block wmi_blocks; + +/* + * If the GUID data block is marked as expensive, we must enable and + * explicitily disable data collection. + */ +#define ACPI_WMI_EXPENSIVE 0x1 +#define ACPI_WMI_METHOD 0x2 /* GUID is a method */ +#define ACPI_WMI_STRING 0x4 /* GUID takes & returns a string */ +#define ACPI_WMI_EVENT 0x8 /* GUID is an event */ + +static int acpi_wmi_remove(struct acpi_device *device, int type); +static int acpi_wmi_add(struct acpi_device *device); + +static const struct acpi_device_id wmi_device_ids[] = { + {"PNP0C14", 0}, + {"pnp0c14", 0}, + {"", 0}, +}; +MODULE_DEVICE_TABLE(acpi, wmi_device_ids); + +static struct acpi_driver acpi_wmi_driver = { + .name = "wmi", + .class = ACPI_WMI_CLASS, + .ids = wmi_device_ids, + .ops = { + .add = acpi_wmi_add, + .remove = acpi_wmi_remove, + }, +}; + +/* + * GUID parsing functions + */ + +/** + * wmi_parse_hexbyte - Convert a ASCII hex number to a byte + * @src: Pointer to at least 2 characters to convert. + * + * Convert a two character ASCII hex string to a number. + * + * Return: 0-255 Success, the byte was parsed correctly + * -1 Error, an invalid character was supplied + */ +static int wmi_parse_hexbyte(const u8 *src) +{ + unsigned int x; /* For correct wrapping */ + int h; + + /* high part */ + x = src[0]; + if (x - '0' <= '9' - '0') { + h = x - '0'; + } else if (x - 'a' <= 'f' - 'a') { + h = x - 'a' + 10; + } else if (x - 'A' <= 'F' - 'A') { + h = x - 'A' + 10; + } else { + return -1; + } + h <<= 4; + + /* low part */ + x = src[1]; + if (x - '0' <= '9' - '0') + return h | (x - '0'); + if (x - 'a' <= 'f' - 'a') + return h | (x - 'a' + 10); + if (x - 'A' <= 'F' - 'A') + return h | (x - 'A' + 10); + return -1; +} + +/** + * wmi_swap_bytes - Rearrange GUID bytes to match GUID binary + * @src: Memory block holding binary GUID (16 bytes) + * @dest: Memory block to hold byte swapped binary GUID (16 bytes) + * + * Byte swap a binary GUID to match it's real GUID value + */ +static void wmi_swap_bytes(u8 *src, u8 *dest) +{ + int i; + + for (i = 0; i <= 3; i++) + memcpy(dest + i, src + (3 - i), 1); + + for (i = 0; i <= 1; i++) + memcpy(dest + 4 + i, src + (5 - i), 1); + + for (i = 0; i <= 1; i++) + memcpy(dest + 6 + i, src + (7 - i), 1); + + memcpy(dest + 8, src + 8, 8); +} + +/** + * wmi_parse_guid - Convert GUID from ASCII to binary + * @src: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba + * @dest: Memory block to hold binary GUID (16 bytes) + * + * N.B. The GUID need not be NULL terminated. + * + * Return: 'true' @dest contains binary GUID + * 'false' @dest contents are undefined + */ +static bool wmi_parse_guid(const u8 *src, u8 *dest) +{ + static const int size[] = { 4, 2, 2, 2, 6 }; + int i, j, v; + + if (src[8] != '-' || src[13] != '-' || + src[18] != '-' || src[23] != '-') + return false; + + for (j = 0; j < 5; j++, src++) { + for (i = 0; i < size[j]; i++, src += 2, *dest++ = v) { + v = wmi_parse_hexbyte(src); + if (v < 0) + return false; + } + } + + return true; +} + +static bool find_guid(const char *guid_string, struct wmi_block **out) +{ + char tmp[16], guid_input[16]; + struct wmi_block *wblock; + struct guid_block *block; + struct list_head *p; + + wmi_parse_guid(guid_string, tmp); + wmi_swap_bytes(tmp, guid_input); + + list_for_each(p, &wmi_blocks.list) { + wblock = list_entry(p, struct wmi_block, list); + block = &wblock->gblock; + + if (memcmp(block->guid, guid_input, 16) == 0) { + if (out) + *out = wblock; + return 1; + } + } + return 0; +} + +/* + * Exported WMI functions + */ +/** + * wmi_evaluate_method - Evaluate a WMI method + * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba + * @instance: Instance index + * @method_id: Method ID to call + * &in: Buffer containing input for the method call + * &out: Empty buffer to return the method results + * + * Call an ACPI-WMI method + */ +acpi_status wmi_evaluate_method(const char *guid_string, u8 instance, +u32 method_id, const struct acpi_buffer *in, struct acpi_buffer *out) +{ + struct guid_block *block = NULL; + struct wmi_block *wblock = NULL; + acpi_handle handle; + acpi_status status; + struct acpi_object_list input; + union acpi_object params[3]; + char method[4] = "WM"; + + if (!find_guid(guid_string, &wblock)) + return AE_BAD_ADDRESS; + + block = &wblock->gblock; + handle = wblock->handle; + + if (!block->flags & ACPI_WMI_METHOD) + return AE_BAD_DATA; + + if (block->instance_count < instance) + return AE_BAD_PARAMETER; + + input.count = 2; + input.pointer = params; + params[0].type = ACPI_TYPE_INTEGER; + params[0].integer.value = instance; + params[1].type = ACPI_TYPE_INTEGER; + params[1].integer.value = method_id; + + if (in) { + input.count = 3; + + if (block->flags & ACPI_WMI_STRING) { + params[2].type = ACPI_TYPE_STRING; + } else { + params[2].type = ACPI_TYPE_BUFFER; + } + params[2].buffer.length = in->length; + params[2].buffer.pointer = in->pointer; + } + + strncat(method, block->object_id, 2); + + status = acpi_evaluate_object(handle, method, &input, out); + + return status; +} +EXPORT_SYMBOL_GPL(wmi_evaluate_method); + +/** + * wmi_query_block - Return contents of a WMI block + * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba + * @instance: Instance index + * &out: Empty buffer to return the contents of the data block to + * + * Return the contents of an ACPI-WMI data block to a buffer + */ +acpi_status wmi_query_block(const char *guid_string, u8 instance, +struct acpi_buffer *out) +{ + struct guid_block *block = NULL; + struct wmi_block *wblock = NULL; + acpi_handle handle; + acpi_status status, wc_status = AE_ERROR; + struct acpi_object_list input, wc_input; + union acpi_object wc_params[1], wq_params[1]; + char method[4]; + char wc_method[4] = "WC"; + + if (!guid_string || !out) + return AE_BAD_PARAMETER; + + if (!find_guid(guid_string, &wblock)) + return AE_BAD_ADDRESS; + + block = &wblock->gblock; + handle = wblock->handle; + + if (block->instance_count < instance) + return AE_BAD_PARAMETER; + + /* Check GUID is a data block */ + if (block->flags & (ACPI_WMI_EVENT | ACPI_WMI_METHOD)) + return AE_BAD_ADDRESS; + + input.count = 1; + input.pointer = wq_params; + wq_params[0].type = ACPI_TYPE_INTEGER; + wq_params[0].integer.value = instance; + + /* + * If ACPI_WMI_EXPENSIVE, call the relevant WCxx method first to + * enable collection. + */ + if (block->flags & ACPI_WMI_EXPENSIVE) { + wc_input.count = 1; + wc_input.pointer = wc_params; + wc_params[0].type = ACPI_TYPE_INTEGER; + wc_params[0].integer.value = 1; + + strncat(wc_method, block->object_id, 2); + + /* + * Some GUIDs break the specification by declaring themselves + * expensive, but have no corresponding WCxx method. So we + * should not fail if this happens. + */ + wc_status = acpi_evaluate_object(handle, wc_method, + &wc_input, NULL); + } + + strcpy(method, "WQ"); + strncat(method, block->object_id, 2); + + status = acpi_evaluate_object(handle, method, NULL, out); + + /* + * If ACPI_WMI_EXPENSIVE, call the relevant WCxx method, even if + * the WQxx method failed - we should disable collection anyway. + */ + if ((block->flags & ACPI_WMI_EXPENSIVE) && wc_status) { + wc_params[0].integer.value = 0; + status = acpi_evaluate_object(handle, + wc_method, &wc_input, NULL); + } + + return status; +} +EXPORT_SYMBOL_GPL(wmi_query_block); + +/** + * wmi_set_block - Write to a WMI block + * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba + * @instance: Instance index + * &in: Buffer containing new values for the data block + * + * Write the contents of the input buffer to an ACPI-WMI data block + */ +acpi_status wmi_set_block(const char *guid_string, u8 instance, +const struct acpi_buffer *in) +{ + struct guid_block *block = NULL; + struct wmi_block *wblock = NULL; + acpi_handle handle; + struct acpi_object_list input; + union acpi_object params[2]; + char method[4] = "WS"; + + if (!guid_string || !in) + return AE_BAD_DATA; + + if (!find_guid(guid_string, &wblock)) + return AE_BAD_ADDRESS; + + block = &wblock->gblock; + handle = wblock->handle; + + if (block->instance_count < instance) + return AE_BAD_PARAMETER; + + /* Check GUID is a data block */ + if (block->flags & (ACPI_WMI_EVENT | ACPI_WMI_METHOD)) + return AE_BAD_ADDRESS; + + input.count = 2; + input.pointer = params; + params[0].type = ACPI_TYPE_INTEGER; + params[0].integer.value = instance; + + if (block->flags & ACPI_WMI_STRING) { + params[1].type = ACPI_TYPE_STRING; + } else { + params[1].type = ACPI_TYPE_BUFFER; + } + params[1].buffer.length = in->length; + params[1].buffer.pointer = in->pointer; + + strncat(method, block->object_id, 2); + + return acpi_evaluate_object(handle, method, &input, NULL); +} +EXPORT_SYMBOL_GPL(wmi_set_block); + +/** + * wmi_install_notify_handler - Register handler for WMI events + * @handler: Function to handle notifications + * @data: Data to be returned to handler when event is fired + * + * Register a handler for events sent to the ACPI-WMI mapper device. + */ +acpi_status wmi_install_notify_handler(const char *guid, +wmi_notify_handler handler, void *data) +{ + struct wmi_block *block; + + if (!guid || !handler) + return AE_BAD_PARAMETER; + + find_guid(guid, &block); + if (!block) + return AE_NOT_EXIST; + + if (block->handler) + return AE_ALREADY_ACQUIRED; + + block->handler = handler; + block->handler_data = data; + + return AE_OK; +} +EXPORT_SYMBOL_GPL(wmi_install_notify_handler); + +/** + * wmi_uninstall_notify_handler - Unregister handler for WMI events + * + * Unregister handler for events sent to the ACPI-WMI mapper device. + */ +acpi_status wmi_remove_notify_handler(const char *guid) +{ + struct wmi_block *block; + + if (!guid) + return AE_BAD_PARAMETER; + + find_guid(guid, &block); + if (!block) + return AE_NOT_EXIST; + + if (!block->handler) + return AE_NULL_ENTRY; + + block->handler = NULL; + block->handler_data = NULL; + + return AE_OK; +} +EXPORT_SYMBOL_GPL(wmi_remove_notify_handler); + +/** + * wmi_get_event_data - Get WMI data associated with an event + * + * @event - Event to find + * &out - Buffer to hold event data + * + * Returns extra data associated with an event in WMI. + */ +acpi_status wmi_get_event_data(u32 event, struct acpi_buffer *out) +{ + struct acpi_object_list input; + union acpi_object params[1]; + struct guid_block *gblock; + struct wmi_block *wblock; + struct list_head *p; + + input.count = 1; + input.pointer = params; + params[0].type = ACPI_TYPE_INTEGER; + params[0].integer.value = event; + + list_for_each(p, &wmi_blocks.list) { + wblock = list_entry(p, struct wmi_block, list); + gblock = &wblock->gblock; + + if ((gblock->flags & ACPI_WMI_EVENT) && + (gblock->notify_id == event)) + return acpi_evaluate_object(wblock->handle, "_WED", + &input, out); + } + + return AE_NOT_FOUND; +} +EXPORT_SYMBOL_GPL(wmi_get_event_data); + +/** + * wmi_has_guid - Check if a GUID is available + * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba + * + * Check if a given GUID is defined by _WDG + */ +bool wmi_has_guid(const char *guid_string) +{ + return find_guid(guid_string, NULL); +} +EXPORT_SYMBOL_GPL(wmi_has_guid); + +/* + * Parse the _WDG method for the GUID data blocks + */ +static __init acpi_status parse_wdg(acpi_handle handle) +{ + struct acpi_buffer out = {ACPI_ALLOCATE_BUFFER, NULL}; + union acpi_object *obj; + struct guid_block *gblock; + struct wmi_block *wblock; + acpi_status status; + u32 i, total; + + status = acpi_evaluate_object(handle, "_WDG", NULL, &out); + + if (ACPI_FAILURE(status)) + return status; + + obj = (union acpi_object *) out.pointer; + + if (obj->type != ACPI_TYPE_BUFFER) + return AE_ERROR; + + total = obj->buffer.length / sizeof(struct guid_block); + + gblock = kzalloc(obj->buffer.length, GFP_KERNEL); + if (!gblock) + return AE_NO_MEMORY; + + memcpy(gblock, obj->buffer.pointer, obj->buffer.length); + + for (i = 0; i < total; i++) { + wblock = kzalloc(sizeof(struct wmi_block), GFP_KERNEL); + if (!wblock) + return AE_NO_MEMORY; + + wblock->gblock = gblock[i]; + wblock->handle = handle; + list_add_tail(&wblock->list, &wmi_blocks.list); + } + + kfree(out.pointer); + kfree(gblock); + + return status; +} + +/* + * WMI can have EmbeddedControl access regions. In which case, we just want to + * hand these off to the EC driver. + */ +static acpi_status +acpi_wmi_ec_space_handler(u32 function, acpi_physical_address address, + u32 bits, acpi_integer * value, + void *handler_context, void *region_context) +{ + int result = 0, i = 0; + u8 temp = 0; + + if ((address > 0xFF) || !value) + return AE_BAD_PARAMETER; + + if (function != ACPI_READ && function != ACPI_WRITE) + return AE_BAD_PARAMETER; + + if (bits != 8) + return AE_BAD_PARAMETER; + + if (function == ACPI_READ) { + result = ec_read(address, &temp); + (*value) |= ((acpi_integer)temp) << i; + } else { + temp = 0xff & ((*value) >> i); + result = ec_write(address, temp); + } + + switch (result) { + case -EINVAL: + return AE_BAD_PARAMETER; + break; + case -ENODEV: + return AE_NOT_FOUND; + break; + case -ETIME: + return AE_TIME; + break; + default: + return AE_OK; + } +} + +static void acpi_wmi_notify(acpi_handle handle, u32 event, void *data) +{ + struct guid_block *block; + struct wmi_block *wblock; + struct list_head *p; + struct acpi_device *device = data; + + list_for_each(p, &wmi_blocks.list) { + wblock = list_entry(p, struct wmi_block, list); + block = &wblock->gblock; + + if ((block->flags & ACPI_WMI_EVENT) && + (block->notify_id == event)) { + if (wblock->handler) + wblock->handler(event, wblock->handler_data); + + acpi_bus_generate_netlink_event( + device->pnp.device_class, device->dev.bus_id, + event, 0); + break; + } + } +} + +static int acpi_wmi_remove(struct acpi_device *device, int type) +{ + acpi_remove_notify_handler(device->handle, ACPI_DEVICE_NOTIFY, + acpi_wmi_notify); + + acpi_remove_address_space_handler(device->handle, + ACPI_ADR_SPACE_EC, &acpi_wmi_ec_space_handler); + + return 0; +} + +static int __init acpi_wmi_add(struct acpi_device *device) +{ + acpi_status status; + int result = 0; + + status = acpi_install_notify_handler(device->handle, ACPI_DEVICE_NOTIFY, + acpi_wmi_notify, device); + if (ACPI_FAILURE(status)) { + printk(KERN_ERR PREFIX "Error installing notify handler\n"); + return -ENODEV; + } + + status = acpi_install_address_space_handler(device->handle, + ACPI_ADR_SPACE_EC, + &acpi_wmi_ec_space_handler, + NULL, NULL); + if (ACPI_FAILURE(status)) + return -ENODEV; + + status = parse_wdg(device->handle); + if (ACPI_FAILURE(status)) { + printk(KERN_ERR PREFIX "Error installing EC region handler\n"); + return -ENODEV; + } + + return result; +} + +static int __init acpi_wmi_init(void) +{ + acpi_status result; + + if (acpi_disabled) + return -ENODEV; + + INIT_LIST_HEAD(&wmi_blocks.list); + + result = acpi_bus_register_driver(&acpi_wmi_driver); + + if (result < 0) { + printk(KERN_INFO PREFIX "Error loading mapper\n"); + } else { + printk(KERN_INFO PREFIX "Mapper loaded\n"); + } + + return result; +} + +static void __exit acpi_wmi_exit(void) +{ + struct list_head *p, *tmp; + struct wmi_block *wblock; + + acpi_bus_unregister_driver(&acpi_wmi_driver); + + list_for_each_safe(p, tmp, &wmi_blocks.list) { + wblock = list_entry(p, struct wmi_block, list); + + list_del(p); + kfree(wblock); + } + + printk(KERN_INFO PREFIX "Mapper unloaded\n"); +} + +subsys_initcall(acpi_wmi_init); +module_exit(acpi_wmi_exit); |