From f59c55d04b43bd72df8efa692dd07224fe94d1ac Mon Sep 17 00:00:00 2001 From: Huang Ying Date: Tue, 7 Dec 2010 10:22:30 +0800 Subject: ACPI, APEI, Add APEI generic error status printing support In APEI, Hardware error information reported by firmware to Linux kernel is in the data structure of APEI generic error status (struct acpi_hes_generic_status). While now printk is used by Linux kernel to report hardware error information to user space. So, this patch adds printing support for the data structure, so that the corresponding hardware error information can be reported to user space via printk. PCIe AER information printing is not implemented yet. Will refactor the original PCIe AER information printing code to avoid code duplicating. The output format is as follow: := APEI generic hardware error status severity: , section: , severity: , flags:
fru_id: fru_text: section_type:
* := recoverable | fatal | corrected | info
# := [primary][, containment warning][, reset][, threshold exceeded]\ [, resource not accessible][, latent error]
:= generic processor error | memory error | \ PCIe error | unknown,
:= | | \ | := [processor_type: , ] [processor_isa: , ] [error_type: ] [operation: , ] [flags: ] [level: ] [version_info: ] [processor_id: ] [target_address: ] [requestor_id: ] [responder_id: ] [IP: ] * := IA32/X64 | IA64 * := IA32 | IA64 | X64 # := [cache error][, TLB error][, bus error][, micro-architectural error] * := unknown or generic | data read | data write | \ instruction execution # := [restartable][, precise IP][, overflow][, corrected] := [error_status: ] [physical_address: ] [physical_address_mask: ] [node: ] [card: ] [module: ] [bank: ] [device: ] [row: ] [column: ] [bit_position: ] [requestor_id: ] [responder_id: ] [target_id: ] [error_type: , ] * := unknown | no error | single-bit ECC | multi-bit ECC | \ single-symbol chipkill ECC | multi-symbol chipkill ECC | master abort | \ target abort | parity error | watchdog timeout | invalid address | \ mirror Broken | memory sparing | scrub corrected error | \ scrub uncorrected error := [port_type: , ] [version: .] [command: , status: ] [device_id: ::. slot: secondary_bus: vendor_id: , device_id: class_code: ] [serial number: , ] [bridge: secondary_status: , control: ] * := PCIe end point | legacy PCI end point | \ unknown | unknown | root port | upstream switch port | \ downstream switch port | PCIe to PCI/PCI-X bridge | \ PCI/PCI-X to PCIe bridge | root complex integrated endpoint device | \ root complex event collector Where, [] designate corresponding content is optional All description with * has the following format: field: , Where value of should be the position of "string" in description. Otherwise, will be "unknown". All description with # has the following format: field: Where each string in corresponding to one set bit of . The bit position is the position of "string" in description. For more detailed explanation of every field, please refer to UEFI specification version 2.3 or later, section Appendix N: Common Platform Error Record. Signed-off-by: Huang Ying Signed-off-by: Len Brown --- drivers/acpi/apei/apei-internal.h | 2 + drivers/acpi/apei/cper.c | 311 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 313 insertions(+) (limited to 'drivers/acpi') diff --git a/drivers/acpi/apei/apei-internal.h b/drivers/acpi/apei/apei-internal.h index 18df1e940276..ef0581f2094d 100644 --- a/drivers/acpi/apei/apei-internal.h +++ b/drivers/acpi/apei/apei-internal.h @@ -109,6 +109,8 @@ static inline u32 apei_estatus_len(struct acpi_hest_generic_status *estatus) return sizeof(*estatus) + estatus->data_length; } +void apei_estatus_print(const char *pfx, + const struct acpi_hest_generic_status *estatus); int apei_estatus_check_header(const struct acpi_hest_generic_status *estatus); int apei_estatus_check(const struct acpi_hest_generic_status *estatus); #endif diff --git a/drivers/acpi/apei/cper.c b/drivers/acpi/apei/cper.c index f4cf2fc4c8c1..31464a006d76 100644 --- a/drivers/acpi/apei/cper.c +++ b/drivers/acpi/apei/cper.c @@ -46,6 +46,317 @@ u64 cper_next_record_id(void) } EXPORT_SYMBOL_GPL(cper_next_record_id); +static const char *cper_severity_strs[] = { + "recoverable", + "fatal", + "corrected", + "info", +}; + +static const char *cper_severity_str(unsigned int severity) +{ + return severity < ARRAY_SIZE(cper_severity_strs) ? + cper_severity_strs[severity] : "unknown"; +} + +/* + * cper_print_bits - print strings for set bits + * @pfx: prefix for each line, including log level and prefix string + * @bits: bit mask + * @strs: string array, indexed by bit position + * @strs_size: size of the string array: @strs + * + * For each set bit in @bits, print the corresponding string in @strs. + * If the output length is longer than 80, multiple line will be + * printed, with @pfx is printed at the beginning of each line. + */ +static void cper_print_bits(const char *pfx, unsigned int bits, + const char *strs[], unsigned int strs_size) +{ + int i, len = 0; + const char *str; + char buf[84]; + + for (i = 0; i < strs_size; i++) { + if (!(bits & (1U << i))) + continue; + str = strs[i]; + if (len && len + strlen(str) + 2 > 80) { + printk("%s\n", buf); + len = 0; + } + if (!len) + len = snprintf(buf, sizeof(buf), "%s%s", pfx, str); + else + len += snprintf(buf+len, sizeof(buf)-len, ", %s", str); + } + if (len) + printk("%s\n", buf); +} + +static const char *cper_proc_type_strs[] = { + "IA32/X64", + "IA64", +}; + +static const char *cper_proc_isa_strs[] = { + "IA32", + "IA64", + "X64", +}; + +static const char *cper_proc_error_type_strs[] = { + "cache error", + "TLB error", + "bus error", + "micro-architectural error", +}; + +static const char *cper_proc_op_strs[] = { + "unknown or generic", + "data read", + "data write", + "instruction execution", +}; + +static const char *cper_proc_flag_strs[] = { + "restartable", + "precise IP", + "overflow", + "corrected", +}; + +static void cper_print_proc_generic(const char *pfx, + const struct cper_sec_proc_generic *proc) +{ + if (proc->validation_bits & CPER_PROC_VALID_TYPE) + printk("%s""processor_type: %d, %s\n", pfx, proc->proc_type, + proc->proc_type < ARRAY_SIZE(cper_proc_type_strs) ? + cper_proc_type_strs[proc->proc_type] : "unknown"); + if (proc->validation_bits & CPER_PROC_VALID_ISA) + printk("%s""processor_isa: %d, %s\n", pfx, proc->proc_isa, + proc->proc_isa < ARRAY_SIZE(cper_proc_isa_strs) ? + cper_proc_isa_strs[proc->proc_isa] : "unknown"); + if (proc->validation_bits & CPER_PROC_VALID_ERROR_TYPE) { + printk("%s""error_type: 0x%02x\n", pfx, proc->proc_error_type); + cper_print_bits(pfx, proc->proc_error_type, + cper_proc_error_type_strs, + ARRAY_SIZE(cper_proc_error_type_strs)); + } + if (proc->validation_bits & CPER_PROC_VALID_OPERATION) + printk("%s""operation: %d, %s\n", pfx, proc->operation, + proc->operation < ARRAY_SIZE(cper_proc_op_strs) ? + cper_proc_op_strs[proc->operation] : "unknown"); + if (proc->validation_bits & CPER_PROC_VALID_FLAGS) { + printk("%s""flags: 0x%02x\n", pfx, proc->flags); + cper_print_bits(pfx, proc->flags, cper_proc_flag_strs, + ARRAY_SIZE(cper_proc_flag_strs)); + } + if (proc->validation_bits & CPER_PROC_VALID_LEVEL) + printk("%s""level: %d\n", pfx, proc->level); + if (proc->validation_bits & CPER_PROC_VALID_VERSION) + printk("%s""version_info: 0x%016llx\n", pfx, proc->cpu_version); + if (proc->validation_bits & CPER_PROC_VALID_ID) + printk("%s""processor_id: 0x%016llx\n", pfx, proc->proc_id); + if (proc->validation_bits & CPER_PROC_VALID_TARGET_ADDRESS) + printk("%s""target_address: 0x%016llx\n", + pfx, proc->target_addr); + if (proc->validation_bits & CPER_PROC_VALID_REQUESTOR_ID) + printk("%s""requestor_id: 0x%016llx\n", + pfx, proc->requestor_id); + if (proc->validation_bits & CPER_PROC_VALID_RESPONDER_ID) + printk("%s""responder_id: 0x%016llx\n", + pfx, proc->responder_id); + if (proc->validation_bits & CPER_PROC_VALID_IP) + printk("%s""IP: 0x%016llx\n", pfx, proc->ip); +} + +static const char *cper_mem_err_type_strs[] = { + "unknown", + "no error", + "single-bit ECC", + "multi-bit ECC", + "single-symbol chipkill ECC", + "multi-symbol chipkill ECC", + "master abort", + "target abort", + "parity error", + "watchdog timeout", + "invalid address", + "mirror Broken", + "memory sparing", + "scrub corrected error", + "scrub uncorrected error", +}; + +static void cper_print_mem(const char *pfx, const struct cper_sec_mem_err *mem) +{ + if (mem->validation_bits & CPER_MEM_VALID_ERROR_STATUS) + printk("%s""error_status: 0x%016llx\n", pfx, mem->error_status); + if (mem->validation_bits & CPER_MEM_VALID_PHYSICAL_ADDRESS) + printk("%s""physical_address: 0x%016llx\n", + pfx, mem->physical_addr); + if (mem->validation_bits & CPER_MEM_VALID_PHYSICAL_ADDRESS_MASK) + printk("%s""physical_address_mask: 0x%016llx\n", + pfx, mem->physical_addr_mask); + if (mem->validation_bits & CPER_MEM_VALID_NODE) + printk("%s""node: %d\n", pfx, mem->node); + if (mem->validation_bits & CPER_MEM_VALID_CARD) + printk("%s""card: %d\n", pfx, mem->card); + if (mem->validation_bits & CPER_MEM_VALID_MODULE) + printk("%s""module: %d\n", pfx, mem->module); + if (mem->validation_bits & CPER_MEM_VALID_BANK) + printk("%s""bank: %d\n", pfx, mem->bank); + if (mem->validation_bits & CPER_MEM_VALID_DEVICE) + printk("%s""device: %d\n", pfx, mem->device); + if (mem->validation_bits & CPER_MEM_VALID_ROW) + printk("%s""row: %d\n", pfx, mem->row); + if (mem->validation_bits & CPER_MEM_VALID_COLUMN) + printk("%s""column: %d\n", pfx, mem->column); + if (mem->validation_bits & CPER_MEM_VALID_BIT_POSITION) + printk("%s""bit_position: %d\n", pfx, mem->bit_pos); + if (mem->validation_bits & CPER_MEM_VALID_REQUESTOR_ID) + printk("%s""requestor_id: 0x%016llx\n", pfx, mem->requestor_id); + if (mem->validation_bits & CPER_MEM_VALID_RESPONDER_ID) + printk("%s""responder_id: 0x%016llx\n", pfx, mem->responder_id); + if (mem->validation_bits & CPER_MEM_VALID_TARGET_ID) + printk("%s""target_id: 0x%016llx\n", pfx, mem->target_id); + if (mem->validation_bits & CPER_MEM_VALID_ERROR_TYPE) { + u8 etype = mem->error_type; + printk("%s""error_type: %d, %s\n", pfx, etype, + etype < ARRAY_SIZE(cper_mem_err_type_strs) ? + cper_mem_err_type_strs[etype] : "unknown"); + } +} + +static const char *cper_pcie_port_type_strs[] = { + "PCIe end point", + "legacy PCI end point", + "unknown", + "unknown", + "root port", + "upstream switch port", + "downstream switch port", + "PCIe to PCI/PCI-X bridge", + "PCI/PCI-X to PCIe bridge", + "root complex integrated endpoint device", + "root complex event collector", +}; + +static void cper_print_pcie(const char *pfx, const struct cper_sec_pcie *pcie) +{ + if (pcie->validation_bits & CPER_PCIE_VALID_PORT_TYPE) + printk("%s""port_type: %d, %s\n", pfx, pcie->port_type, + pcie->port_type < ARRAY_SIZE(cper_pcie_port_type_strs) ? + cper_pcie_port_type_strs[pcie->port_type] : "unknown"); + if (pcie->validation_bits & CPER_PCIE_VALID_VERSION) + printk("%s""version: %d.%d\n", pfx, + pcie->version.major, pcie->version.minor); + if (pcie->validation_bits & CPER_PCIE_VALID_COMMAND_STATUS) + printk("%s""command: 0x%04x, status: 0x%04x\n", pfx, + pcie->command, pcie->status); + if (pcie->validation_bits & CPER_PCIE_VALID_DEVICE_ID) { + const __u8 *p; + printk("%s""device_id: %04x:%02x:%02x.%x\n", pfx, + pcie->device_id.segment, pcie->device_id.bus, + pcie->device_id.device, pcie->device_id.function); + printk("%s""slot: %d\n", pfx, + pcie->device_id.slot >> CPER_PCIE_SLOT_SHIFT); + printk("%s""secondary_bus: 0x%02x\n", pfx, + pcie->device_id.secondary_bus); + printk("%s""vendor_id: 0x%04x, device_id: 0x%04x\n", pfx, + pcie->device_id.vendor_id, pcie->device_id.device_id); + p = pcie->device_id.class_code; + printk("%s""class_code: %02x%02x%02x\n", pfx, p[0], p[1], p[2]); + } + if (pcie->validation_bits & CPER_PCIE_VALID_SERIAL_NUMBER) + printk("%s""serial number: 0x%04x, 0x%04x\n", pfx, + pcie->serial_number.lower, pcie->serial_number.upper); + if (pcie->validation_bits & CPER_PCIE_VALID_BRIDGE_CONTROL_STATUS) + printk( + "%s""bridge: secondary_status: 0x%04x, control: 0x%04x\n", + pfx, pcie->bridge.secondary_status, pcie->bridge.control); +} + +static const char *apei_estatus_section_flag_strs[] = { + "primary", + "containment warning", + "reset", + "threshold exceeded", + "resource not accessible", + "latent error", +}; + +static void apei_estatus_print_section( + const char *pfx, const struct acpi_hest_generic_data *gdata, int sec_no) +{ + uuid_le *sec_type = (uuid_le *)gdata->section_type; + __u16 severity; + + severity = gdata->error_severity; + printk("%s""section: %d, severity: %d, %s\n", pfx, sec_no, severity, + cper_severity_str(severity)); + printk("%s""flags: 0x%02x\n", pfx, gdata->flags); + cper_print_bits(pfx, gdata->flags, apei_estatus_section_flag_strs, + ARRAY_SIZE(apei_estatus_section_flag_strs)); + if (gdata->validation_bits & CPER_SEC_VALID_FRU_ID) + printk("%s""fru_id: %pUl\n", pfx, (uuid_le *)gdata->fru_id); + if (gdata->validation_bits & CPER_SEC_VALID_FRU_TEXT) + printk("%s""fru_text: %.20s\n", pfx, gdata->fru_text); + + if (!uuid_le_cmp(*sec_type, CPER_SEC_PROC_GENERIC)) { + struct cper_sec_proc_generic *proc_err = (void *)(gdata + 1); + printk("%s""section_type: general processor error\n", pfx); + if (gdata->error_data_length >= sizeof(*proc_err)) + cper_print_proc_generic(pfx, proc_err); + else + goto err_section_too_small; + } else if (!uuid_le_cmp(*sec_type, CPER_SEC_PLATFORM_MEM)) { + struct cper_sec_mem_err *mem_err = (void *)(gdata + 1); + printk("%s""section_type: memory error\n", pfx); + if (gdata->error_data_length >= sizeof(*mem_err)) + cper_print_mem(pfx, mem_err); + else + goto err_section_too_small; + } else if (!uuid_le_cmp(*sec_type, CPER_SEC_PCIE)) { + struct cper_sec_pcie *pcie = (void *)(gdata + 1); + printk("%s""section_type: PCIe error\n", pfx); + if (gdata->error_data_length >= sizeof(*pcie)) + cper_print_pcie(pfx, pcie); + else + goto err_section_too_small; + } else + printk("%s""section type: unknown, %pUl\n", pfx, sec_type); + + return; + +err_section_too_small: + pr_err(FW_WARN "error section length is too small\n"); +} + +void apei_estatus_print(const char *pfx, + const struct acpi_hest_generic_status *estatus) +{ + struct acpi_hest_generic_data *gdata; + unsigned int data_len, gedata_len; + int sec_no = 0; + __u16 severity; + + printk("%s""APEI generic hardware error status\n", pfx); + severity = estatus->error_severity; + printk("%s""severity: %d, %s\n", pfx, severity, + cper_severity_str(severity)); + data_len = estatus->data_length; + gdata = (struct acpi_hest_generic_data *)(estatus + 1); + while (data_len > sizeof(*gdata)) { + gedata_len = gdata->error_data_length; + apei_estatus_print_section(pfx, gdata, sec_no); + data_len -= gedata_len + sizeof(*gdata); + sec_no++; + } +} +EXPORT_SYMBOL_GPL(apei_estatus_print); + int apei_estatus_check_header(const struct acpi_hest_generic_status *estatus) { if (estatus->data_length && -- cgit v1.2.3 From 32c361f574f85fa47600d84900598e2efc99082e Mon Sep 17 00:00:00 2001 From: Huang Ying Date: Tue, 7 Dec 2010 10:22:31 +0800 Subject: ACPI, APEI, Report GHES error information via printk printk is one of the methods to report hardware errors to user space. This patch implements hardware error reporting for GHES via printk. Signed-off-by: Huang Ying Signed-off-by: Len Brown --- drivers/acpi/apei/ghes.c | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c index 0d505e59214d..51905d07a4d9 100644 --- a/drivers/acpi/apei/ghes.c +++ b/drivers/acpi/apei/ghes.c @@ -43,6 +43,7 @@ #include #include #include +#include #include #include #include @@ -255,11 +256,26 @@ static void ghes_do_proc(struct ghes *ghes) } #endif } +} - if (!processed && printk_ratelimit()) - pr_warning(GHES_PFX - "Unknown error record from generic hardware error source: %d\n", - ghes->generic->header.source_id); +static void ghes_print_estatus(const char *pfx, struct ghes *ghes) +{ + /* Not more than 2 messages every 5 seconds */ + static DEFINE_RATELIMIT_STATE(ratelimit, 5*HZ, 2); + + if (pfx == NULL) { + if (ghes_severity(ghes->estatus->error_severity) <= + GHES_SEV_CORRECTED) + pfx = KERN_WARNING HW_ERR; + else + pfx = KERN_ERR HW_ERR; + } + if (__ratelimit(&ratelimit)) { + printk( + "%s""Hardware error from APEI Generic Hardware Error Source: %d\n", + pfx, ghes->generic->header.source_id); + apei_estatus_print(pfx, ghes->estatus); + } } static int ghes_proc(struct ghes *ghes) @@ -269,6 +285,7 @@ static int ghes_proc(struct ghes *ghes) rc = ghes_read_estatus(ghes, 0); if (rc) goto out; + ghes_print_estatus(NULL, ghes); ghes_do_proc(ghes); out: -- cgit v1.2.3 From cef6e8a3790d6bdc305496629c357a56001d59a6 Mon Sep 17 00:00:00 2001 From: Zhang Rui Date: Mon, 6 Dec 2010 15:04:15 +0800 Subject: ACPI processor: remove processor throttling control procfs I/F Remove deprecated ACPI process procfs I/F for throttling control. This is because the t-state control should only be done in kernel, when system is in a overheating state. Now users can only change the processor t-state indirectly, by poking the cooling device sysfs I/F of the processor. Signed-off-by: Zhang Rui Signed-off-by: Len Brown --- drivers/acpi/Kconfig | 4 -- drivers/acpi/processor_driver.c | 75 +----------------------- drivers/acpi/processor_throttling.c | 114 ------------------------------------ 3 files changed, 1 insertion(+), 192 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index 3f3489c5ca8c..5959077be0a4 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig @@ -53,10 +53,6 @@ config ACPI_PROCFS they have been replaced by functions in /sys. The deprecated files (and their replacements) include: - /proc/acpi/processor/*/throttling (/sys/class/thermal/ - cooling_device*/*) - /proc/acpi/video/*/brightness (/sys/class/backlight/) - /proc/acpi/thermal_zone/*/* (/sys/class/thermal/) This option has no effect on /proc/acpi/ files and functions which do not yet exist in /sys. diff --git a/drivers/acpi/processor_driver.c b/drivers/acpi/processor_driver.c index 85e48047d7b0..29572debc65d 100644 --- a/drivers/acpi/processor_driver.c +++ b/drivers/acpi/processor_driver.c @@ -40,10 +40,6 @@ #include #include #include -#ifdef CONFIG_ACPI_PROCFS -#include -#include -#endif #include #include #include @@ -246,53 +242,6 @@ static int acpi_processor_errata(struct acpi_processor *pr) return result; } -#ifdef CONFIG_ACPI_PROCFS -static struct proc_dir_entry *acpi_processor_dir = NULL; - -static int __cpuinit acpi_processor_add_fs(struct acpi_device *device) -{ - struct proc_dir_entry *entry = NULL; - - - if (!acpi_device_dir(device)) { - acpi_device_dir(device) = proc_mkdir(acpi_device_bid(device), - acpi_processor_dir); - if (!acpi_device_dir(device)) - return -ENODEV; - } - - /* 'throttling' [R/W] */ - entry = proc_create_data(ACPI_PROCESSOR_FILE_THROTTLING, - S_IFREG | S_IRUGO | S_IWUSR, - acpi_device_dir(device), - &acpi_processor_throttling_fops, - acpi_driver_data(device)); - if (!entry) - return -EIO; - return 0; -} -static int acpi_processor_remove_fs(struct acpi_device *device) -{ - - if (acpi_device_dir(device)) { - remove_proc_entry(ACPI_PROCESSOR_FILE_THROTTLING, - acpi_device_dir(device)); - remove_proc_entry(acpi_device_bid(device), acpi_processor_dir); - acpi_device_dir(device) = NULL; - } - - return 0; -} -#else -static inline int acpi_processor_add_fs(struct acpi_device *device) -{ - return 0; -} -static inline int acpi_processor_remove_fs(struct acpi_device *device) -{ - return 0; -} -#endif /* -------------------------------------------------------------------------- Driver Interface -------------------------------------------------------------------------- */ @@ -537,14 +486,10 @@ static int __cpuinit acpi_processor_add(struct acpi_device *device) per_cpu(processors, pr->id) = pr; - result = acpi_processor_add_fs(device); - if (result) - goto err_free_cpumask; - sysdev = get_cpu_sysdev(pr->id); if (sysfs_create_link(&device->dev.kobj, &sysdev->kobj, "sysdev")) { result = -EFAULT; - goto err_remove_fs; + goto err_free_cpumask; } #ifdef CONFIG_CPU_FREQ @@ -590,8 +535,6 @@ err_thermal_unregister: thermal_cooling_device_unregister(pr->cdev); err_power_exit: acpi_processor_power_exit(pr, device); -err_remove_fs: - acpi_processor_remove_fs(device); err_free_cpumask: free_cpumask_var(pr->throttling.shared_cpu_map); @@ -620,8 +563,6 @@ static int acpi_processor_remove(struct acpi_device *device, int type) sysfs_remove_link(&device->dev.kobj, "sysdev"); - acpi_processor_remove_fs(device); - if (pr->cdev) { sysfs_remove_link(&device->dev.kobj, "thermal_cooling"); sysfs_remove_link(&pr->cdev->device.kobj, "device"); @@ -854,12 +795,6 @@ static int __init acpi_processor_init(void) memset(&errata, 0, sizeof(errata)); -#ifdef CONFIG_ACPI_PROCFS - acpi_processor_dir = proc_mkdir(ACPI_PROCESSOR_CLASS, acpi_root_dir); - if (!acpi_processor_dir) - return -ENOMEM; -#endif - if (!cpuidle_register_driver(&acpi_idle_driver)) { printk(KERN_DEBUG "ACPI: %s registered with cpuidle\n", acpi_idle_driver.name); @@ -885,10 +820,6 @@ static int __init acpi_processor_init(void) out_cpuidle: cpuidle_unregister_driver(&acpi_idle_driver); -#ifdef CONFIG_ACPI_PROCFS - remove_proc_entry(ACPI_PROCESSOR_CLASS, acpi_root_dir); -#endif - return result; } @@ -907,10 +838,6 @@ static void __exit acpi_processor_exit(void) cpuidle_unregister_driver(&acpi_idle_driver); -#ifdef CONFIG_ACPI_PROCFS - remove_proc_entry(ACPI_PROCESSOR_CLASS, acpi_root_dir); -#endif - return; } diff --git a/drivers/acpi/processor_throttling.c b/drivers/acpi/processor_throttling.c index ff3632717c51..4a0eec5dd24a 100644 --- a/drivers/acpi/processor_throttling.c +++ b/drivers/acpi/processor_throttling.c @@ -32,10 +32,6 @@ #include #include #include -#ifdef CONFIG_ACPI_PROCFS -#include -#include -#endif #include #include @@ -1216,113 +1212,3 @@ int acpi_processor_get_throttling_info(struct acpi_processor *pr) return result; } -#ifdef CONFIG_ACPI_PROCFS -/* proc interface */ -static int acpi_processor_throttling_seq_show(struct seq_file *seq, - void *offset) -{ - struct acpi_processor *pr = seq->private; - int i = 0; - int result = 0; - - if (!pr) - goto end; - - if (!(pr->throttling.state_count > 0)) { - seq_puts(seq, "\n"); - goto end; - } - - result = acpi_processor_get_throttling(pr); - - if (result) { - seq_puts(seq, - "Could not determine current throttling state.\n"); - goto end; - } - - seq_printf(seq, "state count: %d\n" - "active state: T%d\n" - "state available: T%d to T%d\n", - pr->throttling.state_count, pr->throttling.state, - pr->throttling_platform_limit, - pr->throttling.state_count - 1); - - seq_puts(seq, "states:\n"); - if (pr->throttling.acpi_processor_get_throttling == - acpi_processor_get_throttling_fadt) { - for (i = 0; i < pr->throttling.state_count; i++) - seq_printf(seq, " %cT%d: %02d%%\n", - (i == pr->throttling.state ? '*' : ' '), i, - (pr->throttling.states[i].performance ? pr-> - throttling.states[i].performance / 10 : 0)); - } else { - for (i = 0; i < pr->throttling.state_count; i++) - seq_printf(seq, " %cT%d: %02d%%\n", - (i == pr->throttling.state ? '*' : ' '), i, - (int)pr->throttling.states_tss[i]. - freqpercentage); - } - - end: - return 0; -} - -static int acpi_processor_throttling_open_fs(struct inode *inode, - struct file *file) -{ - return single_open(file, acpi_processor_throttling_seq_show, - PDE(inode)->data); -} - -static ssize_t acpi_processor_write_throttling(struct file *file, - const char __user * buffer, - size_t count, loff_t * data) -{ - int result = 0; - struct seq_file *m = file->private_data; - struct acpi_processor *pr = m->private; - char state_string[5] = ""; - char *charp = NULL; - size_t state_val = 0; - char tmpbuf[5] = ""; - - if (!pr || (count > sizeof(state_string) - 1)) - return -EINVAL; - - if (copy_from_user(state_string, buffer, count)) - return -EFAULT; - - state_string[count] = '\0'; - if ((count > 0) && (state_string[count-1] == '\n')) - state_string[count-1] = '\0'; - - charp = state_string; - if ((state_string[0] == 't') || (state_string[0] == 'T')) - charp++; - - state_val = simple_strtoul(charp, NULL, 0); - if (state_val >= pr->throttling.state_count) - return -EINVAL; - - snprintf(tmpbuf, 5, "%zu", state_val); - - if (strcmp(tmpbuf, charp) != 0) - return -EINVAL; - - result = acpi_processor_set_throttling(pr, state_val, false); - if (result) - return result; - - return count; -} - -const struct file_operations acpi_processor_throttling_fops = { - .owner = THIS_MODULE, - .open = acpi_processor_throttling_open_fs, - .read = seq_read, - .write = acpi_processor_write_throttling, - .llseek = seq_lseek, - .release = single_release, -}; -#endif -- cgit v1.2.3 From 677bd810eedce61edf15452491781ff046b92edc Mon Sep 17 00:00:00 2001 From: Zhang Rui Date: Mon, 6 Dec 2010 15:04:21 +0800 Subject: ACPI video: remove output switching control Remove the ACPI video output switching control as it never works. With the patch applied, ACPI video driver still catches the video output notification, but it does nothing but raises the notification to userspace. Signed-off-by: Zhang Rui Signed-off-by: Len Brown --- Documentation/kernel-parameters.txt | 5 --- drivers/acpi/video.c | 86 ------------------------------------- drivers/acpi/video_detect.c | 57 ++---------------------- drivers/gpu/drm/Kconfig | 1 - drivers/gpu/stub/Kconfig | 1 - 5 files changed, 4 insertions(+), 146 deletions(-) (limited to 'drivers/acpi') diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index cdd2a6e8a3b7..f91fc5a65034 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -199,11 +199,6 @@ and is between 256 and 4096 characters. It is defined in the file unusable. The "log_buf_len" parameter may be useful if you need to capture more output. - acpi_display_output= [HW,ACPI] - acpi_display_output=vendor - acpi_display_output=video - See above. - acpi_irq_balance [HW,ACPI] ACPI will balance active IRQs default in APIC mode diff --git a/drivers/acpi/video.c b/drivers/acpi/video.c index 5cd0228d2daa..81766ebec843 100644 --- a/drivers/acpi/video.c +++ b/drivers/acpi/video.c @@ -33,7 +33,6 @@ #include #include #include -#include #include #include #include @@ -172,9 +171,6 @@ struct acpi_video_device_cap { u8 _BQC:1; /* Get current brightness level */ u8 _BCQ:1; /* Some buggy BIOS uses _BCQ instead of _BQC */ u8 _DDC:1; /*Return the EDID for this device */ - u8 _DCS:1; /*Return status of output device */ - u8 _DGS:1; /*Query graphics state */ - u8 _DSS:1; /*Device state set */ }; struct acpi_video_brightness_flags { @@ -202,7 +198,6 @@ struct acpi_video_device { struct acpi_video_device_brightness *brightness; struct backlight_device *backlight; struct thermal_cooling_device *cooling_dev; - struct output_device *output_dev; }; static const char device_decode[][30] = { @@ -226,10 +221,6 @@ static int acpi_video_get_next_level(struct acpi_video_device *device, u32 level_current, u32 event); static int acpi_video_switch_brightness(struct acpi_video_device *device, int event); -static int acpi_video_device_get_state(struct acpi_video_device *device, - unsigned long long *state); -static int acpi_video_output_get(struct output_device *od); -static int acpi_video_device_set_state(struct acpi_video_device *device, int state); /*backlight device sysfs support*/ static int acpi_video_get_brightness(struct backlight_device *bd) @@ -265,30 +256,6 @@ static struct backlight_ops acpi_backlight_ops = { .update_status = acpi_video_set_brightness, }; -/*video output device sysfs support*/ -static int acpi_video_output_get(struct output_device *od) -{ - unsigned long long state; - struct acpi_video_device *vd = - (struct acpi_video_device *)dev_get_drvdata(&od->dev); - acpi_video_device_get_state(vd, &state); - return (int)state; -} - -static int acpi_video_output_set(struct output_device *od) -{ - unsigned long state = od->request_state; - struct acpi_video_device *vd= - (struct acpi_video_device *)dev_get_drvdata(&od->dev); - return acpi_video_device_set_state(vd, state); -} - -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 *cooling_dev, unsigned long *state) @@ -344,34 +311,6 @@ static struct thermal_cooling_device_ops video_cooling_ops = { Video Management -------------------------------------------------------------------------- */ -/* device */ - -static int -acpi_video_device_get_state(struct acpi_video_device *device, - unsigned long long *state) -{ - int status; - - status = acpi_evaluate_integer(device->dev->handle, "_DCS", NULL, state); - - return status; -} - -static int -acpi_video_device_set_state(struct acpi_video_device *device, int state) -{ - int status; - union acpi_object arg0 = { ACPI_TYPE_INTEGER }; - struct acpi_object_list args = { 1, &arg0 }; - unsigned long long ret; - - - arg0.integer.value = state; - status = acpi_evaluate_integer(device->dev->handle, "_DSS", &args, &ret); - - return status; -} - static int acpi_video_device_lcd_query_levels(struct acpi_video_device *device, union acpi_object **levels) @@ -831,15 +770,6 @@ static void acpi_video_device_find_cap(struct acpi_video_device *device) if (ACPI_SUCCESS(acpi_get_handle(device->dev->handle, "_DDC", &h_dummy1))) { device->cap._DDC = 1; } - if (ACPI_SUCCESS(acpi_get_handle(device->dev->handle, "_DCS", &h_dummy1))) { - device->cap._DCS = 1; - } - if (ACPI_SUCCESS(acpi_get_handle(device->dev->handle, "_DGS", &h_dummy1))) { - device->cap._DGS = 1; - } - if (ACPI_SUCCESS(acpi_get_handle(device->dev->handle, "_DSS", &h_dummy1))) { - device->cap._DSS = 1; - } if (acpi_video_backlight_support()) { struct backlight_properties props; @@ -904,21 +834,6 @@ static void acpi_video_device_find_cap(struct acpi_video_device *device) printk(KERN_ERR PREFIX "Create sysfs link\n"); } - - if (acpi_video_display_switch_support()) { - - if (device->cap._DCS && device->cap._DSS) { - static int count; - char *name; - name = kasprintf(GFP_KERNEL, "acpi_video%d", count); - if (!name) - return; - count++; - device->output_dev = video_output_register(name, - NULL, device, &acpi_output_properties); - kfree(name); - } - } } /* @@ -1452,7 +1367,6 @@ static int acpi_video_bus_put_one_device(struct acpi_video_device *device) thermal_cooling_device_unregister(device->cooling_dev); device->cooling_dev = NULL; } - video_output_unregister(device->output_dev); return 0; } diff --git a/drivers/acpi/video_detect.c b/drivers/acpi/video_detect.c index b83676126598..42d3d72dae85 100644 --- a/drivers/acpi/video_detect.c +++ b/drivers/acpi/video_detect.c @@ -17,15 +17,14 @@ * capabilities the graphics cards plugged in support. The check for general * video capabilities will be triggered by the first caller of * acpi_video_get_capabilities(NULL); which will happen when the first - * backlight (or display output) switching supporting driver calls: + * backlight switching supporting driver calls: * acpi_video_backlight_support(); * * Depending on whether ACPI graphics extensions (cmp. ACPI spec Appendix B) * are available, video.ko should be used to handle the device. * * Otherwise vendor specific drivers like thinkpad_acpi, asus_acpi, - * sony_acpi,... can take care about backlight brightness and display output - * switching. + * sony_acpi,... can take care about backlight brightness. * * If CONFIG_ACPI_VIDEO is neither set as "compiled in" (y) nor as a module (m) * this file will not be compiled, acpi_video_get_capabilities() and @@ -83,11 +82,6 @@ long acpi_is_video_device(struct acpi_device *device) if (!device) return 0; - /* Is this device able to support video switching ? */ - if (ACPI_SUCCESS(acpi_get_handle(device->handle, "_DOD", &h_dummy)) || - ACPI_SUCCESS(acpi_get_handle(device->handle, "_DOS", &h_dummy))) - video_caps |= ACPI_VIDEO_OUTPUT_SWITCHING; - /* Is this device able to retrieve a video ROM ? */ if (ACPI_SUCCESS(acpi_get_handle(device->handle, "_ROM", &h_dummy))) video_caps |= ACPI_VIDEO_ROM_AVAILABLE; @@ -161,8 +155,6 @@ long acpi_video_get_capabilities(acpi_handle graphics_handle) * * if (dmi_name_in_vendors("XY")) { * acpi_video_support |= - * ACPI_VIDEO_OUTPUT_SWITCHING_DMI_VENDOR; - * acpi_video_support |= * ACPI_VIDEO_BACKLIGHT_DMI_VENDOR; *} */ @@ -212,33 +204,8 @@ int acpi_video_backlight_support(void) EXPORT_SYMBOL(acpi_video_backlight_support); /* - * Returns true if video.ko can do display output switching. - * This does not work well/at all with binary graphics drivers - * which disable system io ranges and do it on their own. - */ -int acpi_video_display_switch_support(void) -{ - if (!acpi_video_caps_checked) - acpi_video_get_capabilities(NULL); - - if (acpi_video_support & ACPI_VIDEO_OUTPUT_SWITCHING_FORCE_VENDOR) - return 0; - else if (acpi_video_support & ACPI_VIDEO_OUTPUT_SWITCHING_FORCE_VIDEO) - return 1; - - if (acpi_video_support & ACPI_VIDEO_OUTPUT_SWITCHING_DMI_VENDOR) - return 0; - else if (acpi_video_support & ACPI_VIDEO_OUTPUT_SWITCHING_DMI_VIDEO) - return 1; - - return acpi_video_support & ACPI_VIDEO_OUTPUT_SWITCHING; -} -EXPORT_SYMBOL(acpi_video_display_switch_support); - -/* - * Use acpi_display_output=vendor/video or acpi_backlight=vendor/video - * To force that backlight or display output switching is processed by vendor - * specific acpi drivers or video.ko driver. + * Use acpi_backlight=vendor/video to force that backlight switching + * is processed by vendor specific acpi drivers or video.ko driver. */ static int __init acpi_backlight(char *str) { @@ -255,19 +222,3 @@ static int __init acpi_backlight(char *str) return 1; } __setup("acpi_backlight=", acpi_backlight); - -static int __init acpi_display_output(char *str) -{ - if (str == NULL || *str == '\0') - return 1; - else { - if (!strcmp("vendor", str)) - acpi_video_support |= - ACPI_VIDEO_OUTPUT_SWITCHING_FORCE_VENDOR; - if (!strcmp("video", str)) - acpi_video_support |= - ACPI_VIDEO_OUTPUT_SWITCHING_FORCE_VIDEO; - } - return 1; -} -__setup("acpi_display_output=", acpi_display_output); diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index 7af443672626..64828a7db77b 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -107,7 +107,6 @@ config DRM_I915 select FB_CFB_IMAGEBLIT # i915 depends on ACPI_VIDEO when ACPI is enabled # but for select to work, need to select ACPI_VIDEO's dependencies, ick - select VIDEO_OUTPUT_CONTROL if ACPI select BACKLIGHT_CLASS_DEVICE if ACPI select INPUT if ACPI select ACPI_VIDEO if ACPI diff --git a/drivers/gpu/stub/Kconfig b/drivers/gpu/stub/Kconfig index 0e1edd7311ff..09aea5f1556d 100644 --- a/drivers/gpu/stub/Kconfig +++ b/drivers/gpu/stub/Kconfig @@ -3,7 +3,6 @@ config STUB_POULSBO depends on PCI # Poulsbo stub depends on ACPI_VIDEO when ACPI is enabled # but for select to work, need to select ACPI_VIDEO's dependencies, ick - select VIDEO_OUTPUT_CONTROL if ACPI select BACKLIGHT_CLASS_DEVICE if ACPI select INPUT if ACPI select ACPI_VIDEO if ACPI -- cgit v1.2.3 From 82069552555cf951d9ade5c1aec61dd40b0765b5 Mon Sep 17 00:00:00 2001 From: Zhang Rui Date: Mon, 6 Dec 2010 15:04:24 +0800 Subject: ACPI video: check cap._DDC flag before getting EDID cap._DDC is defined but never used. Check this flag now and don't try to get EDID for video output devices with this flag cleared. Signed-off-by: Zhang Rui Signed-off-by: Len Brown --- drivers/acpi/video.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/acpi') diff --git a/drivers/acpi/video.c b/drivers/acpi/video.c index 81766ebec843..177b4ddc3479 100644 --- a/drivers/acpi/video.c +++ b/drivers/acpi/video.c @@ -1275,6 +1275,9 @@ int acpi_video_get_edid(struct acpi_device *device, int type, int device_id, if (!video_device) continue; + if (!video_device->cap._DDC) + continue; + if (type) { switch (type) { case ACPI_VIDEO_DISPLAY_CRT: -- cgit v1.2.3 From 99fd1895ef603f1a0fa9af478c96c637a7b4529d Mon Sep 17 00:00:00 2001 From: Zhang Rui Date: Mon, 6 Dec 2010 15:04:27 +0800 Subject: ACPI video: introduce module parameter video.use_bios_initial_backlight Introduce module parameter video.use_bios_initial_backlight. Some BIOSes claim they use the minimum backlight at boot, and this may bring dimming screen after boot. https://bugzilla.kernel.org/show_bug.cgi?id=21212 use video.use_bios_initl_backlight=0 to use the maximum backlight level after boot. Signed-off-by: Zhang Rui Signed-off-by: Len Brown --- drivers/acpi/video.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/video.c b/drivers/acpi/video.c index 5cd0228d2daa..89f19a8fc726 100644 --- a/drivers/acpi/video.c +++ b/drivers/acpi/video.c @@ -81,6 +81,13 @@ module_param(brightness_switch_enabled, bool, 0644); static int allow_duplicates; module_param(allow_duplicates, bool, 0644); +/* + * Some BIOSes claim they use minimum backlight at boot, + * and this may bring dimming screen after boot + */ +static int use_bios_initial_backlight = 1; +module_param(use_bios_initial_backlight, bool, 0644); + static int register_count = 0; static int acpi_video_bus_add(struct acpi_device *device); static int acpi_video_bus_remove(struct acpi_device *device, int type); @@ -766,9 +773,11 @@ acpi_video_init_brightness(struct acpi_video_device *device) * when invoked for the first time, i.e. level_old is invalid. * set the backlight to max_level in this case */ - for (i = 2; i < br->count; i++) - if (level_old == br->levels[i]) - level = level_old; + if (use_bios_initial_backlight) { + for (i = 2; i < br->count; i++) + if (level_old == br->levels[i]) + level = level_old; + } goto set_level; } -- cgit v1.2.3 From e92b297cc72ade7a58eaec7e01c906d856f8ab6e Mon Sep 17 00:00:00 2001 From: Zhao Yakui Date: Wed, 8 Dec 2010 10:10:18 +0800 Subject: IPMI/ACPI: Add the IPMI opregion driver to enable ACPI to access BMC controller ACPI 4.0 spec adds the ACPI IPMI opregion, which means that the ACPI AML code can also communicate with the BMC controller. This is to install the ACPI IPMI opregion and enable the ACPI to access the BMC controller through the IPMI message. It will create IPMI user interface for every IPMI device detected in ACPI namespace and install the corresponding IPMI opregion space handler. Then it can enable ACPI to access the BMC controller through the IPMI message. The following describes how to process the IPMI request in IPMI space handler: 1. format the IPMI message based on the request in AML code. IPMI system address. Now the address type is SYSTEM_INTERFACE_ADDR_TYPE IPMI net function & command IPMI message payload 2. send the IPMI message by using the function of ipmi_request_settime 3. wait for the completion of IPMI message. It can be done in different routes: One is in handled in IPMI user recv callback function. Another is handled in timeout function. 4. format the IPMI response and return it to ACPI AML code. At the same time it also addes the module dependency. The ACPI IPMI opregion will depend on the IPMI subsystem. Signed-off-by: Zhao Yakui cc: Bjorn Helgaas Signed-off-by: Corey Minyard Signed-off-by: Len Brown --- drivers/acpi/Kconfig | 11 + drivers/acpi/Makefile | 1 + drivers/acpi/acpi_ipmi.c | 525 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 537 insertions(+) create mode 100644 drivers/acpi/acpi_ipmi.c (limited to 'drivers/acpi') diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index 3f3489c5ca8c..a0c03658b2ea 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig @@ -209,6 +209,17 @@ config ACPI_PROCESSOR To compile this driver as a module, choose M here: the module will be called processor. +config ACPI_IPMI + tristate "IPMI" + depends on EXPERIMENTAL && IPMI_SI && IPMI_HANDLER + default n + help + This driver enables the ACPI to access the BMC controller. And it + uses the IPMI request/response message to communicate with BMC + controller, which can be found on on the server. + + To compile this driver as a module, choose M here: + the module will be called as acpi_ipmi. config ACPI_HOTPLUG_CPU bool diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile index 3d031d02e54b..df4c4f030388 100644 --- a/drivers/acpi/Makefile +++ b/drivers/acpi/Makefile @@ -69,5 +69,6 @@ processor-y += processor_idle.o processor_thermal.o processor-$(CONFIG_CPU_FREQ) += processor_perflib.o obj-$(CONFIG_ACPI_PROCESSOR_AGGREGATOR) += acpi_pad.o +obj-$(CONFIG_ACPI_IPMI) += acpi_ipmi.o obj-$(CONFIG_ACPI_APEI) += apei/ diff --git a/drivers/acpi/acpi_ipmi.c b/drivers/acpi/acpi_ipmi.c new file mode 100644 index 000000000000..f40acef80269 --- /dev/null +++ b/drivers/acpi/acpi_ipmi.c @@ -0,0 +1,525 @@ +/* + * acpi_ipmi.c - ACPI IPMI opregion + * + * Copyright (C) 2010 Intel Corporation + * Copyright (C) 2010 Zhao Yakui + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Zhao Yakui"); +MODULE_DESCRIPTION("ACPI IPMI Opregion driver"); +MODULE_LICENSE("GPL"); + +#define IPMI_FLAGS_HANDLER_INSTALL 0 + +#define ACPI_IPMI_OK 0 +#define ACPI_IPMI_TIMEOUT 0x10 +#define ACPI_IPMI_UNKNOWN 0x07 +/* the IPMI timeout is 5s */ +#define IPMI_TIMEOUT (5 * HZ) + +struct acpi_ipmi_device { + /* the device list attached to driver_data.ipmi_devices */ + struct list_head head; + /* the IPMI request message list */ + struct list_head tx_msg_list; + struct mutex tx_msg_lock; + acpi_handle handle; + struct pnp_dev *pnp_dev; + ipmi_user_t user_interface; + int ipmi_ifnum; /* IPMI interface number */ + long curr_msgid; + unsigned long flags; + struct ipmi_smi_info smi_data; +}; + +struct ipmi_driver_data { + struct list_head ipmi_devices; + struct ipmi_smi_watcher bmc_events; + struct ipmi_user_hndl ipmi_hndlrs; + struct mutex ipmi_lock; +}; + +struct acpi_ipmi_msg { + struct list_head head; + /* + * General speaking the addr type should be SI_ADDR_TYPE. And + * the addr channel should be BMC. + * In fact it can also be IPMB type. But we will have to + * parse it from the Netfn command buffer. It is so complex + * that it is skipped. + */ + struct ipmi_addr addr; + long tx_msgid; + /* it is used to track whether the IPMI message is finished */ + struct completion tx_complete; + struct kernel_ipmi_msg tx_message; + int msg_done; + /* tx data . And copy it from ACPI object buffer */ + u8 tx_data[64]; + int tx_len; + u8 rx_data[64]; + int rx_len; + struct acpi_ipmi_device *device; +}; + +/* IPMI request/response buffer per ACPI 4.0, sec 5.5.2.4.3.2 */ +struct acpi_ipmi_buffer { + u8 status; + u8 length; + u8 data[64]; +}; + +static void ipmi_register_bmc(int iface, struct device *dev); +static void ipmi_bmc_gone(int iface); +static void ipmi_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data); +static void acpi_add_ipmi_device(struct acpi_ipmi_device *ipmi_device); +static void acpi_remove_ipmi_device(struct acpi_ipmi_device *ipmi_device); + +static struct ipmi_driver_data driver_data = { + .ipmi_devices = LIST_HEAD_INIT(driver_data.ipmi_devices), + .bmc_events = { + .owner = THIS_MODULE, + .new_smi = ipmi_register_bmc, + .smi_gone = ipmi_bmc_gone, + }, + .ipmi_hndlrs = { + .ipmi_recv_hndl = ipmi_msg_handler, + }, +}; + +static struct acpi_ipmi_msg *acpi_alloc_ipmi_msg(struct acpi_ipmi_device *ipmi) +{ + struct acpi_ipmi_msg *ipmi_msg; + struct pnp_dev *pnp_dev = ipmi->pnp_dev; + + ipmi_msg = kzalloc(sizeof(struct acpi_ipmi_msg), GFP_KERNEL); + if (!ipmi_msg) { + dev_warn(&pnp_dev->dev, "Can't allocate memory for ipmi_msg\n"); + return NULL; + } + init_completion(&ipmi_msg->tx_complete); + INIT_LIST_HEAD(&ipmi_msg->head); + ipmi_msg->device = ipmi; + return ipmi_msg; +} + +#define IPMI_OP_RGN_NETFN(offset) ((offset >> 8) & 0xff) +#define IPMI_OP_RGN_CMD(offset) (offset & 0xff) +static void acpi_format_ipmi_msg(struct acpi_ipmi_msg *tx_msg, + acpi_physical_address address, + acpi_integer *value) +{ + struct kernel_ipmi_msg *msg; + struct acpi_ipmi_buffer *buffer; + struct acpi_ipmi_device *device; + + msg = &tx_msg->tx_message; + /* + * IPMI network function and command are encoded in the address + * within the IPMI OpRegion; see ACPI 4.0, sec 5.5.2.4.3. + */ + msg->netfn = IPMI_OP_RGN_NETFN(address); + msg->cmd = IPMI_OP_RGN_CMD(address); + msg->data = tx_msg->tx_data; + /* + * value is the parameter passed by the IPMI opregion space handler. + * It points to the IPMI request message buffer + */ + buffer = (struct acpi_ipmi_buffer *)value; + /* copy the tx message data */ + msg->data_len = buffer->length; + memcpy(tx_msg->tx_data, buffer->data, msg->data_len); + /* + * now the default type is SYSTEM_INTERFACE and channel type is BMC. + * If the netfn is APP_REQUEST and the cmd is SEND_MESSAGE, + * the addr type should be changed to IPMB. Then we will have to parse + * the IPMI request message buffer to get the IPMB address. + * If so, please fix me. + */ + tx_msg->addr.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE; + tx_msg->addr.channel = IPMI_BMC_CHANNEL; + tx_msg->addr.data[0] = 0; + + /* Get the msgid */ + device = tx_msg->device; + mutex_lock(&device->tx_msg_lock); + device->curr_msgid++; + tx_msg->tx_msgid = device->curr_msgid; + mutex_unlock(&device->tx_msg_lock); +} + +static void acpi_format_ipmi_response(struct acpi_ipmi_msg *msg, + acpi_integer *value, int rem_time) +{ + struct acpi_ipmi_buffer *buffer; + + /* + * value is also used as output parameter. It represents the response + * IPMI message returned by IPMI command. + */ + buffer = (struct acpi_ipmi_buffer *)value; + if (!rem_time && !msg->msg_done) { + buffer->status = ACPI_IPMI_TIMEOUT; + return; + } + /* + * If the flag of msg_done is not set or the recv length is zero, it + * means that the IPMI command is not executed correctly. + * The status code will be ACPI_IPMI_UNKNOWN. + */ + if (!msg->msg_done || !msg->rx_len) { + buffer->status = ACPI_IPMI_UNKNOWN; + return; + } + /* + * If the IPMI response message is obtained correctly, the status code + * will be ACPI_IPMI_OK + */ + buffer->status = ACPI_IPMI_OK; + buffer->length = msg->rx_len; + memcpy(buffer->data, msg->rx_data, msg->rx_len); +} + +static void ipmi_flush_tx_msg(struct acpi_ipmi_device *ipmi) +{ + struct acpi_ipmi_msg *tx_msg, *temp; + int count = HZ / 10; + struct pnp_dev *pnp_dev = ipmi->pnp_dev; + + list_for_each_entry_safe(tx_msg, temp, &ipmi->tx_msg_list, head) { + /* wake up the sleep thread on the Tx msg */ + complete(&tx_msg->tx_complete); + } + + /* wait for about 100ms to flush the tx message list */ + while (count--) { + if (list_empty(&ipmi->tx_msg_list)) + break; + schedule_timeout(1); + } + if (!list_empty(&ipmi->tx_msg_list)) + dev_warn(&pnp_dev->dev, "tx msg list is not NULL\n"); +} + +static void ipmi_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data) +{ + struct acpi_ipmi_device *ipmi_device = user_msg_data; + int msg_found = 0; + struct acpi_ipmi_msg *tx_msg; + struct pnp_dev *pnp_dev = ipmi_device->pnp_dev; + + if (msg->user != ipmi_device->user_interface) { + dev_warn(&pnp_dev->dev, "Unexpected response is returned. " + "returned user %p, expected user %p\n", + msg->user, ipmi_device->user_interface); + ipmi_free_recv_msg(msg); + return; + } + mutex_lock(&ipmi_device->tx_msg_lock); + list_for_each_entry(tx_msg, &ipmi_device->tx_msg_list, head) { + if (msg->msgid == tx_msg->tx_msgid) { + msg_found = 1; + break; + } + } + + mutex_unlock(&ipmi_device->tx_msg_lock); + if (!msg_found) { + dev_warn(&pnp_dev->dev, "Unexpected response (msg id %ld) is " + "returned.\n", msg->msgid); + ipmi_free_recv_msg(msg); + return; + } + + if (msg->msg.data_len) { + /* copy the response data to Rx_data buffer */ + memcpy(tx_msg->rx_data, msg->msg_data, msg->msg.data_len); + tx_msg->rx_len = msg->msg.data_len; + tx_msg->msg_done = 1; + } + complete(&tx_msg->tx_complete); + ipmi_free_recv_msg(msg); +}; + +static void ipmi_register_bmc(int iface, struct device *dev) +{ + struct acpi_ipmi_device *ipmi_device, *temp; + struct pnp_dev *pnp_dev; + ipmi_user_t user; + int err; + struct ipmi_smi_info smi_data; + acpi_handle handle; + + err = ipmi_get_smi_info(iface, &smi_data); + + if (err) + return; + + if (smi_data.addr_src != SI_ACPI) { + put_device(smi_data.dev); + return; + } + + handle = smi_data.addr_info.acpi_info.acpi_handle; + + mutex_lock(&driver_data.ipmi_lock); + list_for_each_entry(temp, &driver_data.ipmi_devices, head) { + /* + * if the corresponding ACPI handle is already added + * to the device list, don't add it again. + */ + if (temp->handle == handle) + goto out; + } + + ipmi_device = kzalloc(sizeof(*ipmi_device), GFP_KERNEL); + + if (!ipmi_device) + goto out; + + pnp_dev = to_pnp_dev(smi_data.dev); + ipmi_device->handle = handle; + ipmi_device->pnp_dev = pnp_dev; + + err = ipmi_create_user(iface, &driver_data.ipmi_hndlrs, + ipmi_device, &user); + if (err) { + dev_warn(&pnp_dev->dev, "Can't create IPMI user interface\n"); + kfree(ipmi_device); + goto out; + } + acpi_add_ipmi_device(ipmi_device); + ipmi_device->user_interface = user; + ipmi_device->ipmi_ifnum = iface; + mutex_unlock(&driver_data.ipmi_lock); + memcpy(&ipmi_device->smi_data, &smi_data, sizeof(struct ipmi_smi_info)); + return; + +out: + mutex_unlock(&driver_data.ipmi_lock); + put_device(smi_data.dev); + return; +} + +static void ipmi_bmc_gone(int iface) +{ + struct acpi_ipmi_device *ipmi_device, *temp; + + mutex_lock(&driver_data.ipmi_lock); + list_for_each_entry_safe(ipmi_device, temp, + &driver_data.ipmi_devices, head) { + if (ipmi_device->ipmi_ifnum != iface) + continue; + + acpi_remove_ipmi_device(ipmi_device); + put_device(ipmi_device->smi_data.dev); + kfree(ipmi_device); + break; + } + mutex_unlock(&driver_data.ipmi_lock); +} +/* -------------------------------------------------------------------------- + * Address Space Management + * -------------------------------------------------------------------------- */ +/* + * This is the IPMI opregion space handler. + * @function: indicates the read/write. In fact as the IPMI message is driven + * by command, only write is meaningful. + * @address: This contains the netfn/command of IPMI request message. + * @bits : not used. + * @value : it is an in/out parameter. It points to the IPMI message buffer. + * Before the IPMI message is sent, it represents the actual request + * IPMI message. After the IPMI message is finished, it represents + * the response IPMI message returned by IPMI command. + * @handler_context: IPMI device context. + */ + +static acpi_status +acpi_ipmi_space_handler(u32 function, acpi_physical_address address, + u32 bits, acpi_integer *value, + void *handler_context, void *region_context) +{ + struct acpi_ipmi_msg *tx_msg; + struct acpi_ipmi_device *ipmi_device = handler_context; + int err, rem_time; + acpi_status status; + /* + * IPMI opregion message. + * IPMI message is firstly written to the BMC and system software + * can get the respsonse. So it is unmeaningful for the read access + * of IPMI opregion. + */ + if ((function & ACPI_IO_MASK) == ACPI_READ) + return AE_TYPE; + + if (!ipmi_device->user_interface) + return AE_NOT_EXIST; + + tx_msg = acpi_alloc_ipmi_msg(ipmi_device); + if (!tx_msg) + return AE_NO_MEMORY; + + acpi_format_ipmi_msg(tx_msg, address, value); + mutex_lock(&ipmi_device->tx_msg_lock); + list_add_tail(&tx_msg->head, &ipmi_device->tx_msg_list); + mutex_unlock(&ipmi_device->tx_msg_lock); + err = ipmi_request_settime(ipmi_device->user_interface, + &tx_msg->addr, + tx_msg->tx_msgid, + &tx_msg->tx_message, + NULL, 0, 0, 0); + if (err) { + status = AE_ERROR; + goto end_label; + } + rem_time = wait_for_completion_timeout(&tx_msg->tx_complete, + IPMI_TIMEOUT); + acpi_format_ipmi_response(tx_msg, value, rem_time); + status = AE_OK; + +end_label: + mutex_lock(&ipmi_device->tx_msg_lock); + list_del(&tx_msg->head); + mutex_unlock(&ipmi_device->tx_msg_lock); + kfree(tx_msg); + return status; +} + +static void ipmi_remove_space_handler(struct acpi_ipmi_device *ipmi) +{ + if (!test_bit(IPMI_FLAGS_HANDLER_INSTALL, &ipmi->flags)) + return; + + acpi_remove_address_space_handler(ipmi->handle, + ACPI_ADR_SPACE_IPMI, &acpi_ipmi_space_handler); + + clear_bit(IPMI_FLAGS_HANDLER_INSTALL, &ipmi->flags); +} + +static int ipmi_install_space_handler(struct acpi_ipmi_device *ipmi) +{ + acpi_status status; + + if (test_bit(IPMI_FLAGS_HANDLER_INSTALL, &ipmi->flags)) + return 0; + + status = acpi_install_address_space_handler(ipmi->handle, + ACPI_ADR_SPACE_IPMI, + &acpi_ipmi_space_handler, + NULL, ipmi); + if (ACPI_FAILURE(status)) { + struct pnp_dev *pnp_dev = ipmi->pnp_dev; + dev_warn(&pnp_dev->dev, "Can't register IPMI opregion space " + "handle\n"); + return -EINVAL; + } + set_bit(IPMI_FLAGS_HANDLER_INSTALL, &ipmi->flags); + return 0; +} + +static void acpi_add_ipmi_device(struct acpi_ipmi_device *ipmi_device) +{ + + INIT_LIST_HEAD(&ipmi_device->head); + + mutex_init(&ipmi_device->tx_msg_lock); + INIT_LIST_HEAD(&ipmi_device->tx_msg_list); + ipmi_install_space_handler(ipmi_device); + + list_add_tail(&ipmi_device->head, &driver_data.ipmi_devices); +} + +static void acpi_remove_ipmi_device(struct acpi_ipmi_device *ipmi_device) +{ + /* + * If the IPMI user interface is created, it should be + * destroyed. + */ + if (ipmi_device->user_interface) { + ipmi_destroy_user(ipmi_device->user_interface); + ipmi_device->user_interface = NULL; + } + /* flush the Tx_msg list */ + if (!list_empty(&ipmi_device->tx_msg_list)) + ipmi_flush_tx_msg(ipmi_device); + + list_del(&ipmi_device->head); + ipmi_remove_space_handler(ipmi_device); +} + +static int __init acpi_ipmi_init(void) +{ + int result = 0; + + if (acpi_disabled) + return result; + + mutex_init(&driver_data.ipmi_lock); + + result = ipmi_smi_watcher_register(&driver_data.bmc_events); + + return result; +} + +static void __exit acpi_ipmi_exit(void) +{ + struct acpi_ipmi_device *ipmi_device, *temp; + + if (acpi_disabled) + return; + + ipmi_smi_watcher_unregister(&driver_data.bmc_events); + + /* + * When one smi_watcher is unregistered, it is only deleted + * from the smi_watcher list. But the smi_gone callback function + * is not called. So explicitly uninstall the ACPI IPMI oregion + * handler and free it. + */ + mutex_lock(&driver_data.ipmi_lock); + list_for_each_entry_safe(ipmi_device, temp, + &driver_data.ipmi_devices, head) { + acpi_remove_ipmi_device(ipmi_device); + put_device(ipmi_device->smi_data.dev); + kfree(ipmi_device); + } + mutex_unlock(&driver_data.ipmi_lock); +} + +module_init(acpi_ipmi_init); +module_exit(acpi_ipmi_exit); -- cgit v1.2.3 From 26fcaf60fe3861409eb4c455c5c0d0f00f599b08 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Fri, 7 Jan 2011 01:42:31 +0100 Subject: PM: Fix oops in suspend/hibernate code related to failing ioremap() When ioremap() fails (which might happen for some reason), we nicely oops in suspend_nvs_save() due to NULL dereference by memcpy() in there. Fail gracefully instead. Signed-off-by: Jiri Slaby Signed-off-by: Rafael J. Wysocki Signed-off-by: Len Brown --- drivers/acpi/sleep.c | 5 ++--- include/linux/suspend.h | 4 ++-- kernel/power/nvs.c | 8 +++++++- 3 files changed, 11 insertions(+), 6 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/sleep.c b/drivers/acpi/sleep.c index febb153b5a68..d8bca6c90719 100644 --- a/drivers/acpi/sleep.c +++ b/drivers/acpi/sleep.c @@ -124,8 +124,7 @@ static int acpi_pm_freeze(void) static int acpi_pm_pre_suspend(void) { acpi_pm_freeze(); - suspend_nvs_save(); - return 0; + return suspend_nvs_save(); } /** @@ -151,7 +150,7 @@ static int acpi_pm_prepare(void) { int error = __acpi_pm_prepare(); if (!error) - acpi_pm_pre_suspend(); + error = acpi_pm_pre_suspend(); return error; } diff --git a/include/linux/suspend.h b/include/linux/suspend.h index 26697514c5ec..acb7d911bb0c 100644 --- a/include/linux/suspend.h +++ b/include/linux/suspend.h @@ -262,7 +262,7 @@ static inline bool system_entering_hibernation(void) { return false; } extern int suspend_nvs_register(unsigned long start, unsigned long size); extern int suspend_nvs_alloc(void); extern void suspend_nvs_free(void); -extern void suspend_nvs_save(void); +extern int suspend_nvs_save(void); extern void suspend_nvs_restore(void); #else /* CONFIG_SUSPEND_NVS */ static inline int suspend_nvs_register(unsigned long a, unsigned long b) @@ -271,7 +271,7 @@ static inline int suspend_nvs_register(unsigned long a, unsigned long b) } static inline int suspend_nvs_alloc(void) { return 0; } static inline void suspend_nvs_free(void) {} -static inline void suspend_nvs_save(void) {} +static inline int suspend_nvs_save(void) {} static inline void suspend_nvs_restore(void) {} #endif /* CONFIG_SUSPEND_NVS */ diff --git a/kernel/power/nvs.c b/kernel/power/nvs.c index 1836db60bbb6..57c6fabbb6b6 100644 --- a/kernel/power/nvs.c +++ b/kernel/power/nvs.c @@ -105,7 +105,7 @@ int suspend_nvs_alloc(void) /** * suspend_nvs_save - save NVS memory regions */ -void suspend_nvs_save(void) +int suspend_nvs_save(void) { struct nvs_page *entry; @@ -114,8 +114,14 @@ void suspend_nvs_save(void) list_for_each_entry(entry, &nvs_list, node) if (entry->data) { entry->kaddr = ioremap(entry->phys_start, entry->size); + if (!entry->kaddr) { + suspend_nvs_free(); + return -ENOMEM; + } memcpy(entry->data, entry->kaddr, entry->size); } + + return 0; } /** -- cgit v1.2.3 From 976513dbfc1547c7b1822566923058655f0c32fd Mon Sep 17 00:00:00 2001 From: Rafael J. Wysocki Date: Fri, 7 Jan 2011 01:43:44 +0100 Subject: PM / ACPI: Move NVS saving and restoring code to drivers/acpi The saving of the ACPI NVS area during hibernation and suspend and restoring it during the subsequent resume is entirely specific to ACPI, so move it to drivers/acpi and drop the CONFIG_SUSPEND_NVS configuration option which is redundant. Signed-off-by: Rafael J. Wysocki Signed-off-by: Len Brown --- arch/x86/kernel/e820.c | 1 + drivers/acpi/Makefile | 2 +- drivers/acpi/internal.h | 8 +++ drivers/acpi/nvs.c | 142 ++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/acpi.h | 9 +++ include/linux/suspend.h | 17 ------ kernel/power/Kconfig | 5 -- kernel/power/Makefile | 1 - kernel/power/nvs.c | 142 ------------------------------------------------ 9 files changed, 161 insertions(+), 166 deletions(-) create mode 100644 drivers/acpi/nvs.c delete mode 100644 kernel/power/nvs.c (limited to 'drivers/acpi') diff --git a/arch/x86/kernel/e820.c b/arch/x86/kernel/e820.c index 0c2b7ef7a34d..294f26da0c0c 100644 --- a/arch/x86/kernel/e820.c +++ b/arch/x86/kernel/e820.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile index 3d031d02e54b..9cc9f2c4da79 100644 --- a/drivers/acpi/Makefile +++ b/drivers/acpi/Makefile @@ -24,7 +24,7 @@ acpi-y += atomicio.o # sleep related files acpi-y += wakeup.o acpi-y += sleep.o -acpi-$(CONFIG_ACPI_SLEEP) += proc.o +acpi-$(CONFIG_ACPI_SLEEP) += proc.o nvs.o # diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h index a212bfeddf8c..7c23b76e8eca 100644 --- a/drivers/acpi/internal.h +++ b/drivers/acpi/internal.h @@ -82,8 +82,16 @@ extern int acpi_sleep_init(void); #ifdef CONFIG_ACPI_SLEEP int acpi_sleep_proc_init(void); +int suspend_nvs_alloc(void); +void suspend_nvs_free(void); +int suspend_nvs_save(void); +void suspend_nvs_restore(void); #else static inline int acpi_sleep_proc_init(void) { return 0; } +static inline int suspend_nvs_alloc(void) { return 0; } +static inline void suspend_nvs_free(void) {} +static inline int suspend_nvs_save(void) {} +static inline void suspend_nvs_restore(void) {} #endif #endif /* _ACPI_INTERNAL_H_ */ diff --git a/drivers/acpi/nvs.c b/drivers/acpi/nvs.c new file mode 100644 index 000000000000..57c6fabbb6b6 --- /dev/null +++ b/drivers/acpi/nvs.c @@ -0,0 +1,142 @@ +/* + * linux/kernel/power/hibernate_nvs.c - Routines for handling NVS memory + * + * Copyright (C) 2008,2009 Rafael J. Wysocki , Novell Inc. + * + * This file is released under the GPLv2. + */ + +#include +#include +#include +#include +#include +#include + +/* + * Platforms, like ACPI, may want us to save some memory used by them during + * suspend and to restore the contents of this memory during the subsequent + * resume. The code below implements a mechanism allowing us to do that. + */ + +struct nvs_page { + unsigned long phys_start; + unsigned int size; + void *kaddr; + void *data; + struct list_head node; +}; + +static LIST_HEAD(nvs_list); + +/** + * suspend_nvs_register - register platform NVS memory region to save + * @start - physical address of the region + * @size - size of the region + * + * The NVS region need not be page-aligned (both ends) and we arrange + * things so that the data from page-aligned addresses in this region will + * be copied into separate RAM pages. + */ +int suspend_nvs_register(unsigned long start, unsigned long size) +{ + struct nvs_page *entry, *next; + + while (size > 0) { + unsigned int nr_bytes; + + entry = kzalloc(sizeof(struct nvs_page), GFP_KERNEL); + if (!entry) + goto Error; + + list_add_tail(&entry->node, &nvs_list); + entry->phys_start = start; + nr_bytes = PAGE_SIZE - (start & ~PAGE_MASK); + entry->size = (size < nr_bytes) ? size : nr_bytes; + + start += entry->size; + size -= entry->size; + } + return 0; + + Error: + list_for_each_entry_safe(entry, next, &nvs_list, node) { + list_del(&entry->node); + kfree(entry); + } + return -ENOMEM; +} + +/** + * suspend_nvs_free - free data pages allocated for saving NVS regions + */ +void suspend_nvs_free(void) +{ + struct nvs_page *entry; + + list_for_each_entry(entry, &nvs_list, node) + if (entry->data) { + free_page((unsigned long)entry->data); + entry->data = NULL; + if (entry->kaddr) { + iounmap(entry->kaddr); + entry->kaddr = NULL; + } + } +} + +/** + * suspend_nvs_alloc - allocate memory necessary for saving NVS regions + */ +int suspend_nvs_alloc(void) +{ + struct nvs_page *entry; + + list_for_each_entry(entry, &nvs_list, node) { + entry->data = (void *)__get_free_page(GFP_KERNEL); + if (!entry->data) { + suspend_nvs_free(); + return -ENOMEM; + } + } + return 0; +} + +/** + * suspend_nvs_save - save NVS memory regions + */ +int suspend_nvs_save(void) +{ + struct nvs_page *entry; + + printk(KERN_INFO "PM: Saving platform NVS memory\n"); + + list_for_each_entry(entry, &nvs_list, node) + if (entry->data) { + entry->kaddr = ioremap(entry->phys_start, entry->size); + if (!entry->kaddr) { + suspend_nvs_free(); + return -ENOMEM; + } + memcpy(entry->data, entry->kaddr, entry->size); + } + + return 0; +} + +/** + * suspend_nvs_restore - restore NVS memory regions + * + * This function is going to be called with interrupts disabled, so it + * cannot iounmap the virtual addresses used to access the NVS region. + */ +void suspend_nvs_restore(void) +{ + struct nvs_page *entry; + + printk(KERN_INFO "PM: Restoring platform NVS memory\n"); + + list_for_each_entry(entry, &nvs_list, node) + if (entry->data) + memcpy(entry->kaddr, entry->data, entry->size); +} diff --git a/include/linux/acpi.h b/include/linux/acpi.h index 67c91b4418b0..fa7ed6a983d0 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -254,6 +254,15 @@ void __init acpi_old_suspend_ordering(void); void __init acpi_nvs_nosave(void); #endif /* CONFIG_PM_SLEEP */ +#ifdef CONFIG_ACPI_SLEEP +int suspend_nvs_register(unsigned long start, unsigned long size); +#else +static inline int suspend_nvs_register(unsigned long a, unsigned long b) +{ + return 0; +} +#endif + struct acpi_osc_context { char *uuid_str; /* uuid string */ int rev; diff --git a/include/linux/suspend.h b/include/linux/suspend.h index acb7d911bb0c..0e288e3c37be 100644 --- a/include/linux/suspend.h +++ b/include/linux/suspend.h @@ -258,23 +258,6 @@ static inline int hibernate(void) { return -ENOSYS; } static inline bool system_entering_hibernation(void) { return false; } #endif /* CONFIG_HIBERNATION */ -#ifdef CONFIG_SUSPEND_NVS -extern int suspend_nvs_register(unsigned long start, unsigned long size); -extern int suspend_nvs_alloc(void); -extern void suspend_nvs_free(void); -extern int suspend_nvs_save(void); -extern void suspend_nvs_restore(void); -#else /* CONFIG_SUSPEND_NVS */ -static inline int suspend_nvs_register(unsigned long a, unsigned long b) -{ - return 0; -} -static inline int suspend_nvs_alloc(void) { return 0; } -static inline void suspend_nvs_free(void) {} -static inline int suspend_nvs_save(void) {} -static inline void suspend_nvs_restore(void) {} -#endif /* CONFIG_SUSPEND_NVS */ - #ifdef CONFIG_PM_SLEEP void save_processor_state(void); void restore_processor_state(void); diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig index a5aff3ebad38..265729966ece 100644 --- a/kernel/power/Kconfig +++ b/kernel/power/Kconfig @@ -100,13 +100,9 @@ config PM_SLEEP_ADVANCED_DEBUG depends on PM_ADVANCED_DEBUG default n -config SUSPEND_NVS - bool - config SUSPEND bool "Suspend to RAM and standby" depends on PM && ARCH_SUSPEND_POSSIBLE - select SUSPEND_NVS if HAS_IOMEM default y ---help--- Allow the system to enter sleep states in which main memory is @@ -140,7 +136,6 @@ config HIBERNATION depends on PM && SWAP && ARCH_HIBERNATION_POSSIBLE select LZO_COMPRESS select LZO_DECOMPRESS - select SUSPEND_NVS if HAS_IOMEM ---help--- Enable the suspend to disk (STD) functionality, which is usually called "hibernation" in user interfaces. STD checkpoints the diff --git a/kernel/power/Makefile b/kernel/power/Makefile index f9063c6b185d..120a15823325 100644 --- a/kernel/power/Makefile +++ b/kernel/power/Makefile @@ -10,6 +10,5 @@ obj-$(CONFIG_SUSPEND) += suspend.o obj-$(CONFIG_PM_TEST_SUSPEND) += suspend_test.o obj-$(CONFIG_HIBERNATION) += hibernate.o snapshot.o swap.o user.o \ block_io.o -obj-$(CONFIG_SUSPEND_NVS) += nvs.o obj-$(CONFIG_MAGIC_SYSRQ) += poweroff.o diff --git a/kernel/power/nvs.c b/kernel/power/nvs.c deleted file mode 100644 index 57c6fabbb6b6..000000000000 --- a/kernel/power/nvs.c +++ /dev/null @@ -1,142 +0,0 @@ -/* - * linux/kernel/power/hibernate_nvs.c - Routines for handling NVS memory - * - * Copyright (C) 2008,2009 Rafael J. Wysocki , Novell Inc. - * - * This file is released under the GPLv2. - */ - -#include -#include -#include -#include -#include -#include - -/* - * Platforms, like ACPI, may want us to save some memory used by them during - * suspend and to restore the contents of this memory during the subsequent - * resume. The code below implements a mechanism allowing us to do that. - */ - -struct nvs_page { - unsigned long phys_start; - unsigned int size; - void *kaddr; - void *data; - struct list_head node; -}; - -static LIST_HEAD(nvs_list); - -/** - * suspend_nvs_register - register platform NVS memory region to save - * @start - physical address of the region - * @size - size of the region - * - * The NVS region need not be page-aligned (both ends) and we arrange - * things so that the data from page-aligned addresses in this region will - * be copied into separate RAM pages. - */ -int suspend_nvs_register(unsigned long start, unsigned long size) -{ - struct nvs_page *entry, *next; - - while (size > 0) { - unsigned int nr_bytes; - - entry = kzalloc(sizeof(struct nvs_page), GFP_KERNEL); - if (!entry) - goto Error; - - list_add_tail(&entry->node, &nvs_list); - entry->phys_start = start; - nr_bytes = PAGE_SIZE - (start & ~PAGE_MASK); - entry->size = (size < nr_bytes) ? size : nr_bytes; - - start += entry->size; - size -= entry->size; - } - return 0; - - Error: - list_for_each_entry_safe(entry, next, &nvs_list, node) { - list_del(&entry->node); - kfree(entry); - } - return -ENOMEM; -} - -/** - * suspend_nvs_free - free data pages allocated for saving NVS regions - */ -void suspend_nvs_free(void) -{ - struct nvs_page *entry; - - list_for_each_entry(entry, &nvs_list, node) - if (entry->data) { - free_page((unsigned long)entry->data); - entry->data = NULL; - if (entry->kaddr) { - iounmap(entry->kaddr); - entry->kaddr = NULL; - } - } -} - -/** - * suspend_nvs_alloc - allocate memory necessary for saving NVS regions - */ -int suspend_nvs_alloc(void) -{ - struct nvs_page *entry; - - list_for_each_entry(entry, &nvs_list, node) { - entry->data = (void *)__get_free_page(GFP_KERNEL); - if (!entry->data) { - suspend_nvs_free(); - return -ENOMEM; - } - } - return 0; -} - -/** - * suspend_nvs_save - save NVS memory regions - */ -int suspend_nvs_save(void) -{ - struct nvs_page *entry; - - printk(KERN_INFO "PM: Saving platform NVS memory\n"); - - list_for_each_entry(entry, &nvs_list, node) - if (entry->data) { - entry->kaddr = ioremap(entry->phys_start, entry->size); - if (!entry->kaddr) { - suspend_nvs_free(); - return -ENOMEM; - } - memcpy(entry->data, entry->kaddr, entry->size); - } - - return 0; -} - -/** - * suspend_nvs_restore - restore NVS memory regions - * - * This function is going to be called with interrupts disabled, so it - * cannot iounmap the virtual addresses used to access the NVS region. - */ -void suspend_nvs_restore(void) -{ - struct nvs_page *entry; - - printk(KERN_INFO "PM: Restoring platform NVS memory\n"); - - list_for_each_entry(entry, &nvs_list, node) - if (entry->data) - memcpy(entry->kaddr, entry->data, entry->size); -} -- cgit v1.2.3 From d146df18c13d16e321efa8ef9b57c95c3bec1722 Mon Sep 17 00:00:00 2001 From: Rafael J. Wysocki Date: Fri, 7 Jan 2011 01:44:28 +0100 Subject: ACPI / PM: Update file information and the list of includes in nvs.c The file information and the list of include in drivers/acpi/nvs.c are outdated, so update them. Signed-off-by: Rafael J. Wysocki Signed-off-by: Len Brown --- drivers/acpi/nvs.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/nvs.c b/drivers/acpi/nvs.c index 57c6fabbb6b6..7d648092427e 100644 --- a/drivers/acpi/nvs.c +++ b/drivers/acpi/nvs.c @@ -1,7 +1,7 @@ /* - * linux/kernel/power/hibernate_nvs.c - Routines for handling NVS memory + * nvs.c - Routines for saving and restoring ACPI NVS memory region * - * Copyright (C) 2008,2009 Rafael J. Wysocki , Novell Inc. + * Copyright (C) 2008-2011 Rafael J. Wysocki , Novell Inc. * * This file is released under the GPLv2. */ @@ -11,7 +11,7 @@ #include #include #include -#include +#include /* * Platforms, like ACPI, may want us to save some memory used by them during -- cgit v1.2.3 From ca9b600be38c73b7d25acfb8b7e4e9a9e941d881 Mon Sep 17 00:00:00 2001 From: Rafael J. Wysocki Date: Fri, 7 Jan 2011 01:45:58 +0100 Subject: ACPI / PM: Make suspend_nvs_save() use acpi_os_map_memory() It turns out that the NVS memory region that suspend_nvs_save() attempts to map has been already mapped by acpi_os_map_memory(), so suspend_nvs_save() should better use acpi_os_map_memory() for mapping memory to avoid conflicts. Signed-off-by: Rafael J. Wysocki Signed-off-by: Len Brown --- drivers/acpi/nvs.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/nvs.c b/drivers/acpi/nvs.c index 7d648092427e..54b6ab8040a6 100644 --- a/drivers/acpi/nvs.c +++ b/drivers/acpi/nvs.c @@ -12,6 +12,7 @@ #include #include #include +#include /* * Platforms, like ACPI, may want us to save some memory used by them during @@ -79,7 +80,7 @@ void suspend_nvs_free(void) free_page((unsigned long)entry->data); entry->data = NULL; if (entry->kaddr) { - iounmap(entry->kaddr); + acpi_os_unmap_memory(entry->kaddr, entry->size); entry->kaddr = NULL; } } @@ -113,7 +114,8 @@ int suspend_nvs_save(void) list_for_each_entry(entry, &nvs_list, node) if (entry->data) { - entry->kaddr = ioremap(entry->phys_start, entry->size); + entry->kaddr = acpi_os_map_memory(entry->phys_start, + entry->size); if (!entry->kaddr) { suspend_nvs_free(); return -ENOMEM; -- cgit v1.2.3 From 6d5bbf00d251cc73223a71422d69e069dc2e0b8d Mon Sep 17 00:00:00 2001 From: Len Brown Date: Fri, 7 Jan 2011 01:46:40 +0100 Subject: ACPI: Use ioremap_cache() Although the temporary boot-time ACPI table mappings were set up with CPU caching enabled, the permanent table mappings and AML run-time region memory accesses were set up with ioremap(), which on x86 is a synonym for ioremap_nocache(). Changing this to ioremap_cache() improves performance as seen when accessing the tables via acpidump, or /sys/firmware/acpi/tables. It should also improve AML run-time performance. No change on ia64. Reported-by: Jack Steiner Signed-off-by: Len Brown Signed-off-by: Rafael J. Wysocki --- arch/ia64/include/asm/io.h | 5 +++++ drivers/acpi/osl.c | 6 +++--- 2 files changed, 8 insertions(+), 3 deletions(-) (limited to 'drivers/acpi') diff --git a/arch/ia64/include/asm/io.h b/arch/ia64/include/asm/io.h index cc8335eb3110..e5a6c3530c6c 100644 --- a/arch/ia64/include/asm/io.h +++ b/arch/ia64/include/asm/io.h @@ -426,6 +426,11 @@ extern void __iomem * ioremap_nocache (unsigned long offset, unsigned long size) extern void iounmap (volatile void __iomem *addr); extern void __iomem * early_ioremap (unsigned long phys_addr, unsigned long size); extern void early_iounmap (volatile void __iomem *addr, unsigned long size); +static inline void __iomem * ioremap_cache (unsigned long phys_addr, unsigned long size) +{ + return ioremap(phys_addr, size); +} + /* * String version of IO memory access ops: diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c index 055d7b701fff..3a7b4879fd99 100644 --- a/drivers/acpi/osl.c +++ b/drivers/acpi/osl.c @@ -320,7 +320,7 @@ acpi_os_map_memory(acpi_physical_address phys, acpi_size size) pg_off = round_down(phys, PAGE_SIZE); pg_sz = round_up(phys + size, PAGE_SIZE) - pg_off; - virt = ioremap(pg_off, pg_sz); + virt = ioremap_cache(pg_off, pg_sz); if (!virt) { kfree(map); return NULL; @@ -642,7 +642,7 @@ acpi_os_read_memory(acpi_physical_address phys_addr, u32 * value, u32 width) virt_addr = acpi_map_vaddr_lookup(phys_addr, size); rcu_read_unlock(); if (!virt_addr) { - virt_addr = ioremap(phys_addr, size); + virt_addr = ioremap_cache(phys_addr, size); unmap = 1; } if (!value) @@ -678,7 +678,7 @@ acpi_os_write_memory(acpi_physical_address phys_addr, u32 value, u32 width) virt_addr = acpi_map_vaddr_lookup(phys_addr, size); rcu_read_unlock(); if (!virt_addr) { - virt_addr = ioremap(phys_addr, size); + virt_addr = ioremap_cache(phys_addr, size); unmap = 1; } -- cgit v1.2.3 From 9cd031441a4fd09273b7c2beb337d0b2683d104c Mon Sep 17 00:00:00 2001 From: Rafael J. Wysocki Date: Thu, 6 Jan 2011 23:32:23 +0100 Subject: ACPI / ACPICA: Fix global lock acquisition There are two problems with the ACPICA's current implementation of the global lock acquisition. First, acpi_ev_global_lock_handler(), which in fact is an interface to the outside of the kernel, doesn't validate its input, so it only works correctly if the other side (i.e. the ACPI firmware) is fully specification-compliant (as far as the global lock is concerned). Unfortunately, that's known not to be the case on some systems (i.e. we get spurious global lock signaling interrupts without the pending flag set on some systems). Second, acpi_ev_global_lock_handler() attempts to acquire the global lock on behalf of a thread waiting for it without checking if there actually is such a thread. Both of these shortcomings need to be addressed to prevent all possible race conditions from happening. Rework acpi_ev_global_lock_handler() so that it doesn't try to acquire the global lock and make it signal the availability of the global lock to the waiting thread instead. Make sure that the availability of the global lock can only be signaled when there is a thread waiting for it and that it can't be signaled more than once in a row (to keep acpi_gbl_global_lock_semaphore in balance). Signed-off-by: Rafael J. Wysocki Signed-off-by: Len Brown --- drivers/acpi/acpica/evmisc.c | 94 ++++++++++++++++++++++++++------------------ 1 file changed, 55 insertions(+), 39 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/acpica/evmisc.c b/drivers/acpi/acpica/evmisc.c index fcaed9fb44ff..8e31bb5a973a 100644 --- a/drivers/acpi/acpica/evmisc.c +++ b/drivers/acpi/acpica/evmisc.c @@ -284,41 +284,41 @@ static void ACPI_SYSTEM_XFACE acpi_ev_notify_dispatch(void *context) * RETURN: ACPI_INTERRUPT_HANDLED * * DESCRIPTION: Invoked directly from the SCI handler when a global lock - * release interrupt occurs. Attempt to acquire the global lock, - * if successful, signal the thread waiting for the lock. + * release interrupt occurs. If there's a thread waiting for + * the global lock, signal it. * * NOTE: Assumes that the semaphore can be signaled from interrupt level. If * this is not possible for some reason, a separate thread will have to be * scheduled to do this. * ******************************************************************************/ +static u8 acpi_ev_global_lock_pending; +static spinlock_t _acpi_ev_global_lock_pending_lock; +#define acpi_ev_global_lock_pending_lock &_acpi_ev_global_lock_pending_lock static u32 acpi_ev_global_lock_handler(void *context) { - u8 acquired = FALSE; + acpi_status status; + acpi_cpu_flags flags; - /* - * Attempt to get the lock. - * - * If we don't get it now, it will be marked pending and we will - * take another interrupt when it becomes free. - */ - ACPI_ACQUIRE_GLOBAL_LOCK(acpi_gbl_FACS, acquired); - if (acquired) { + flags = acpi_os_acquire_lock(acpi_ev_global_lock_pending_lock); - /* Got the lock, now wake all threads waiting for it */ + if (!acpi_ev_global_lock_pending) { + goto out; + } - acpi_gbl_global_lock_acquired = TRUE; - /* Send a unit to the semaphore */ + /* Send a unit to the semaphore */ - if (ACPI_FAILURE - (acpi_os_signal_semaphore - (acpi_gbl_global_lock_semaphore, 1))) { - ACPI_ERROR((AE_INFO, - "Could not signal Global Lock semaphore")); - } + status = acpi_os_signal_semaphore(acpi_gbl_global_lock_semaphore, 1); + if (ACPI_FAILURE(status)) { + ACPI_ERROR((AE_INFO, "Could not signal Global Lock semaphore")); } + acpi_ev_global_lock_pending = FALSE; + + out: + acpi_os_release_lock(acpi_ev_global_lock_pending_lock, flags); + return (ACPI_INTERRUPT_HANDLED); } @@ -415,6 +415,7 @@ static int acpi_ev_global_lock_acquired; acpi_status acpi_ev_acquire_global_lock(u16 timeout) { + acpi_cpu_flags flags; acpi_status status = AE_OK; u8 acquired = FALSE; @@ -467,32 +468,47 @@ acpi_status acpi_ev_acquire_global_lock(u16 timeout) return_ACPI_STATUS(AE_OK); } - /* Attempt to acquire the actual hardware lock */ + flags = acpi_os_acquire_lock(acpi_ev_global_lock_pending_lock); + + do { + + /* Attempt to acquire the actual hardware lock */ + + ACPI_ACQUIRE_GLOBAL_LOCK(acpi_gbl_FACS, acquired); + if (acquired) { + acpi_gbl_global_lock_acquired = TRUE; + + ACPI_DEBUG_PRINT((ACPI_DB_EXEC, + "Acquired hardware Global Lock\n")); + break; + } - ACPI_ACQUIRE_GLOBAL_LOCK(acpi_gbl_FACS, acquired); - if (acquired) { + acpi_ev_global_lock_pending = TRUE; - /* We got the lock */ + acpi_os_release_lock(acpi_ev_global_lock_pending_lock, flags); + /* + * Did not get the lock. The pending bit was set above, and we + * must wait until we get the global lock released interrupt. + */ ACPI_DEBUG_PRINT((ACPI_DB_EXEC, - "Acquired hardware Global Lock\n")); + "Waiting for hardware Global Lock\n")); - acpi_gbl_global_lock_acquired = TRUE; - return_ACPI_STATUS(AE_OK); - } + /* + * Wait for handshake with the global lock interrupt handler. + * This interface releases the interpreter if we must wait. + */ + status = acpi_ex_system_wait_semaphore( + acpi_gbl_global_lock_semaphore, + ACPI_WAIT_FOREVER); - /* - * Did not get the lock. The pending bit was set above, and we must now - * wait until we get the global lock released interrupt. - */ - ACPI_DEBUG_PRINT((ACPI_DB_EXEC, "Waiting for hardware Global Lock\n")); + flags = acpi_os_acquire_lock(acpi_ev_global_lock_pending_lock); - /* - * Wait for handshake with the global lock interrupt handler. - * This interface releases the interpreter if we must wait. - */ - status = acpi_ex_system_wait_semaphore(acpi_gbl_global_lock_semaphore, - ACPI_WAIT_FOREVER); + } while (ACPI_SUCCESS(status)); + + acpi_ev_global_lock_pending = FALSE; + + acpi_os_release_lock(acpi_ev_global_lock_pending_lock, flags); return_ACPI_STATUS(status); } -- cgit v1.2.3 From b014f4f1aad3f25d5c7d877a394869645ea0c96b Mon Sep 17 00:00:00 2001 From: Rafael J. Wysocki Date: Thu, 6 Jan 2011 23:33:30 +0100 Subject: ACPI / PM: Do not enable multiple devices to wake up simultaneously If a device is enabled to wake up the system from sleep states via /proc/acpi/wakeup and there are other devices associated with the same wakeup GPE, all of these devices are automatically enabled to wake up the system. This isn't correct, because the fact the GPE is shared need not imply that wakeup power has to be enabled for all the devices at the same time (i.e. it is possible that one device will have its wakeup power enabled and it will wake up the system from a sleep state if the shared wakeup GPE is enabled, while another device having its wakeup power disabled will not wake up the system even though the GPE is enabled). Rework acpi_system_write_wakeup_device() so that it only enables wakeup for one device at a time. Signed-off-by: Rafael J. Wysocki Signed-off-by: Len Brown --- drivers/acpi/proc.c | 26 +------------------------- 1 file changed, 1 insertion(+), 25 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/proc.c b/drivers/acpi/proc.c index afad67769db6..129effbb7bd4 100644 --- a/drivers/acpi/proc.c +++ b/drivers/acpi/proc.c @@ -341,7 +341,6 @@ acpi_system_write_wakeup_device(struct file *file, char strbuf[5]; char str[5] = ""; unsigned int len = count; - struct acpi_device *found_dev = NULL; if (len > 4) len = 4; @@ -363,33 +362,10 @@ acpi_system_write_wakeup_device(struct file *file, if (!strncmp(dev->pnp.bus_id, str, 4)) { dev->wakeup.state.enabled = dev->wakeup.state.enabled ? 0 : 1; - found_dev = dev; + physical_device_enable_wakeup(dev); break; } } - if (found_dev) { - physical_device_enable_wakeup(found_dev); - list_for_each_safe(node, next, &acpi_wakeup_device_list) { - struct acpi_device *dev = container_of(node, - struct - acpi_device, - wakeup_list); - - if ((dev != found_dev) && - (dev->wakeup.gpe_number == - found_dev->wakeup.gpe_number) - && (dev->wakeup.gpe_device == - found_dev->wakeup.gpe_device)) { - printk(KERN_WARNING - "ACPI: '%s' and '%s' have the same GPE, " - "can't disable/enable one separately\n", - dev->pnp.bus_id, found_dev->pnp.bus_id); - dev->wakeup.state.enabled = - found_dev->wakeup.state.enabled; - physical_device_enable_wakeup(dev); - } - } - } mutex_unlock(&acpi_device_lock); return count; } -- cgit v1.2.3 From f2b56bc808addb908a5bf435d9b942c02af9a7c4 Mon Sep 17 00:00:00 2001 From: Rafael J. Wysocki Date: Thu, 6 Jan 2011 23:34:22 +0100 Subject: ACPI / PM: Use device wakeup flags for handling ACPI wakeup devices There are ACPI devices (buttons and the laptop lid) that can wake up the system from sleep states and have no "physical" companion devices. The ACPI subsystem uses two flags, wakeup.state.enabled and wakeup.flags.always_enabled, for handling those devices, but they are not accessible through the standard device wakeup infrastructure. User space can only control them via the /proc/acpi/wakeup interface that is not really convenient (e.g. the way in which devices are enabled to wake up the system is not portable between different systems, because it requires one to know the devices' "names" used in the system's ACPI tables). To address this problem, use standard device wakeup flags instead of the special ACPI flags for handling those devices. In particular, use device_set_wakeup_capable() to mark the ACPI wakeup devices during initialization and use device_set_wakeup_enable() to allow or disallow them to wake up the system from sleep states. Rework the /proc/acpi/wakeup interface to take these changes into account. Signed-off-by: Rafael J. Wysocki Signed-off-by: Len Brown --- drivers/acpi/button.c | 4 ++-- drivers/acpi/proc.c | 19 +++++++++++++------ drivers/acpi/scan.c | 2 +- drivers/acpi/wakeup.c | 18 ++++++++++-------- 4 files changed, 26 insertions(+), 17 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/button.c b/drivers/acpi/button.c index 71ef9cd0735f..234c104fcdd8 100644 --- a/drivers/acpi/button.c +++ b/drivers/acpi/button.c @@ -426,7 +426,7 @@ static int acpi_button_add(struct acpi_device *device) acpi_enable_gpe(device->wakeup.gpe_device, device->wakeup.gpe_number); device->wakeup.run_wake_count++; - device->wakeup.state.enabled = 1; + device_set_wakeup_enable(&device->dev, true); } printk(KERN_INFO PREFIX "%s [%s]\n", name, acpi_device_bid(device)); @@ -449,7 +449,7 @@ static int acpi_button_remove(struct acpi_device *device, int type) acpi_disable_gpe(device->wakeup.gpe_device, device->wakeup.gpe_number); device->wakeup.run_wake_count--; - device->wakeup.state.enabled = 0; + device_set_wakeup_enable(&device->dev, false); } acpi_button_remove_fs(device); diff --git a/drivers/acpi/proc.c b/drivers/acpi/proc.c index 129effbb7bd4..f5f986991b52 100644 --- a/drivers/acpi/proc.c +++ b/drivers/acpi/proc.c @@ -311,7 +311,9 @@ acpi_system_wakeup_device_seq_show(struct seq_file *seq, void *offset) dev->pnp.bus_id, (u32) dev->wakeup.sleep_state, dev->wakeup.flags.run_wake ? '*' : ' ', - dev->wakeup.state.enabled ? "enabled" : "disabled"); + (device_may_wakeup(&dev->dev) + || (ldev && device_may_wakeup(ldev))) ? + "enabled" : "disabled"); if (ldev) seq_printf(seq, "%s:%s", ldev->bus ? ldev->bus->name : "no-bus", @@ -328,8 +330,10 @@ static void physical_device_enable_wakeup(struct acpi_device *adev) { struct device *dev = acpi_get_physical_device(adev->handle); - if (dev && device_can_wakeup(dev)) - device_set_wakeup_enable(dev, adev->wakeup.state.enabled); + if (dev && device_can_wakeup(dev)) { + bool enable = !device_may_wakeup(dev); + device_set_wakeup_enable(dev, enable); + } } static ssize_t @@ -360,9 +364,12 @@ acpi_system_write_wakeup_device(struct file *file, continue; if (!strncmp(dev->pnp.bus_id, str, 4)) { - dev->wakeup.state.enabled = - dev->wakeup.state.enabled ? 0 : 1; - physical_device_enable_wakeup(dev); + if (device_can_wakeup(&dev->dev)) { + bool enable = !device_may_wakeup(&dev->dev); + device_set_wakeup_enable(&dev->dev, enable); + } else { + physical_device_enable_wakeup(dev); + } break; } } diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 29ef505c487b..bf7acbff1f5f 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -803,7 +803,7 @@ static void acpi_bus_set_run_wake_flags(struct acpi_device *device) /* Power button, Lid switch always enable wakeup */ if (!acpi_match_device_ids(device, button_device_ids)) { device->wakeup.flags.run_wake = 1; - device->wakeup.flags.always_enabled = 1; + device_set_wakeup_capable(&device->dev, true); return; } diff --git a/drivers/acpi/wakeup.c b/drivers/acpi/wakeup.c index f62a50c3ed34..f252d0de9922 100644 --- a/drivers/acpi/wakeup.c +++ b/drivers/acpi/wakeup.c @@ -37,11 +37,12 @@ void acpi_enable_wakeup_devices(u8 sleep_state) container_of(node, struct acpi_device, wakeup_list); if (!dev->wakeup.flags.valid - || !(dev->wakeup.state.enabled || dev->wakeup.prepare_count) - || sleep_state > (u32) dev->wakeup.sleep_state) + || sleep_state > (u32) dev->wakeup.sleep_state + || !(device_may_wakeup(&dev->dev) + || dev->wakeup.prepare_count)) continue; - if (dev->wakeup.state.enabled) + if (device_may_wakeup(&dev->dev)) acpi_enable_wakeup_device_power(dev, sleep_state); /* The wake-up power should have been enabled already. */ @@ -63,14 +64,15 @@ void acpi_disable_wakeup_devices(u8 sleep_state) container_of(node, struct acpi_device, wakeup_list); if (!dev->wakeup.flags.valid - || !(dev->wakeup.state.enabled || dev->wakeup.prepare_count) - || (sleep_state > (u32) dev->wakeup.sleep_state)) + || sleep_state > (u32) dev->wakeup.sleep_state + || !(device_may_wakeup(&dev->dev) + || dev->wakeup.prepare_count)) continue; acpi_gpe_wakeup(dev->wakeup.gpe_device, dev->wakeup.gpe_number, ACPI_GPE_DISABLE); - if (dev->wakeup.state.enabled) + if (device_may_wakeup(&dev->dev)) acpi_disable_wakeup_device_power(dev); } } @@ -84,8 +86,8 @@ int __init acpi_wakeup_device_init(void) struct acpi_device *dev = container_of(node, struct acpi_device, wakeup_list); - if (dev->wakeup.flags.always_enabled) - dev->wakeup.state.enabled = 1; + if (device_can_wakeup(&dev->dev)) + device_set_wakeup_enable(&dev->dev, true); } mutex_unlock(&acpi_device_lock); return 0; -- cgit v1.2.3 From 7fa69baf29de8c77a6b32c054df2abb8f11f8aa4 Mon Sep 17 00:00:00 2001 From: Rafael J. Wysocki Date: Thu, 6 Jan 2011 23:35:10 +0100 Subject: ACPI / PM: Drop special ACPI wakeup flags Drop special ACPI wakeup flags, wakeup.state.enabled and wakeup.flags.always_enabled, that aren't necessary any more after we've started to use standard device wakeup flags for handling ACPI wakeup devices. Signed-off-by: Rafael J. Wysocki Signed-off-by: Len Brown --- drivers/acpi/glue.c | 5 +---- include/acpi/acpi_bus.h | 6 ------ 2 files changed, 1 insertion(+), 10 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/glue.c b/drivers/acpi/glue.c index 78b0164c35b2..7c47ed55e528 100644 --- a/drivers/acpi/glue.c +++ b/drivers/acpi/glue.c @@ -167,11 +167,8 @@ static int acpi_bind_one(struct device *dev, acpi_handle handle) "firmware_node"); ret = sysfs_create_link(&acpi_dev->dev.kobj, &dev->kobj, "physical_node"); - if (acpi_dev->wakeup.flags.valid) { + if (acpi_dev->wakeup.flags.valid) device_set_wakeup_capable(dev, true); - device_set_wakeup_enable(dev, - acpi_dev->wakeup.state.enabled); - } } return 0; diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index 359ef11725a6..f2499f572109 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -242,20 +242,14 @@ struct acpi_device_perf { struct acpi_device_wakeup_flags { u8 valid:1; /* Can successfully enable wakeup? */ u8 run_wake:1; /* Run-Wake GPE devices */ - u8 always_enabled:1; /* Run-wake devices that are always enabled */ u8 notifier_present:1; /* Wake-up notify handler has been installed */ }; -struct acpi_device_wakeup_state { - u8 enabled:1; -}; - struct acpi_device_wakeup { acpi_handle gpe_device; u64 gpe_number; u64 sleep_state; struct acpi_handle_list resources; - struct acpi_device_wakeup_state state; struct acpi_device_wakeup_flags flags; int prepare_count; int run_wake_count; -- cgit v1.2.3 From 1f83511bd8f44b8a9e2d82263b2c95f26a625fcc Mon Sep 17 00:00:00 2001 From: Rafael J. Wysocki Date: Thu, 6 Jan 2011 23:36:01 +0100 Subject: ACPI / PM: Report wakeup events from buttons Since ACPI buttons and lids can be configured to wake up the system from sleep states, report wakeup events from these devices. Signed-off-by: Rafael J. Wysocki Signed-off-by: Len Brown --- drivers/acpi/button.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers/acpi') diff --git a/drivers/acpi/button.c b/drivers/acpi/button.c index 234c104fcdd8..76bbb78a5ad9 100644 --- a/drivers/acpi/button.c +++ b/drivers/acpi/button.c @@ -279,6 +279,9 @@ static int acpi_lid_send_state(struct acpi_device *device) input_report_switch(button->input, SW_LID, !state); input_sync(button->input); + if (state) + pm_wakeup_event(&device->dev, 0); + ret = blocking_notifier_call_chain(&acpi_lid_notifier, state, device); if (ret == NOTIFY_DONE) ret = blocking_notifier_call_chain(&acpi_lid_notifier, state, @@ -314,6 +317,8 @@ static void acpi_button_notify(struct acpi_device *device, u32 event) input_sync(input); input_report_key(input, keycode, 0); input_sync(input); + + pm_wakeup_event(&device->dev, 0); } acpi_bus_generate_proc_event(device, event, ++button->pushed); -- cgit v1.2.3 From 7b330707dddab1ad772898c1c82516342a551173 Mon Sep 17 00:00:00 2001 From: Rafael J. Wysocki Date: Thu, 6 Jan 2011 23:37:01 +0100 Subject: ACPI / PM: Blacklist Averatec machine known to require acpi_sleep=nonvs Apparently, Averatec AV1020-ED2 does not resume correctly without acpi_sleep=nonvs, so add it to the ACPI sleep blacklist. References: https://bugzilla.kernel.org/show_bug.cgi?id=16396#c86 Signed-off-by: Rafael J. Wysocki Signed-off-by: Len Brown --- drivers/acpi/sleep.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'drivers/acpi') diff --git a/drivers/acpi/sleep.c b/drivers/acpi/sleep.c index febb153b5a68..ddc5cce508a1 100644 --- a/drivers/acpi/sleep.c +++ b/drivers/acpi/sleep.c @@ -435,6 +435,14 @@ static struct dmi_system_id __initdata acpisleep_dmi_table[] = { DMI_MATCH(DMI_PRODUCT_NAME, "VGN-NW130D"), }, }, + { + .callback = init_nvs_nosave, + .ident = "Averatec AV1020-ED2", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "AVERATEC"), + DMI_MATCH(DMI_PRODUCT_NAME, "1000 Series"), + }, + }, {}, }; #endif /* CONFIG_SUSPEND */ -- cgit v1.2.3 From daef1f35ea1e2cca125eecd5f078f40b55eb9105 Mon Sep 17 00:00:00 2001 From: Zhao Yakui Date: Mon, 10 Jan 2011 16:35:44 +0800 Subject: ACPI: Check the returned value of set_cpus_allowed_ptr before T-state operation Now before it executes the T-state operation on one CPU, it will try to migrate to the target CPU. Especially this is required on the system that uses the MSR_IA32_THERMAL_CONTROL register to switch T-state. But unfortunately it doesn't check whether the migration is successful or not. In such case we will get/set the incorrect T-state on the offline CPU as it fails in the migration to the offline CPU. Signed-off-by: Zhao Yakui Signed-off-by: Len Brown --- drivers/acpi/processor_throttling.c | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/processor_throttling.c b/drivers/acpi/processor_throttling.c index ff3632717c51..ffc859c61393 100644 --- a/drivers/acpi/processor_throttling.c +++ b/drivers/acpi/processor_throttling.c @@ -876,7 +876,11 @@ static int acpi_processor_get_throttling(struct acpi_processor *pr) */ cpumask_copy(saved_mask, ¤t->cpus_allowed); /* FIXME: use work_on_cpu() */ - set_cpus_allowed_ptr(current, cpumask_of(pr->id)); + if (set_cpus_allowed_ptr(current, cpumask_of(pr->id))) { + /* Can't migrate to the target pr->id CPU. Exit */ + free_cpumask_var(saved_mask); + return -ENODEV; + } ret = pr->throttling.acpi_processor_get_throttling(pr); /* restore the previous state */ set_cpus_allowed_ptr(current, saved_mask); @@ -1051,6 +1055,14 @@ int acpi_processor_set_throttling(struct acpi_processor *pr, return -ENOMEM; } + if (cpu_is_offline(pr->id)) { + /* + * the cpu pointed by pr->id is offline. Unnecessary to change + * the throttling state any more. + */ + return -ENODEV; + } + cpumask_copy(saved_mask, ¤t->cpus_allowed); t_state.target_state = state; p_throttling = &(pr->throttling); @@ -1074,7 +1086,11 @@ int acpi_processor_set_throttling(struct acpi_processor *pr, */ if (p_throttling->shared_type == DOMAIN_COORD_TYPE_SW_ANY) { /* FIXME: use work_on_cpu() */ - set_cpus_allowed_ptr(current, cpumask_of(pr->id)); + if (set_cpus_allowed_ptr(current, cpumask_of(pr->id))) { + /* Can't migrate to the pr->id CPU. Exit */ + ret = -ENODEV; + goto exit; + } ret = p_throttling->acpi_processor_set_throttling(pr, t_state.target_state, force); } else { @@ -1106,7 +1122,8 @@ int acpi_processor_set_throttling(struct acpi_processor *pr, } t_state.cpu = i; /* FIXME: use work_on_cpu() */ - set_cpus_allowed_ptr(current, cpumask_of(i)); + if (set_cpus_allowed_ptr(current, cpumask_of(i))) + continue; ret = match_pr->throttling. acpi_processor_set_throttling( match_pr, t_state.target_state, force); @@ -1126,6 +1143,7 @@ int acpi_processor_set_throttling(struct acpi_processor *pr, /* restore the previous state */ /* FIXME: use work_on_cpu() */ set_cpus_allowed_ptr(current, saved_mask); +exit: free_cpumask_var(online_throttling_cpus); free_cpumask_var(saved_mask); return ret; -- cgit v1.2.3 From 5a344a505093dd65f82f338ffdb7208321b3630e Mon Sep 17 00:00:00 2001 From: Zhao Yakui Date: Mon, 10 Jan 2011 16:35:45 +0800 Subject: ACPI: Reevaluate whether the T-state is supported or not after cpu is online/offline After one CPU is offlined, it is unnecessary to switch T-state for it. So it will be better that the throttling is disabled after the cpu is offline. At the same time after one cpu is online, we should check whether the T-state is supported and then set the corresponding T-state flag. Signed-off-by: Zhao Yakui Signed-off-by: Len Brown --- drivers/acpi/processor_driver.c | 5 ++++ drivers/acpi/processor_throttling.c | 52 +++++++++++++++++++++++++++++++++++++ include/acpi/processor.h | 6 +++++ 3 files changed, 63 insertions(+) (limited to 'drivers/acpi') diff --git a/drivers/acpi/processor_driver.c b/drivers/acpi/processor_driver.c index 85e48047d7b0..c8a0ca2af839 100644 --- a/drivers/acpi/processor_driver.c +++ b/drivers/acpi/processor_driver.c @@ -478,8 +478,13 @@ static int acpi_cpu_soft_notify(struct notifier_block *nfb, if (action == CPU_ONLINE && pr) { acpi_processor_ppc_has_changed(pr, 0); acpi_processor_cst_has_changed(pr); + acpi_processor_reevaluate_tstate(pr, action); acpi_processor_tstate_has_changed(pr); } + if (action == CPU_DEAD && pr) { + /* invalidate the flag.throttling after one CPU is offline */ + acpi_processor_reevaluate_tstate(pr, action); + } return NOTIFY_OK; } diff --git a/drivers/acpi/processor_throttling.c b/drivers/acpi/processor_throttling.c index ffc859c61393..4305d560b065 100644 --- a/drivers/acpi/processor_throttling.c +++ b/drivers/acpi/processor_throttling.c @@ -369,6 +369,58 @@ int acpi_processor_tstate_has_changed(struct acpi_processor *pr) return acpi_processor_set_throttling(pr, target_state, false); } +/* + * This function is used to reevaluate whether the T-state is valid + * after one CPU is onlined/offlined. + * It is noted that it won't reevaluate the following properties for + * the T-state. + * 1. Control method. + * 2. the number of supported T-state + * 3. TSD domain + */ +void acpi_processor_reevaluate_tstate(struct acpi_processor *pr, + unsigned long action) +{ + int result = 0; + + if (action == CPU_DEAD) { + /* When one CPU is offline, the T-state throttling + * will be invalidated. + */ + pr->flags.throttling = 0; + return; + } + /* the following is to recheck whether the T-state is valid for + * the online CPU + */ + if (!pr->throttling.state_count) { + /* If the number of T-state is invalid, it is + * invalidated. + */ + pr->flags.throttling = 0; + return; + } + pr->flags.throttling = 1; + + /* Disable throttling (if enabled). We'll let subsequent + * policy (e.g.thermal) decide to lower performance if it + * so chooses, but for now we'll crank up the speed. + */ + + result = acpi_processor_get_throttling(pr); + if (result) + goto end; + + if (pr->throttling.state) { + result = acpi_processor_set_throttling(pr, 0, false); + if (result) + goto end; + } + +end: + if (result) + pr->flags.throttling = 0; +} /* * _PTC - Processor Throttling Control (and status) register location */ diff --git a/include/acpi/processor.h b/include/acpi/processor.h index 1b62102fbb67..55192ac0cede 100644 --- a/include/acpi/processor.h +++ b/include/acpi/processor.h @@ -324,6 +324,12 @@ int acpi_processor_tstate_has_changed(struct acpi_processor *pr); int acpi_processor_get_throttling_info(struct acpi_processor *pr); extern int acpi_processor_set_throttling(struct acpi_processor *pr, int state, bool force); +/* + * Reevaluate whether the T-state is invalid after one cpu is + * onlined/offlined. In such case the flags.throttling will be updated. + */ +extern void acpi_processor_reevaluate_tstate(struct acpi_processor *pr, + unsigned long action); extern const struct file_operations acpi_processor_throttling_fops; extern void acpi_processor_throttling_init(void); /* in processor_idle.c */ -- cgit v1.2.3 From 4464ed3b05de7a41ae55c74109cec8aeb138ce14 Mon Sep 17 00:00:00 2001 From: Zhang Rui Date: Mon, 10 Jan 2011 11:16:26 +0800 Subject: ACPI: update CONFIG_ACPI_PROCFS description Update CONFIG_ACPI_PROCFS description because the processor, video and thermal zone procfs I/F have been removed. Some ACPI drivers, e.g. button, have their procfs I/F always built in, because we don't have sysfs I/F replacement at the moment. But once we finish developing the sysfs I/F for these driver, we need CONFIG_ACPI_PROCFS to enabled/disable the corresponding procfs I/F. So just updating the description rather than removing this option, although there is no procfs I/F depends on it for now. Signed-off-by: Zhang Rui Signed-off-by: Len Brown --- drivers/acpi/Kconfig | 5 ----- 1 file changed, 5 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index a0c03658b2ea..ad370a1fe2e4 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig @@ -51,12 +51,7 @@ config ACPI_PROCFS For backwards compatibility, this option allows deprecated /proc/acpi/ files to exist, even when they have been replaced by functions in /sys. - The deprecated files (and their replacements) include: - /proc/acpi/processor/*/throttling (/sys/class/thermal/ - cooling_device*/*) - /proc/acpi/video/*/brightness (/sys/class/backlight/) - /proc/acpi/thermal_zone/*/* (/sys/class/thermal/) This option has no effect on /proc/acpi/ files and functions which do not yet exist in /sys. -- cgit v1.2.3 From 6d855fcdd24d2491455527c4999b4d04363f1980 Mon Sep 17 00:00:00 2001 From: Zhang Rui Date: Mon, 10 Jan 2011 11:16:30 +0800 Subject: ACPI: delete CONFIG_ACPI_PROCFS_POWER and power procfs I/F in 2.6.39 sysfs I/F for ACPI power devices, including AC and Battery, has been working in upstream kenrel since 2.6.24, Sep 2007. In 2.6.37, we made the sysfs I/F always built in and this option disabled by default. Now, we plan to remove this option and the ACPI power procfs interface in 2.6.39. First, update the feature-removal-schedule to announce this change. Second, add runtime warnings in ACPI AC/Battery/SBS driver, so that users will notice this change even if "make oldconfig" is used. Signed-off-by: Zhang Rui Signed-off-by: Len Brown --- Documentation/feature-removal-schedule.txt | 11 +++++++++++ drivers/acpi/Kconfig | 2 ++ drivers/acpi/ac.c | 3 ++- drivers/acpi/battery.c | 2 ++ drivers/acpi/sbs.c | 2 ++ 5 files changed, 19 insertions(+), 1 deletion(-) (limited to 'drivers/acpi') diff --git a/Documentation/feature-removal-schedule.txt b/Documentation/feature-removal-schedule.txt index 6c2f55e05f13..f281532a15ce 100644 --- a/Documentation/feature-removal-schedule.txt +++ b/Documentation/feature-removal-schedule.txt @@ -232,6 +232,17 @@ Who: Zhang Rui --------------------------- +What: CONFIG_ACPI_PROCFS_POWER +When: 2.6.39 +Why: sysfs I/F for ACPI power devices, including AC and Battery, + has been working in upstream kenrel since 2.6.24, Sep 2007. + In 2.6.37, we make the sysfs I/F always built in and this option + disabled by default. + Remove this option and the ACPI power procfs interface in 2.6.39. +Who: Zhang Rui + +--------------------------- + What: /proc/acpi/button When: August 2007 Why: /proc/acpi/button has been replaced by events to the input layer diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index 5959077be0a4..788e88eb18ec 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig @@ -70,6 +70,8 @@ config ACPI_PROCFS_POWER /proc/acpi/ac_adapter/* (sys/class/power_supply/*) This option has no effect on /proc/acpi/ directories and functions, which do not yet exist in /sys + This option, together with the proc directories, will be + deleted in 2.6.39. Say N to delete power /proc/acpi/ directories that have moved to /sys/ diff --git a/drivers/acpi/ac.c b/drivers/acpi/ac.c index ba9afeaa23ac..f441e92481f6 100644 --- a/drivers/acpi/ac.c +++ b/drivers/acpi/ac.c @@ -185,7 +185,8 @@ static int acpi_ac_add_fs(struct acpi_device *device) { struct proc_dir_entry *entry = NULL; - + printk(KERN_WARNING PREFIX "Deprecated procfs I/F for AC is loaded," + " please retry with CONFIG_ACPI_PROCFS_POWER cleared\n"); if (!acpi_device_dir(device)) { acpi_device_dir(device) = proc_mkdir(acpi_device_bid(device), acpi_ac_dir); diff --git a/drivers/acpi/battery.c b/drivers/acpi/battery.c index 95649d373071..2a31421e0d75 100644 --- a/drivers/acpi/battery.c +++ b/drivers/acpi/battery.c @@ -868,6 +868,8 @@ static int acpi_battery_add_fs(struct acpi_device *device) struct proc_dir_entry *entry = NULL; int i; + printk(KERN_WARNING PREFIX "Deprecated procfs I/F for battery is loaded," + " please retry with CONFIG_ACPI_PROCFS_POWER cleared\n"); if (!acpi_device_dir(device)) { acpi_device_dir(device) = proc_mkdir(acpi_device_bid(device), acpi_battery_dir); diff --git a/drivers/acpi/sbs.c b/drivers/acpi/sbs.c index e5dbedb16bbf..51ae3794ec7f 100644 --- a/drivers/acpi/sbs.c +++ b/drivers/acpi/sbs.c @@ -484,6 +484,8 @@ acpi_sbs_add_fs(struct proc_dir_entry **dir, const struct file_operations *state_fops, const struct file_operations *alarm_fops, void *data) { + printk(KERN_WARNING PREFIX "Deprecated procfs I/F for SBS is loaded," + " please retry with CONFIG_ACPI_PROCFS_POWER cleared\n"); if (!*dir) { *dir = proc_mkdir(dir_name, parent_dir); if (!*dir) { -- cgit v1.2.3 From 81e88fdc432a1552401d6e91a984dcccce72b8dc Mon Sep 17 00:00:00 2001 From: Huang Ying Date: Wed, 12 Jan 2011 14:44:55 +0800 Subject: ACPI, APEI, Generic Hardware Error Source POLL/IRQ/NMI notification type support Generic Hardware Error Source provides a way to report platform hardware errors (such as that from chipset). It works in so called "Firmware First" mode, that is, hardware errors are reported to firmware firstly, then reported to Linux by firmware. This way, some non-standard hardware error registers or non-standard hardware link can be checked by firmware to produce more valuable hardware error information for Linux. This patch adds POLL/IRQ/NMI notification types support. Because the memory area used to transfer hardware error information from BIOS to Linux can be determined only in NMI, IRQ or timer handler, but general ioremap can not be used in atomic context, so a special version of atomic ioremap is implemented for that. Known issue: - Error information can not be printed for recoverable errors notified via NMI, because printk is not NMI-safe. Will fix this via delay printing to IRQ context via irq_work or make printk NMI-safe. v2: - adjust printk format per comments. Signed-off-by: Huang Ying Reviewed-by: Andi Kleen Signed-off-by: Len Brown --- arch/x86/kernel/acpi/boot.c | 1 + arch/x86/kernel/dumpstack.c | 1 + drivers/acpi/apei/ghes.c | 406 +++++++++++++++++++++++++++++++++++--------- kernel/panic.c | 1 + lib/ioremap.c | 2 + mm/vmalloc.c | 1 + 6 files changed, 328 insertions(+), 84 deletions(-) (limited to 'drivers/acpi') diff --git a/arch/x86/kernel/acpi/boot.c b/arch/x86/kernel/acpi/boot.c index 71232b941b6c..c2d0baa48d8c 100644 --- a/arch/x86/kernel/acpi/boot.c +++ b/arch/x86/kernel/acpi/boot.c @@ -504,6 +504,7 @@ int acpi_gsi_to_irq(u32 gsi, unsigned int *irq) return 0; } +EXPORT_SYMBOL_GPL(acpi_gsi_to_irq); int acpi_isa_irq_to_gsi(unsigned isa_irq, u32 *gsi) { diff --git a/arch/x86/kernel/dumpstack.c b/arch/x86/kernel/dumpstack.c index 6e8752c1bd52..d34cf80ec402 100644 --- a/arch/x86/kernel/dumpstack.c +++ b/arch/x86/kernel/dumpstack.c @@ -240,6 +240,7 @@ unsigned __kprobes long oops_begin(void) bust_spinlocks(1); return flags; } +EXPORT_SYMBOL_GPL(oops_begin); void __kprobes oops_end(unsigned long flags, struct pt_regs *regs, int signr) { diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c index 51905d07a4d9..d1d484d4a06a 100644 --- a/drivers/acpi/apei/ghes.c +++ b/drivers/acpi/apei/ghes.c @@ -12,10 +12,6 @@ * For more information about Generic Hardware Error Source, please * refer to ACPI Specification version 4.0, section 17.3.2.6 * - * Now, only SCI notification type and memory errors are - * supported. More notification type and hardware error type will be - * added later. - * * Copyright 2010 Intel Corp. * Author: Huang Ying * @@ -39,15 +35,18 @@ #include #include #include +#include #include #include #include #include #include +#include #include #include #include #include +#include #include "apei-internal.h" @@ -56,42 +55,131 @@ #define GHES_ESTATUS_MAX_SIZE 65536 /* - * One struct ghes is created for each generic hardware error - * source. - * + * One struct ghes is created for each generic hardware error source. * It provides the context for APEI hardware error timer/IRQ/SCI/NMI - * handler. Handler for one generic hardware error source is only - * triggered after the previous one is done. So handler can uses - * struct ghes without locking. + * handler. * * estatus: memory buffer for error status block, allocated during * HEST parsing. */ #define GHES_TO_CLEAR 0x0001 +#define GHES_EXITING 0x0002 struct ghes { struct acpi_hest_generic *generic; struct acpi_hest_generic_status *estatus; - struct list_head list; u64 buffer_paddr; unsigned long flags; + union { + struct list_head list; + struct timer_list timer; + unsigned int irq; + }; }; +static int ghes_panic_timeout __read_mostly = 30; + /* - * Error source lists, one list for each notification method. The - * members in lists are struct ghes. + * All error sources notified with SCI shares one notifier function, + * so they need to be linked and checked one by one. This is applied + * to NMI too. * - * The list members are only added in HEST parsing and deleted during - * module_exit, that is, single-threaded. So no lock is needed for - * that. - * - * But the mutual exclusion is needed between members adding/deleting - * and timer/IRQ/SCI/NMI handler, which may traverse the list. RCU is - * used for that. + * RCU is used for these lists, so ghes_list_mutex is only used for + * list changing, not for traversing. */ static LIST_HEAD(ghes_sci); +static LIST_HEAD(ghes_nmi); static DEFINE_MUTEX(ghes_list_mutex); +/* + * NMI may be triggered on any CPU, so ghes_nmi_lock is used for + * mutual exclusion. + */ +static DEFINE_RAW_SPINLOCK(ghes_nmi_lock); + +/* + * Because the memory area used to transfer hardware error information + * from BIOS to Linux can be determined only in NMI, IRQ or timer + * handler, but general ioremap can not be used in atomic context, so + * a special version of atomic ioremap is implemented for that. + */ + +/* + * Two virtual pages are used, one for NMI context, the other for + * IRQ/PROCESS context + */ +#define GHES_IOREMAP_PAGES 2 +#define GHES_IOREMAP_NMI_PAGE(base) (base) +#define GHES_IOREMAP_IRQ_PAGE(base) ((base) + PAGE_SIZE) + +/* virtual memory area for atomic ioremap */ +static struct vm_struct *ghes_ioremap_area; +/* + * These 2 spinlock is used to prevent atomic ioremap virtual memory + * area from being mapped simultaneously. + */ +static DEFINE_RAW_SPINLOCK(ghes_ioremap_lock_nmi); +static DEFINE_SPINLOCK(ghes_ioremap_lock_irq); + +static int ghes_ioremap_init(void) +{ + ghes_ioremap_area = __get_vm_area(PAGE_SIZE * GHES_IOREMAP_PAGES, + VM_IOREMAP, VMALLOC_START, VMALLOC_END); + if (!ghes_ioremap_area) { + pr_err(GHES_PFX "Failed to allocate virtual memory area for atomic ioremap.\n"); + return -ENOMEM; + } + + return 0; +} + +static void ghes_ioremap_exit(void) +{ + free_vm_area(ghes_ioremap_area); +} + +static void __iomem *ghes_ioremap_pfn_nmi(u64 pfn) +{ + unsigned long vaddr; + + vaddr = (unsigned long)GHES_IOREMAP_NMI_PAGE(ghes_ioremap_area->addr); + ioremap_page_range(vaddr, vaddr + PAGE_SIZE, + pfn << PAGE_SHIFT, PAGE_KERNEL); + + return (void __iomem *)vaddr; +} + +static void __iomem *ghes_ioremap_pfn_irq(u64 pfn) +{ + unsigned long vaddr; + + vaddr = (unsigned long)GHES_IOREMAP_IRQ_PAGE(ghes_ioremap_area->addr); + ioremap_page_range(vaddr, vaddr + PAGE_SIZE, + pfn << PAGE_SHIFT, PAGE_KERNEL); + + return (void __iomem *)vaddr; +} + +static void ghes_iounmap_nmi(void __iomem *vaddr_ptr) +{ + unsigned long vaddr = (unsigned long __force)vaddr_ptr; + void *base = ghes_ioremap_area->addr; + + BUG_ON(vaddr != (unsigned long)GHES_IOREMAP_NMI_PAGE(base)); + unmap_kernel_range_noflush(vaddr, PAGE_SIZE); + __flush_tlb_one(vaddr); +} + +static void ghes_iounmap_irq(void __iomem *vaddr_ptr) +{ + unsigned long vaddr = (unsigned long __force)vaddr_ptr; + void *base = ghes_ioremap_area->addr; + + BUG_ON(vaddr != (unsigned long)GHES_IOREMAP_IRQ_PAGE(base)); + unmap_kernel_range_noflush(vaddr, PAGE_SIZE); + __flush_tlb_one(vaddr); +} + static struct ghes *ghes_new(struct acpi_hest_generic *generic) { struct ghes *ghes; @@ -102,7 +190,6 @@ static struct ghes *ghes_new(struct acpi_hest_generic *generic) if (!ghes) return ERR_PTR(-ENOMEM); ghes->generic = generic; - INIT_LIST_HEAD(&ghes->list); rc = acpi_pre_map_gar(&generic->error_status_address); if (rc) goto err_free; @@ -159,22 +246,41 @@ static inline int ghes_severity(int severity) } } -/* SCI handler run in work queue, so ioremap can be used here */ -static int ghes_copy_tofrom_phys(void *buffer, u64 paddr, u32 len, - int from_phys) +static void ghes_copy_tofrom_phys(void *buffer, u64 paddr, u32 len, + int from_phys) { - void *vaddr; - - vaddr = ioremap_cache(paddr, len); - if (!vaddr) - return -ENOMEM; - if (from_phys) - memcpy(buffer, vaddr, len); - else - memcpy(vaddr, buffer, len); - iounmap(vaddr); - - return 0; + void __iomem *vaddr; + unsigned long flags = 0; + int in_nmi = in_nmi(); + u64 offset; + u32 trunk; + + while (len > 0) { + offset = paddr - (paddr & PAGE_MASK); + if (in_nmi) { + raw_spin_lock(&ghes_ioremap_lock_nmi); + vaddr = ghes_ioremap_pfn_nmi(paddr >> PAGE_SHIFT); + } else { + spin_lock_irqsave(&ghes_ioremap_lock_irq, flags); + vaddr = ghes_ioremap_pfn_irq(paddr >> PAGE_SHIFT); + } + trunk = PAGE_SIZE - offset; + trunk = min(trunk, len); + if (from_phys) + memcpy_fromio(buffer, vaddr + offset, trunk); + else + memcpy_toio(vaddr + offset, buffer, trunk); + len -= trunk; + paddr += trunk; + buffer += trunk; + if (in_nmi) { + ghes_iounmap_nmi(vaddr); + raw_spin_unlock(&ghes_ioremap_lock_nmi); + } else { + ghes_iounmap_irq(vaddr); + spin_unlock_irqrestore(&ghes_ioremap_lock_irq, flags); + } + } } static int ghes_read_estatus(struct ghes *ghes, int silent) @@ -195,10 +301,8 @@ static int ghes_read_estatus(struct ghes *ghes, int silent) if (!buf_paddr) return -ENOENT; - rc = ghes_copy_tofrom_phys(ghes->estatus, buf_paddr, - sizeof(*ghes->estatus), 1); - if (rc) - return rc; + ghes_copy_tofrom_phys(ghes->estatus, buf_paddr, + sizeof(*ghes->estatus), 1); if (!ghes->estatus->block_status) return -ENOENT; @@ -213,17 +317,15 @@ static int ghes_read_estatus(struct ghes *ghes, int silent) goto err_read_block; if (apei_estatus_check_header(ghes->estatus)) goto err_read_block; - rc = ghes_copy_tofrom_phys(ghes->estatus + 1, - buf_paddr + sizeof(*ghes->estatus), - len - sizeof(*ghes->estatus), 1); - if (rc) - return rc; + ghes_copy_tofrom_phys(ghes->estatus + 1, + buf_paddr + sizeof(*ghes->estatus), + len - sizeof(*ghes->estatus), 1); if (apei_estatus_check(ghes->estatus)) goto err_read_block; rc = 0; err_read_block: - if (rc && !silent) + if (rc && !silent && printk_ratelimit()) pr_warning(FW_WARN GHES_PFX "Failed to read error status block!\n"); return rc; @@ -293,6 +395,42 @@ out: return 0; } +static void ghes_add_timer(struct ghes *ghes) +{ + struct acpi_hest_generic *g = ghes->generic; + unsigned long expire; + + if (!g->notify.poll_interval) { + pr_warning(FW_WARN GHES_PFX "Poll interval is 0 for generic hardware error source: %d, disabled.\n", + g->header.source_id); + return; + } + expire = jiffies + msecs_to_jiffies(g->notify.poll_interval); + ghes->timer.expires = round_jiffies_relative(expire); + add_timer(&ghes->timer); +} + +static void ghes_poll_func(unsigned long data) +{ + struct ghes *ghes = (void *)data; + + ghes_proc(ghes); + if (!(ghes->flags & GHES_EXITING)) + ghes_add_timer(ghes); +} + +static irqreturn_t ghes_irq_func(int irq, void *data) +{ + struct ghes *ghes = data; + int rc; + + rc = ghes_proc(ghes); + if (rc) + return IRQ_NONE; + + return IRQ_HANDLED; +} + static int ghes_notify_sci(struct notifier_block *this, unsigned long event, void *data) { @@ -309,10 +447,63 @@ static int ghes_notify_sci(struct notifier_block *this, return ret; } +static int ghes_notify_nmi(struct notifier_block *this, + unsigned long cmd, void *data) +{ + struct ghes *ghes, *ghes_global = NULL; + int sev, sev_global = -1; + int ret = NOTIFY_DONE; + + if (cmd != DIE_NMI) + return ret; + + raw_spin_lock(&ghes_nmi_lock); + list_for_each_entry_rcu(ghes, &ghes_nmi, list) { + if (ghes_read_estatus(ghes, 1)) { + ghes_clear_estatus(ghes); + continue; + } + sev = ghes_severity(ghes->estatus->error_severity); + if (sev > sev_global) { + sev_global = sev; + ghes_global = ghes; + } + ret = NOTIFY_STOP; + } + + if (ret == NOTIFY_DONE) + goto out; + + if (sev_global >= GHES_SEV_PANIC) { + oops_begin(); + ghes_print_estatus(KERN_EMERG HW_ERR, ghes_global); + /* reboot to log the error! */ + if (panic_timeout == 0) + panic_timeout = ghes_panic_timeout; + panic("Fatal hardware error!"); + } + + list_for_each_entry_rcu(ghes, &ghes_nmi, list) { + if (!(ghes->flags & GHES_TO_CLEAR)) + continue; + /* Do not print estatus because printk is not NMI safe */ + ghes_do_proc(ghes); + ghes_clear_estatus(ghes); + } + +out: + raw_spin_unlock(&ghes_nmi_lock); + return ret; +} + static struct notifier_block ghes_notifier_sci = { .notifier_call = ghes_notify_sci, }; +static struct notifier_block ghes_notifier_nmi = { + .notifier_call = ghes_notify_nmi, +}; + static int __devinit ghes_probe(struct platform_device *ghes_dev) { struct acpi_hest_generic *generic; @@ -323,18 +514,27 @@ static int __devinit ghes_probe(struct platform_device *ghes_dev) if (!generic->enabled) return -ENODEV; - if (generic->error_block_length < - sizeof(struct acpi_hest_generic_status)) { - pr_warning(FW_BUG GHES_PFX -"Invalid error block length: %u for generic hardware error source: %d\n", - generic->error_block_length, + switch (generic->notify.type) { + case ACPI_HEST_NOTIFY_POLLED: + case ACPI_HEST_NOTIFY_EXTERNAL: + case ACPI_HEST_NOTIFY_SCI: + case ACPI_HEST_NOTIFY_NMI: + break; + case ACPI_HEST_NOTIFY_LOCAL: + pr_warning(GHES_PFX "Generic hardware error source: %d notified via local interrupt is not supported!\n", generic->header.source_id); goto err; + default: + pr_warning(FW_WARN GHES_PFX "Unknown notification type: %u for generic hardware error source: %d\n", + generic->notify.type, generic->header.source_id); + goto err; } - if (generic->records_to_preallocate == 0) { - pr_warning(FW_BUG GHES_PFX -"Invalid records to preallocate: %u for generic hardware error source: %d\n", - generic->records_to_preallocate, + + rc = -EIO; + if (generic->error_block_length < + sizeof(struct acpi_hest_generic_status)) { + pr_warning(FW_BUG GHES_PFX "Invalid error block length: %u for generic hardware error source: %d\n", + generic->error_block_length, generic->header.source_id); goto err; } @@ -344,38 +544,43 @@ static int __devinit ghes_probe(struct platform_device *ghes_dev) ghes = NULL; goto err; } - if (generic->notify.type == ACPI_HEST_NOTIFY_SCI) { + switch (generic->notify.type) { + case ACPI_HEST_NOTIFY_POLLED: + ghes->timer.function = ghes_poll_func; + ghes->timer.data = (unsigned long)ghes; + init_timer_deferrable(&ghes->timer); + ghes_add_timer(ghes); + break; + case ACPI_HEST_NOTIFY_EXTERNAL: + /* External interrupt vector is GSI */ + if (acpi_gsi_to_irq(generic->notify.vector, &ghes->irq)) { + pr_err(GHES_PFX "Failed to map GSI to IRQ for generic hardware error source: %d\n", + generic->header.source_id); + goto err; + } + if (request_irq(ghes->irq, ghes_irq_func, + 0, "GHES IRQ", ghes)) { + pr_err(GHES_PFX "Failed to register IRQ for generic hardware error source: %d\n", + generic->header.source_id); + goto err; + } + break; + case ACPI_HEST_NOTIFY_SCI: mutex_lock(&ghes_list_mutex); if (list_empty(&ghes_sci)) register_acpi_hed_notifier(&ghes_notifier_sci); list_add_rcu(&ghes->list, &ghes_sci); mutex_unlock(&ghes_list_mutex); - } else { - unsigned char *notify = NULL; - - switch (generic->notify.type) { - case ACPI_HEST_NOTIFY_POLLED: - notify = "POLL"; - break; - case ACPI_HEST_NOTIFY_EXTERNAL: - case ACPI_HEST_NOTIFY_LOCAL: - notify = "IRQ"; - break; - case ACPI_HEST_NOTIFY_NMI: - notify = "NMI"; - break; - } - if (notify) { - pr_warning(GHES_PFX -"Generic hardware error source: %d notified via %s is not supported!\n", - generic->header.source_id, notify); - } else { - pr_warning(FW_WARN GHES_PFX -"Unknown notification type: %u for generic hardware error source: %d\n", - generic->notify.type, generic->header.source_id); - } - rc = -ENODEV; - goto err; + break; + case ACPI_HEST_NOTIFY_NMI: + mutex_lock(&ghes_list_mutex); + if (list_empty(&ghes_nmi)) + register_die_notifier(&ghes_notifier_nmi); + list_add_rcu(&ghes->list, &ghes_nmi); + mutex_unlock(&ghes_list_mutex); + break; + default: + BUG(); } platform_set_drvdata(ghes_dev, ghes); @@ -396,7 +601,14 @@ static int __devexit ghes_remove(struct platform_device *ghes_dev) ghes = platform_get_drvdata(ghes_dev); generic = ghes->generic; + ghes->flags |= GHES_EXITING; switch (generic->notify.type) { + case ACPI_HEST_NOTIFY_POLLED: + del_timer_sync(&ghes->timer); + break; + case ACPI_HEST_NOTIFY_EXTERNAL: + free_irq(ghes->irq, ghes); + break; case ACPI_HEST_NOTIFY_SCI: mutex_lock(&ghes_list_mutex); list_del_rcu(&ghes->list); @@ -404,12 +616,23 @@ static int __devexit ghes_remove(struct platform_device *ghes_dev) unregister_acpi_hed_notifier(&ghes_notifier_sci); mutex_unlock(&ghes_list_mutex); break; + case ACPI_HEST_NOTIFY_NMI: + mutex_lock(&ghes_list_mutex); + list_del_rcu(&ghes->list); + if (list_empty(&ghes_nmi)) + unregister_die_notifier(&ghes_notifier_nmi); + mutex_unlock(&ghes_list_mutex); + /* + * To synchronize with NMI handler, ghes can only be + * freed after NMI handler finishes. + */ + synchronize_rcu(); + break; default: BUG(); break; } - synchronize_rcu(); ghes_fini(ghes); kfree(ghes); @@ -429,6 +652,8 @@ static struct platform_driver ghes_platform_driver = { static int __init ghes_init(void) { + int rc; + if (acpi_disabled) return -ENODEV; @@ -437,12 +662,25 @@ static int __init ghes_init(void) return -EINVAL; } - return platform_driver_register(&ghes_platform_driver); + rc = ghes_ioremap_init(); + if (rc) + goto err; + + rc = platform_driver_register(&ghes_platform_driver); + if (rc) + goto err_ioremap_exit; + + return 0; +err_ioremap_exit: + ghes_ioremap_exit(); +err: + return rc; } static void __exit ghes_exit(void) { platform_driver_unregister(&ghes_platform_driver); + ghes_ioremap_exit(); } module_init(ghes_init); diff --git a/kernel/panic.c b/kernel/panic.c index 4c13b1a88ebb..991bb87a1704 100644 --- a/kernel/panic.c +++ b/kernel/panic.c @@ -34,6 +34,7 @@ static int pause_on_oops_flag; static DEFINE_SPINLOCK(pause_on_oops_lock); int panic_timeout; +EXPORT_SYMBOL_GPL(panic_timeout); ATOMIC_NOTIFIER_HEAD(panic_notifier_list); diff --git a/lib/ioremap.c b/lib/ioremap.c index 5730ecd3eb66..da4e2ad74b68 100644 --- a/lib/ioremap.c +++ b/lib/ioremap.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -90,3 +91,4 @@ int ioremap_page_range(unsigned long addr, return err; } +EXPORT_SYMBOL_GPL(ioremap_page_range); diff --git a/mm/vmalloc.c b/mm/vmalloc.c index eb5cc7d00c5a..816f074fb4e1 100644 --- a/mm/vmalloc.c +++ b/mm/vmalloc.c @@ -1175,6 +1175,7 @@ void unmap_kernel_range_noflush(unsigned long addr, unsigned long size) { vunmap_page_range(addr, addr + size); } +EXPORT_SYMBOL_GPL(unmap_kernel_range_noflush); /** * unmap_kernel_range - unmap kernel VM area and flush cache and TLB -- cgit v1.2.3 From 3cfd53d53f700a225716294842b1a843326dea21 Mon Sep 17 00:00:00 2001 From: Lin Ming Date: Mon, 13 Dec 2010 13:36:02 +0800 Subject: ACPICA: Move GPE functions to new file evxfgpe.c Create a new file evxfgpe.c and move GPE specific functions to it. Acked-by: Rafael J. Wysocki Signed-off-by: Lin Ming Signed-off-by: Len Brown --- drivers/acpi/acpica/Makefile | 2 +- drivers/acpi/acpica/acevents.h | 4 + drivers/acpi/acpica/evgpeutil.c | 39 +++ drivers/acpi/acpica/evxfevnt.c | 600 --------------------------------------- drivers/acpi/acpica/evxfgpe.c | 610 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 654 insertions(+), 601 deletions(-) create mode 100644 drivers/acpi/acpica/evxfgpe.c (limited to 'drivers/acpi') diff --git a/drivers/acpi/acpica/Makefile b/drivers/acpi/acpica/Makefile index a7e1d1aa4107..eec2eadd2431 100644 --- a/drivers/acpi/acpica/Makefile +++ b/drivers/acpi/acpica/Makefile @@ -14,7 +14,7 @@ acpi-y := dsfield.o dsmthdat.o dsopcode.o dswexec.o dswscope.o \ acpi-y += evevent.o evregion.o evsci.o evxfevnt.o \ evmisc.o evrgnini.o evxface.o evxfregn.o \ - evgpe.o evgpeblk.o evgpeinit.o evgpeutil.o + evgpe.o evgpeblk.o evgpeinit.o evgpeutil.o evxfgpe.o acpi-y += exconfig.o exfield.o exnames.o exoparg6.o exresolv.o exstorob.o\ exconvrt.o exfldio.o exoparg1.o exprep.o exresop.o exsystem.o\ diff --git a/drivers/acpi/acpica/acevents.h b/drivers/acpi/acpica/acevents.h index a6f99cc37a19..43f15c8cd2b2 100644 --- a/drivers/acpi/acpica/acevents.h +++ b/drivers/acpi/acpica/acevents.h @@ -138,6 +138,10 @@ acpi_ev_walk_gpe_list(acpi_gpe_callback gpe_walk_callback, void *context); u8 acpi_ev_valid_gpe_event(struct acpi_gpe_event_info *gpe_event_info); +acpi_status +acpi_ev_get_gpe_device(struct acpi_gpe_xrupt_info *gpe_xrupt_info, + struct acpi_gpe_block_info *gpe_block, void *context); + struct acpi_gpe_xrupt_info *acpi_ev_get_gpe_xrupt_block(u32 interrupt_number); acpi_status acpi_ev_delete_gpe_xrupt(struct acpi_gpe_xrupt_info *gpe_xrupt); diff --git a/drivers/acpi/acpica/evgpeutil.c b/drivers/acpi/acpica/evgpeutil.c index 19a0e513ea48..10e477494dcf 100644 --- a/drivers/acpi/acpica/evgpeutil.c +++ b/drivers/acpi/acpica/evgpeutil.c @@ -152,6 +152,45 @@ u8 acpi_ev_valid_gpe_event(struct acpi_gpe_event_info *gpe_event_info) return (FALSE); } +/******************************************************************************* + * + * FUNCTION: acpi_ev_get_gpe_device + * + * PARAMETERS: GPE_WALK_CALLBACK + * + * RETURN: Status + * + * DESCRIPTION: Matches the input GPE index (0-current_gpe_count) with a GPE + * block device. NULL if the GPE is one of the FADT-defined GPEs. + * + ******************************************************************************/ + +acpi_status +acpi_ev_get_gpe_device(struct acpi_gpe_xrupt_info *gpe_xrupt_info, + struct acpi_gpe_block_info *gpe_block, void *context) +{ + struct acpi_gpe_device_info *info = context; + + /* Increment Index by the number of GPEs in this block */ + + info->next_block_base_index += gpe_block->gpe_count; + + if (info->index < info->next_block_base_index) { + /* + * The GPE index is within this block, get the node. Leave the node + * NULL for the FADT-defined GPEs + */ + if ((gpe_block->node)->type == ACPI_TYPE_DEVICE) { + info->gpe_device = gpe_block->node; + } + + info->status = AE_OK; + return (AE_CTRL_END); + } + + return (AE_OK); +} + /******************************************************************************* * * FUNCTION: acpi_ev_get_gpe_xrupt_block diff --git a/drivers/acpi/acpica/evxfevnt.c b/drivers/acpi/acpica/evxfevnt.c index a1dabe3fd8ae..90488c1e0f3d 100644 --- a/drivers/acpi/acpica/evxfevnt.c +++ b/drivers/acpi/acpica/evxfevnt.c @@ -43,18 +43,11 @@ #include #include "accommon.h" -#include "acevents.h" -#include "acnamesp.h" #include "actables.h" #define _COMPONENT ACPI_EVENTS ACPI_MODULE_NAME("evxfevnt") -/* Local prototypes */ -static acpi_status -acpi_ev_get_gpe_device(struct acpi_gpe_xrupt_info *gpe_xrupt_info, - struct acpi_gpe_block_info *gpe_block, void *context); - /******************************************************************************* * * FUNCTION: acpi_enable @@ -211,185 +204,6 @@ acpi_status acpi_enable_event(u32 event, u32 flags) ACPI_EXPORT_SYMBOL(acpi_enable_event) -/******************************************************************************* - * - * FUNCTION: acpi_gpe_wakeup - * - * PARAMETERS: gpe_device - Parent GPE Device. NULL for GPE0/GPE1 - * gpe_number - GPE level within the GPE block - * Action - Enable or Disable - * - * RETURN: Status - * - * DESCRIPTION: Set or clear the GPE's wakeup enable mask bit. - * - ******************************************************************************/ -acpi_status acpi_gpe_wakeup(acpi_handle gpe_device, u32 gpe_number, u8 action) -{ - acpi_status status = AE_OK; - struct acpi_gpe_event_info *gpe_event_info; - struct acpi_gpe_register_info *gpe_register_info; - acpi_cpu_flags flags; - u32 register_bit; - - ACPI_FUNCTION_TRACE(acpi_gpe_wakeup); - - flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock); - - /* Ensure that we have a valid GPE number */ - - gpe_event_info = acpi_ev_get_gpe_event_info(gpe_device, gpe_number); - if (!gpe_event_info || !(gpe_event_info->flags & ACPI_GPE_CAN_WAKE)) { - status = AE_BAD_PARAMETER; - goto unlock_and_exit; - } - - gpe_register_info = gpe_event_info->register_info; - if (!gpe_register_info) { - status = AE_NOT_EXIST; - goto unlock_and_exit; - } - - register_bit = - acpi_hw_get_gpe_register_bit(gpe_event_info, gpe_register_info); - - /* Perform the action */ - - switch (action) { - case ACPI_GPE_ENABLE: - ACPI_SET_BIT(gpe_register_info->enable_for_wake, - (u8)register_bit); - break; - - case ACPI_GPE_DISABLE: - ACPI_CLEAR_BIT(gpe_register_info->enable_for_wake, - (u8)register_bit); - break; - - default: - ACPI_ERROR((AE_INFO, "%u, Invalid action", action)); - status = AE_BAD_PARAMETER; - break; - } - -unlock_and_exit: - acpi_os_release_lock(acpi_gbl_gpe_lock, flags); - return_ACPI_STATUS(status); -} - -ACPI_EXPORT_SYMBOL(acpi_gpe_wakeup) - -/******************************************************************************* - * - * FUNCTION: acpi_enable_gpe - * - * PARAMETERS: gpe_device - Parent GPE Device. NULL for GPE0/GPE1 - * gpe_number - GPE level within the GPE block - * - * RETURN: Status - * - * DESCRIPTION: Add a reference to a GPE. On the first reference, the GPE is - * hardware-enabled. - * - ******************************************************************************/ -acpi_status acpi_enable_gpe(acpi_handle gpe_device, u32 gpe_number) -{ - acpi_status status = AE_BAD_PARAMETER; - struct acpi_gpe_event_info *gpe_event_info; - acpi_cpu_flags flags; - - ACPI_FUNCTION_TRACE(acpi_enable_gpe); - - flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock); - - /* Ensure that we have a valid GPE number */ - - gpe_event_info = acpi_ev_get_gpe_event_info(gpe_device, gpe_number); - if (gpe_event_info) { - status = acpi_raw_enable_gpe(gpe_event_info); - } - - acpi_os_release_lock(acpi_gbl_gpe_lock, flags); - return_ACPI_STATUS(status); -} -ACPI_EXPORT_SYMBOL(acpi_enable_gpe) - -/******************************************************************************* - * - * FUNCTION: acpi_disable_gpe - * - * PARAMETERS: gpe_device - Parent GPE Device. NULL for GPE0/GPE1 - * gpe_number - GPE level within the GPE block - * - * RETURN: Status - * - * DESCRIPTION: Remove a reference to a GPE. When the last reference is - * removed, only then is the GPE disabled (for runtime GPEs), or - * the GPE mask bit disabled (for wake GPEs) - * - ******************************************************************************/ -acpi_status acpi_disable_gpe(acpi_handle gpe_device, u32 gpe_number) -{ - acpi_status status = AE_BAD_PARAMETER; - struct acpi_gpe_event_info *gpe_event_info; - acpi_cpu_flags flags; - - ACPI_FUNCTION_TRACE(acpi_disable_gpe); - - flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock); - - /* Ensure that we have a valid GPE number */ - - gpe_event_info = acpi_ev_get_gpe_event_info(gpe_device, gpe_number); - if (gpe_event_info) { - status = acpi_raw_disable_gpe(gpe_event_info) ; - } - - acpi_os_release_lock(acpi_gbl_gpe_lock, flags); - return_ACPI_STATUS(status); -} -ACPI_EXPORT_SYMBOL(acpi_disable_gpe) - -/******************************************************************************* - * - * FUNCTION: acpi_gpe_can_wake - * - * PARAMETERS: gpe_device - Parent GPE Device. NULL for GPE0/GPE1 - * gpe_number - GPE level within the GPE block - * - * RETURN: Status - * - * DESCRIPTION: Set the ACPI_GPE_CAN_WAKE flag for the given GPE. If the GPE - * has a corresponding method and is currently enabled, disable it - * (GPEs with corresponding methods are enabled unconditionally - * during initialization, but GPEs that can wake up are expected - * to be initially disabled). - * - ******************************************************************************/ -acpi_status acpi_gpe_can_wake(acpi_handle gpe_device, u32 gpe_number) -{ - acpi_status status = AE_OK; - struct acpi_gpe_event_info *gpe_event_info; - acpi_cpu_flags flags; - - ACPI_FUNCTION_TRACE(acpi_gpe_can_wake); - - flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock); - - /* Ensure that we have a valid GPE number */ - - gpe_event_info = acpi_ev_get_gpe_event_info(gpe_device, gpe_number); - if (gpe_event_info) { - gpe_event_info->flags |= ACPI_GPE_CAN_WAKE; - } else { - status = AE_BAD_PARAMETER; - } - - acpi_os_release_lock(acpi_gbl_gpe_lock, flags); - return_ACPI_STATUS(status); -} -ACPI_EXPORT_SYMBOL(acpi_gpe_can_wake) - /******************************************************************************* * * FUNCTION: acpi_disable_event @@ -481,44 +295,6 @@ acpi_status acpi_clear_event(u32 event) ACPI_EXPORT_SYMBOL(acpi_clear_event) -/******************************************************************************* - * - * FUNCTION: acpi_clear_gpe - * - * PARAMETERS: gpe_device - Parent GPE Device. NULL for GPE0/GPE1 - * gpe_number - GPE level within the GPE block - * - * RETURN: Status - * - * DESCRIPTION: Clear an ACPI event (general purpose) - * - ******************************************************************************/ -acpi_status acpi_clear_gpe(acpi_handle gpe_device, u32 gpe_number) -{ - acpi_status status = AE_OK; - struct acpi_gpe_event_info *gpe_event_info; - acpi_cpu_flags flags; - - ACPI_FUNCTION_TRACE(acpi_clear_gpe); - - flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock); - - /* Ensure that we have a valid GPE number */ - - gpe_event_info = acpi_ev_get_gpe_event_info(gpe_device, gpe_number); - if (!gpe_event_info) { - status = AE_BAD_PARAMETER; - goto unlock_and_exit; - } - - status = acpi_hw_clear_gpe(gpe_event_info); - - unlock_and_exit: - acpi_os_release_lock(acpi_gbl_gpe_lock, flags); - return_ACPI_STATUS(status); -} - -ACPI_EXPORT_SYMBOL(acpi_clear_gpe) /******************************************************************************* * * FUNCTION: acpi_get_event_status @@ -575,379 +351,3 @@ acpi_status acpi_get_event_status(u32 event, acpi_event_status * event_status) } ACPI_EXPORT_SYMBOL(acpi_get_event_status) - -/******************************************************************************* - * - * FUNCTION: acpi_get_gpe_status - * - * PARAMETERS: gpe_device - Parent GPE Device. NULL for GPE0/GPE1 - * gpe_number - GPE level within the GPE block - * event_status - Where the current status of the event will - * be returned - * - * RETURN: Status - * - * DESCRIPTION: Get status of an event (general purpose) - * - ******************************************************************************/ -acpi_status -acpi_get_gpe_status(acpi_handle gpe_device, - u32 gpe_number, acpi_event_status *event_status) -{ - acpi_status status = AE_OK; - struct acpi_gpe_event_info *gpe_event_info; - acpi_cpu_flags flags; - - ACPI_FUNCTION_TRACE(acpi_get_gpe_status); - - flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock); - - /* Ensure that we have a valid GPE number */ - - gpe_event_info = acpi_ev_get_gpe_event_info(gpe_device, gpe_number); - if (!gpe_event_info) { - status = AE_BAD_PARAMETER; - goto unlock_and_exit; - } - - /* Obtain status on the requested GPE number */ - - status = acpi_hw_get_gpe_status(gpe_event_info, event_status); - - if (gpe_event_info->flags & ACPI_GPE_DISPATCH_MASK) - *event_status |= ACPI_EVENT_FLAG_HANDLE; - - unlock_and_exit: - acpi_os_release_lock(acpi_gbl_gpe_lock, flags); - return_ACPI_STATUS(status); -} - -ACPI_EXPORT_SYMBOL(acpi_get_gpe_status) -/******************************************************************************* - * - * FUNCTION: acpi_install_gpe_block - * - * PARAMETERS: gpe_device - Handle to the parent GPE Block Device - * gpe_block_address - Address and space_iD - * register_count - Number of GPE register pairs in the block - * interrupt_number - H/W interrupt for the block - * - * RETURN: Status - * - * DESCRIPTION: Create and Install a block of GPE registers - * - ******************************************************************************/ -acpi_status -acpi_install_gpe_block(acpi_handle gpe_device, - struct acpi_generic_address *gpe_block_address, - u32 register_count, u32 interrupt_number) -{ - acpi_status status = AE_OK; - union acpi_operand_object *obj_desc; - struct acpi_namespace_node *node; - struct acpi_gpe_block_info *gpe_block; - - ACPI_FUNCTION_TRACE(acpi_install_gpe_block); - - if ((!gpe_device) || (!gpe_block_address) || (!register_count)) { - return_ACPI_STATUS(AE_BAD_PARAMETER); - } - - status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE); - if (ACPI_FAILURE(status)) { - return (status); - } - - node = acpi_ns_validate_handle(gpe_device); - if (!node) { - status = AE_BAD_PARAMETER; - goto unlock_and_exit; - } - - /* - * For user-installed GPE Block Devices, the gpe_block_base_number - * is always zero - */ - status = - acpi_ev_create_gpe_block(node, gpe_block_address, register_count, 0, - interrupt_number, &gpe_block); - if (ACPI_FAILURE(status)) { - goto unlock_and_exit; - } - - /* Install block in the device_object attached to the node */ - - obj_desc = acpi_ns_get_attached_object(node); - if (!obj_desc) { - - /* - * No object, create a new one (Device nodes do not always have - * an attached object) - */ - obj_desc = acpi_ut_create_internal_object(ACPI_TYPE_DEVICE); - if (!obj_desc) { - status = AE_NO_MEMORY; - goto unlock_and_exit; - } - - status = - acpi_ns_attach_object(node, obj_desc, ACPI_TYPE_DEVICE); - - /* Remove local reference to the object */ - - acpi_ut_remove_reference(obj_desc); - - if (ACPI_FAILURE(status)) { - goto unlock_and_exit; - } - } - - /* Now install the GPE block in the device_object */ - - obj_desc->device.gpe_block = gpe_block; - - unlock_and_exit: - (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE); - return_ACPI_STATUS(status); -} - -ACPI_EXPORT_SYMBOL(acpi_install_gpe_block) - -/******************************************************************************* - * - * FUNCTION: acpi_remove_gpe_block - * - * PARAMETERS: gpe_device - Handle to the parent GPE Block Device - * - * RETURN: Status - * - * DESCRIPTION: Remove a previously installed block of GPE registers - * - ******************************************************************************/ -acpi_status acpi_remove_gpe_block(acpi_handle gpe_device) -{ - union acpi_operand_object *obj_desc; - acpi_status status; - struct acpi_namespace_node *node; - - ACPI_FUNCTION_TRACE(acpi_remove_gpe_block); - - if (!gpe_device) { - return_ACPI_STATUS(AE_BAD_PARAMETER); - } - - status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE); - if (ACPI_FAILURE(status)) { - return (status); - } - - node = acpi_ns_validate_handle(gpe_device); - if (!node) { - status = AE_BAD_PARAMETER; - goto unlock_and_exit; - } - - /* Get the device_object attached to the node */ - - obj_desc = acpi_ns_get_attached_object(node); - if (!obj_desc || !obj_desc->device.gpe_block) { - return_ACPI_STATUS(AE_NULL_OBJECT); - } - - /* Delete the GPE block (but not the device_object) */ - - status = acpi_ev_delete_gpe_block(obj_desc->device.gpe_block); - if (ACPI_SUCCESS(status)) { - obj_desc->device.gpe_block = NULL; - } - - unlock_and_exit: - (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE); - return_ACPI_STATUS(status); -} - -ACPI_EXPORT_SYMBOL(acpi_remove_gpe_block) - -/******************************************************************************* - * - * FUNCTION: acpi_get_gpe_device - * - * PARAMETERS: Index - System GPE index (0-current_gpe_count) - * gpe_device - Where the parent GPE Device is returned - * - * RETURN: Status - * - * DESCRIPTION: Obtain the GPE device associated with the input index. A NULL - * gpe device indicates that the gpe number is contained in one of - * the FADT-defined gpe blocks. Otherwise, the GPE block device. - * - ******************************************************************************/ -acpi_status -acpi_get_gpe_device(u32 index, acpi_handle *gpe_device) -{ - struct acpi_gpe_device_info info; - acpi_status status; - - ACPI_FUNCTION_TRACE(acpi_get_gpe_device); - - if (!gpe_device) { - return_ACPI_STATUS(AE_BAD_PARAMETER); - } - - if (index >= acpi_current_gpe_count) { - return_ACPI_STATUS(AE_NOT_EXIST); - } - - /* Setup and walk the GPE list */ - - info.index = index; - info.status = AE_NOT_EXIST; - info.gpe_device = NULL; - info.next_block_base_index = 0; - - status = acpi_ev_walk_gpe_list(acpi_ev_get_gpe_device, &info); - if (ACPI_FAILURE(status)) { - return_ACPI_STATUS(status); - } - - *gpe_device = info.gpe_device; - return_ACPI_STATUS(info.status); -} - -ACPI_EXPORT_SYMBOL(acpi_get_gpe_device) - -/******************************************************************************* - * - * FUNCTION: acpi_ev_get_gpe_device - * - * PARAMETERS: GPE_WALK_CALLBACK - * - * RETURN: Status - * - * DESCRIPTION: Matches the input GPE index (0-current_gpe_count) with a GPE - * block device. NULL if the GPE is one of the FADT-defined GPEs. - * - ******************************************************************************/ -static acpi_status -acpi_ev_get_gpe_device(struct acpi_gpe_xrupt_info *gpe_xrupt_info, - struct acpi_gpe_block_info *gpe_block, void *context) -{ - struct acpi_gpe_device_info *info = context; - - /* Increment Index by the number of GPEs in this block */ - - info->next_block_base_index += gpe_block->gpe_count; - - if (info->index < info->next_block_base_index) { - /* - * The GPE index is within this block, get the node. Leave the node - * NULL for the FADT-defined GPEs - */ - if ((gpe_block->node)->type == ACPI_TYPE_DEVICE) { - info->gpe_device = gpe_block->node; - } - - info->status = AE_OK; - return (AE_CTRL_END); - } - - return (AE_OK); -} - -/****************************************************************************** - * - * FUNCTION: acpi_disable_all_gpes - * - * PARAMETERS: None - * - * RETURN: Status - * - * DESCRIPTION: Disable and clear all GPEs in all GPE blocks - * - ******************************************************************************/ - -acpi_status acpi_disable_all_gpes(void) -{ - acpi_status status; - - ACPI_FUNCTION_TRACE(acpi_disable_all_gpes); - - status = acpi_ut_acquire_mutex(ACPI_MTX_EVENTS); - if (ACPI_FAILURE(status)) { - return_ACPI_STATUS(status); - } - - status = acpi_hw_disable_all_gpes(); - (void)acpi_ut_release_mutex(ACPI_MTX_EVENTS); - - return_ACPI_STATUS(status); -} - -/****************************************************************************** - * - * FUNCTION: acpi_enable_all_runtime_gpes - * - * PARAMETERS: None - * - * RETURN: Status - * - * DESCRIPTION: Enable all "runtime" GPEs, in all GPE blocks - * - ******************************************************************************/ - -acpi_status acpi_enable_all_runtime_gpes(void) -{ - acpi_status status; - - ACPI_FUNCTION_TRACE(acpi_enable_all_runtime_gpes); - - status = acpi_ut_acquire_mutex(ACPI_MTX_EVENTS); - if (ACPI_FAILURE(status)) { - return_ACPI_STATUS(status); - } - - status = acpi_hw_enable_all_runtime_gpes(); - (void)acpi_ut_release_mutex(ACPI_MTX_EVENTS); - - return_ACPI_STATUS(status); -} - -/****************************************************************************** - * - * FUNCTION: acpi_update_gpes - * - * PARAMETERS: None - * - * RETURN: None - * - * DESCRIPTION: Enable all GPEs that have associated _Lxx or _Exx methods and - * are not pointed to by any device _PRW methods indicating that - * these GPEs are generally intended for system or device wakeup - * (such GPEs have to be enabled directly when the devices whose - * _PRW methods point to them are set up for wakeup signaling). - * - ******************************************************************************/ - -acpi_status acpi_update_gpes(void) -{ - acpi_status status; - - ACPI_FUNCTION_TRACE(acpi_update_gpes); - - status = acpi_ut_acquire_mutex(ACPI_MTX_EVENTS); - if (ACPI_FAILURE(status)) { - return_ACPI_STATUS(status); - } else if (acpi_all_gpes_initialized) { - goto unlock; - } - - status = acpi_ev_walk_gpe_list(acpi_ev_initialize_gpe_block, NULL); - if (ACPI_SUCCESS(status)) { - acpi_all_gpes_initialized = TRUE; - } - -unlock: - (void)acpi_ut_release_mutex(ACPI_MTX_EVENTS); - - return_ACPI_STATUS(status); -} diff --git a/drivers/acpi/acpica/evxfgpe.c b/drivers/acpi/acpica/evxfgpe.c new file mode 100644 index 000000000000..8068065d90eb --- /dev/null +++ b/drivers/acpi/acpica/evxfgpe.c @@ -0,0 +1,610 @@ +/****************************************************************************** + * + * Module Name: evxfgpe - External Interfaces for General Purpose Events (GPEs) + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2010, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "acevents.h" +#include "acnamesp.h" + +#define _COMPONENT ACPI_EVENTS +ACPI_MODULE_NAME("evxfgpe") + +/****************************************************************************** + * + * FUNCTION: acpi_update_gpes + * + * PARAMETERS: None + * + * RETURN: None + * + * DESCRIPTION: Enable all GPEs that have associated _Lxx or _Exx methods and + * are not pointed to by any device _PRW methods indicating that + * these GPEs are generally intended for system or device wakeup + * (such GPEs have to be enabled directly when the devices whose + * _PRW methods point to them are set up for wakeup signaling). + * + ******************************************************************************/ + +acpi_status acpi_update_gpes(void) +{ + acpi_status status; + + ACPI_FUNCTION_TRACE(acpi_update_gpes); + + status = acpi_ut_acquire_mutex(ACPI_MTX_EVENTS); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } else if (acpi_all_gpes_initialized) { + goto unlock; + } + + status = acpi_ev_walk_gpe_list(acpi_ev_initialize_gpe_block, NULL); + if (ACPI_SUCCESS(status)) { + acpi_all_gpes_initialized = TRUE; + } + +unlock: + (void)acpi_ut_release_mutex(ACPI_MTX_EVENTS); + + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_enable_gpe + * + * PARAMETERS: gpe_device - Parent GPE Device. NULL for GPE0/GPE1 + * gpe_number - GPE level within the GPE block + * + * RETURN: Status + * + * DESCRIPTION: Add a reference to a GPE. On the first reference, the GPE is + * hardware-enabled. + * + ******************************************************************************/ + +acpi_status acpi_enable_gpe(acpi_handle gpe_device, u32 gpe_number) +{ + acpi_status status = AE_BAD_PARAMETER; + struct acpi_gpe_event_info *gpe_event_info; + acpi_cpu_flags flags; + + ACPI_FUNCTION_TRACE(acpi_enable_gpe); + + flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock); + + /* Ensure that we have a valid GPE number */ + + gpe_event_info = acpi_ev_get_gpe_event_info(gpe_device, gpe_number); + if (gpe_event_info) { + status = acpi_raw_enable_gpe(gpe_event_info); + } + + acpi_os_release_lock(acpi_gbl_gpe_lock, flags); + return_ACPI_STATUS(status); +} +ACPI_EXPORT_SYMBOL(acpi_enable_gpe) + +/******************************************************************************* + * + * FUNCTION: acpi_disable_gpe + * + * PARAMETERS: gpe_device - Parent GPE Device. NULL for GPE0/GPE1 + * gpe_number - GPE level within the GPE block + * + * RETURN: Status + * + * DESCRIPTION: Remove a reference to a GPE. When the last reference is + * removed, only then is the GPE disabled (for runtime GPEs), or + * the GPE mask bit disabled (for wake GPEs) + * + ******************************************************************************/ + +acpi_status acpi_disable_gpe(acpi_handle gpe_device, u32 gpe_number) +{ + acpi_status status = AE_BAD_PARAMETER; + struct acpi_gpe_event_info *gpe_event_info; + acpi_cpu_flags flags; + + ACPI_FUNCTION_TRACE(acpi_disable_gpe); + + flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock); + + /* Ensure that we have a valid GPE number */ + + gpe_event_info = acpi_ev_get_gpe_event_info(gpe_device, gpe_number); + if (gpe_event_info) { + status = acpi_raw_disable_gpe(gpe_event_info) ; + } + + acpi_os_release_lock(acpi_gbl_gpe_lock, flags); + return_ACPI_STATUS(status); +} +ACPI_EXPORT_SYMBOL(acpi_disable_gpe) + +/******************************************************************************* + * + * FUNCTION: acpi_gpe_can_wake + * + * PARAMETERS: gpe_device - Parent GPE Device. NULL for GPE0/GPE1 + * gpe_number - GPE level within the GPE block + * + * RETURN: Status + * + * DESCRIPTION: Set the ACPI_GPE_CAN_WAKE flag for the given GPE. If the GPE + * has a corresponding method and is currently enabled, disable it + * (GPEs with corresponding methods are enabled unconditionally + * during initialization, but GPEs that can wake up are expected + * to be initially disabled). + * + ******************************************************************************/ +acpi_status acpi_gpe_can_wake(acpi_handle gpe_device, u32 gpe_number) +{ + acpi_status status = AE_OK; + struct acpi_gpe_event_info *gpe_event_info; + acpi_cpu_flags flags; + + ACPI_FUNCTION_TRACE(acpi_gpe_can_wake); + + flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock); + + /* Ensure that we have a valid GPE number */ + + gpe_event_info = acpi_ev_get_gpe_event_info(gpe_device, gpe_number); + if (gpe_event_info) { + gpe_event_info->flags |= ACPI_GPE_CAN_WAKE; + } else { + status = AE_BAD_PARAMETER; + } + + acpi_os_release_lock(acpi_gbl_gpe_lock, flags); + return_ACPI_STATUS(status); +} +ACPI_EXPORT_SYMBOL(acpi_gpe_can_wake) + +/******************************************************************************* + * + * FUNCTION: acpi_gpe_wakeup + * + * PARAMETERS: gpe_device - Parent GPE Device. NULL for GPE0/GPE1 + * gpe_number - GPE level within the GPE block + * Action - Enable or Disable + * + * RETURN: Status + * + * DESCRIPTION: Set or clear the GPE's wakeup enable mask bit. + * + ******************************************************************************/ + +acpi_status acpi_gpe_wakeup(acpi_handle gpe_device, u32 gpe_number, u8 action) +{ + acpi_status status = AE_OK; + struct acpi_gpe_event_info *gpe_event_info; + struct acpi_gpe_register_info *gpe_register_info; + acpi_cpu_flags flags; + u32 register_bit; + + ACPI_FUNCTION_TRACE(acpi_gpe_wakeup); + + flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock); + + /* Ensure that we have a valid GPE number */ + + gpe_event_info = acpi_ev_get_gpe_event_info(gpe_device, gpe_number); + if (!gpe_event_info || !(gpe_event_info->flags & ACPI_GPE_CAN_WAKE)) { + status = AE_BAD_PARAMETER; + goto unlock_and_exit; + } + + gpe_register_info = gpe_event_info->register_info; + if (!gpe_register_info) { + status = AE_NOT_EXIST; + goto unlock_and_exit; + } + + register_bit = + acpi_hw_get_gpe_register_bit(gpe_event_info, gpe_register_info); + + /* Perform the action */ + + switch (action) { + case ACPI_GPE_ENABLE: + ACPI_SET_BIT(gpe_register_info->enable_for_wake, + (u8)register_bit); + break; + + case ACPI_GPE_DISABLE: + ACPI_CLEAR_BIT(gpe_register_info->enable_for_wake, + (u8)register_bit); + break; + + default: + ACPI_ERROR((AE_INFO, "%u, Invalid action", action)); + status = AE_BAD_PARAMETER; + break; + } + +unlock_and_exit: + acpi_os_release_lock(acpi_gbl_gpe_lock, flags); + return_ACPI_STATUS(status); +} + +ACPI_EXPORT_SYMBOL(acpi_gpe_wakeup) + +/******************************************************************************* + * + * FUNCTION: acpi_clear_gpe + * + * PARAMETERS: gpe_device - Parent GPE Device. NULL for GPE0/GPE1 + * gpe_number - GPE level within the GPE block + * + * RETURN: Status + * + * DESCRIPTION: Clear an ACPI event (general purpose) + * + ******************************************************************************/ +acpi_status acpi_clear_gpe(acpi_handle gpe_device, u32 gpe_number) +{ + acpi_status status = AE_OK; + struct acpi_gpe_event_info *gpe_event_info; + acpi_cpu_flags flags; + + ACPI_FUNCTION_TRACE(acpi_clear_gpe); + + flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock); + + /* Ensure that we have a valid GPE number */ + + gpe_event_info = acpi_ev_get_gpe_event_info(gpe_device, gpe_number); + if (!gpe_event_info) { + status = AE_BAD_PARAMETER; + goto unlock_and_exit; + } + + status = acpi_hw_clear_gpe(gpe_event_info); + + unlock_and_exit: + acpi_os_release_lock(acpi_gbl_gpe_lock, flags); + return_ACPI_STATUS(status); +} + +ACPI_EXPORT_SYMBOL(acpi_clear_gpe) + +/******************************************************************************* + * + * FUNCTION: acpi_get_gpe_status + * + * PARAMETERS: gpe_device - Parent GPE Device. NULL for GPE0/GPE1 + * gpe_number - GPE level within the GPE block + * event_status - Where the current status of the event will + * be returned + * + * RETURN: Status + * + * DESCRIPTION: Get status of an event (general purpose) + * + ******************************************************************************/ +acpi_status +acpi_get_gpe_status(acpi_handle gpe_device, + u32 gpe_number, acpi_event_status *event_status) +{ + acpi_status status = AE_OK; + struct acpi_gpe_event_info *gpe_event_info; + acpi_cpu_flags flags; + + ACPI_FUNCTION_TRACE(acpi_get_gpe_status); + + flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock); + + /* Ensure that we have a valid GPE number */ + + gpe_event_info = acpi_ev_get_gpe_event_info(gpe_device, gpe_number); + if (!gpe_event_info) { + status = AE_BAD_PARAMETER; + goto unlock_and_exit; + } + + /* Obtain status on the requested GPE number */ + + status = acpi_hw_get_gpe_status(gpe_event_info, event_status); + + if (gpe_event_info->flags & ACPI_GPE_DISPATCH_MASK) + *event_status |= ACPI_EVENT_FLAG_HANDLE; + + unlock_and_exit: + acpi_os_release_lock(acpi_gbl_gpe_lock, flags); + return_ACPI_STATUS(status); +} + +ACPI_EXPORT_SYMBOL(acpi_get_gpe_status) + +/****************************************************************************** + * + * FUNCTION: acpi_disable_all_gpes + * + * PARAMETERS: None + * + * RETURN: Status + * + * DESCRIPTION: Disable and clear all GPEs in all GPE blocks + * + ******************************************************************************/ + +acpi_status acpi_disable_all_gpes(void) +{ + acpi_status status; + + ACPI_FUNCTION_TRACE(acpi_disable_all_gpes); + + status = acpi_ut_acquire_mutex(ACPI_MTX_EVENTS); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + status = acpi_hw_disable_all_gpes(); + (void)acpi_ut_release_mutex(ACPI_MTX_EVENTS); + + return_ACPI_STATUS(status); +} + +/****************************************************************************** + * + * FUNCTION: acpi_enable_all_runtime_gpes + * + * PARAMETERS: None + * + * RETURN: Status + * + * DESCRIPTION: Enable all "runtime" GPEs, in all GPE blocks + * + ******************************************************************************/ + +acpi_status acpi_enable_all_runtime_gpes(void) +{ + acpi_status status; + + ACPI_FUNCTION_TRACE(acpi_enable_all_runtime_gpes); + + status = acpi_ut_acquire_mutex(ACPI_MTX_EVENTS); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + status = acpi_hw_enable_all_runtime_gpes(); + (void)acpi_ut_release_mutex(ACPI_MTX_EVENTS); + + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_install_gpe_block + * + * PARAMETERS: gpe_device - Handle to the parent GPE Block Device + * gpe_block_address - Address and space_iD + * register_count - Number of GPE register pairs in the block + * interrupt_number - H/W interrupt for the block + * + * RETURN: Status + * + * DESCRIPTION: Create and Install a block of GPE registers + * + ******************************************************************************/ +acpi_status +acpi_install_gpe_block(acpi_handle gpe_device, + struct acpi_generic_address *gpe_block_address, + u32 register_count, u32 interrupt_number) +{ + acpi_status status = AE_OK; + union acpi_operand_object *obj_desc; + struct acpi_namespace_node *node; + struct acpi_gpe_block_info *gpe_block; + + ACPI_FUNCTION_TRACE(acpi_install_gpe_block); + + if ((!gpe_device) || (!gpe_block_address) || (!register_count)) { + return_ACPI_STATUS(AE_BAD_PARAMETER); + } + + status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE); + if (ACPI_FAILURE(status)) { + return (status); + } + + node = acpi_ns_validate_handle(gpe_device); + if (!node) { + status = AE_BAD_PARAMETER; + goto unlock_and_exit; + } + + /* + * For user-installed GPE Block Devices, the gpe_block_base_number + * is always zero + */ + status = + acpi_ev_create_gpe_block(node, gpe_block_address, register_count, 0, + interrupt_number, &gpe_block); + if (ACPI_FAILURE(status)) { + goto unlock_and_exit; + } + + /* Install block in the device_object attached to the node */ + + obj_desc = acpi_ns_get_attached_object(node); + if (!obj_desc) { + + /* + * No object, create a new one (Device nodes do not always have + * an attached object) + */ + obj_desc = acpi_ut_create_internal_object(ACPI_TYPE_DEVICE); + if (!obj_desc) { + status = AE_NO_MEMORY; + goto unlock_and_exit; + } + + status = + acpi_ns_attach_object(node, obj_desc, ACPI_TYPE_DEVICE); + + /* Remove local reference to the object */ + + acpi_ut_remove_reference(obj_desc); + + if (ACPI_FAILURE(status)) { + goto unlock_and_exit; + } + } + + /* Now install the GPE block in the device_object */ + + obj_desc->device.gpe_block = gpe_block; + + unlock_and_exit: + (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE); + return_ACPI_STATUS(status); +} + +ACPI_EXPORT_SYMBOL(acpi_install_gpe_block) + +/******************************************************************************* + * + * FUNCTION: acpi_remove_gpe_block + * + * PARAMETERS: gpe_device - Handle to the parent GPE Block Device + * + * RETURN: Status + * + * DESCRIPTION: Remove a previously installed block of GPE registers + * + ******************************************************************************/ +acpi_status acpi_remove_gpe_block(acpi_handle gpe_device) +{ + union acpi_operand_object *obj_desc; + acpi_status status; + struct acpi_namespace_node *node; + + ACPI_FUNCTION_TRACE(acpi_remove_gpe_block); + + if (!gpe_device) { + return_ACPI_STATUS(AE_BAD_PARAMETER); + } + + status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE); + if (ACPI_FAILURE(status)) { + return (status); + } + + node = acpi_ns_validate_handle(gpe_device); + if (!node) { + status = AE_BAD_PARAMETER; + goto unlock_and_exit; + } + + /* Get the device_object attached to the node */ + + obj_desc = acpi_ns_get_attached_object(node); + if (!obj_desc || !obj_desc->device.gpe_block) { + return_ACPI_STATUS(AE_NULL_OBJECT); + } + + /* Delete the GPE block (but not the device_object) */ + + status = acpi_ev_delete_gpe_block(obj_desc->device.gpe_block); + if (ACPI_SUCCESS(status)) { + obj_desc->device.gpe_block = NULL; + } + + unlock_and_exit: + (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE); + return_ACPI_STATUS(status); +} + +ACPI_EXPORT_SYMBOL(acpi_remove_gpe_block) + +/******************************************************************************* + * + * FUNCTION: acpi_get_gpe_device + * + * PARAMETERS: Index - System GPE index (0-current_gpe_count) + * gpe_device - Where the parent GPE Device is returned + * + * RETURN: Status + * + * DESCRIPTION: Obtain the GPE device associated with the input index. A NULL + * gpe device indicates that the gpe number is contained in one of + * the FADT-defined gpe blocks. Otherwise, the GPE block device. + * + ******************************************************************************/ +acpi_status +acpi_get_gpe_device(u32 index, acpi_handle *gpe_device) +{ + struct acpi_gpe_device_info info; + acpi_status status; + + ACPI_FUNCTION_TRACE(acpi_get_gpe_device); + + if (!gpe_device) { + return_ACPI_STATUS(AE_BAD_PARAMETER); + } + + if (index >= acpi_current_gpe_count) { + return_ACPI_STATUS(AE_NOT_EXIST); + } + + /* Setup and walk the GPE list */ + + info.index = index; + info.status = AE_NOT_EXIST; + info.gpe_device = NULL; + info.next_block_base_index = 0; + + status = acpi_ev_walk_gpe_list(acpi_ev_get_gpe_device, &info); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + *gpe_device = info.gpe_device; + return_ACPI_STATUS(info.status); +} + +ACPI_EXPORT_SYMBOL(acpi_get_gpe_device) -- cgit v1.2.3 From 3a37898d507794cfc68a092303e02651d3f01308 Mon Sep 17 00:00:00 2001 From: Lin Ming Date: Mon, 13 Dec 2010 13:36:15 +0800 Subject: ACPICA: Rename some function and variable names Some function and variable names are renamed to be consistent with ACPICA code base. acpi_raw_enable_gpe -> acpi_ev_add_gpe_reference acpi_raw_disable_gpe -> acpi_ev_remove_gpe_reference acpi_gpe_can_wake -> acpi_setup_gpe_for_wake acpi_gpe_wakeup -> acpi_set_gpe_wake_mask acpi_update_gpes -> acpi_update_all_gpes acpi_all_gpes_initialized -> acpi_gbl_all_gpes_initialized acpi_handler_info -> acpi_gpe_handler_info ... Signed-off-by: Lin Ming Signed-off-by: Len Brown --- drivers/acpi/acpica/acevents.h | 6 ++--- drivers/acpi/acpica/acglobal.h | 2 +- drivers/acpi/acpica/aclocal.h | 8 +++---- drivers/acpi/acpica/evgpe.c | 14 +++++++---- drivers/acpi/acpica/evgpeblk.c | 4 ++-- drivers/acpi/acpica/evxface.c | 16 ++++++------- drivers/acpi/acpica/evxfgpe.c | 53 ++++++++++++++++++++++++++---------------- drivers/acpi/acpica/hwgpe.c | 2 +- drivers/acpi/acpica/utglobal.c | 2 +- drivers/acpi/dock.c | 2 +- drivers/acpi/scan.c | 6 ++--- drivers/acpi/wakeup.c | 4 ++-- include/acpi/acpixf.h | 6 ++--- include/acpi/actypes.h | 4 ++-- 14 files changed, 73 insertions(+), 56 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/acpica/acevents.h b/drivers/acpi/acpica/acevents.h index 43f15c8cd2b2..ce2af07f62b3 100644 --- a/drivers/acpi/acpica/acevents.h +++ b/drivers/acpi/acpica/acevents.h @@ -82,9 +82,9 @@ acpi_ev_update_gpe_enable_mask(struct acpi_gpe_event_info *gpe_event_info); acpi_status acpi_ev_enable_gpe(struct acpi_gpe_event_info *gpe_event_info); -acpi_status acpi_raw_enable_gpe(struct acpi_gpe_event_info *gpe_event_info); +acpi_status acpi_ev_add_gpe_reference(struct acpi_gpe_event_info *gpe_event_info); -acpi_status acpi_raw_disable_gpe(struct acpi_gpe_event_info *gpe_event_info); +acpi_status acpi_ev_remove_gpe_reference(struct acpi_gpe_event_info *gpe_event_info); struct acpi_gpe_event_info *acpi_ev_get_gpe_event_info(acpi_handle gpe_device, u32 gpe_number); @@ -107,7 +107,7 @@ acpi_ev_create_gpe_block(struct acpi_namespace_node *gpe_device, acpi_status acpi_ev_initialize_gpe_block(struct acpi_gpe_xrupt_info *gpe_xrupt_info, struct acpi_gpe_block_info *gpe_block, - void *ignored); + void *context); acpi_status acpi_ev_delete_gpe_block(struct acpi_gpe_block_info *gpe_block); diff --git a/drivers/acpi/acpica/acglobal.h b/drivers/acpi/acpica/acglobal.h index ad88fcae4eb9..fc694647699f 100644 --- a/drivers/acpi/acpica/acglobal.h +++ b/drivers/acpi/acpica/acglobal.h @@ -370,7 +370,7 @@ ACPI_EXTERN struct acpi_fixed_event_handler ACPI_EXTERN struct acpi_gpe_xrupt_info *acpi_gbl_gpe_xrupt_list_head; ACPI_EXTERN struct acpi_gpe_block_info *acpi_gbl_gpe_fadt_blocks[ACPI_MAX_GPE_BLOCKS]; -ACPI_EXTERN u8 acpi_all_gpes_initialized; +ACPI_EXTERN u8 acpi_gbl_all_gpes_initialized; /***************************************************************************** * diff --git a/drivers/acpi/acpica/aclocal.h b/drivers/acpi/acpica/aclocal.h index 2ceb0c05b2d7..2f841d019a3a 100644 --- a/drivers/acpi/acpica/aclocal.h +++ b/drivers/acpi/acpica/aclocal.h @@ -412,13 +412,13 @@ struct acpi_handler_info { acpi_event_handler address; /* Address of handler, if any */ void *context; /* Context to be passed to handler */ struct acpi_namespace_node *method_node; /* Method node for this GPE level (saved) */ - u8 orig_flags; /* Original misc info about this GPE */ - u8 orig_enabled; /* Set if the GPE was originally enabled */ + u8 original_flags; /* Original (pre-handler) GPE info */ + u8 originally_enabled; /* True if GPE was originally enabled */ }; union acpi_gpe_dispatch_info { struct acpi_namespace_node *method_node; /* Method node for this GPE level */ - struct acpi_handler_info *handler; + struct acpi_handler_info *handler; /* Installed GPE handler */ }; /* @@ -458,7 +458,7 @@ struct acpi_gpe_block_info { u32 register_count; /* Number of register pairs in block */ u16 gpe_count; /* Number of individual GPEs in block */ u8 block_base_number; /* Base GPE number for this block */ - u8 initialized; /* If set, the GPE block has been initialized */ + u8 initialized; /* TRUE if this block is initialized */ }; /* Information about GPE interrupt handlers, one per each interrupt level used for GPEs */ diff --git a/drivers/acpi/acpica/evgpe.c b/drivers/acpi/acpica/evgpe.c index f226eac314db..005c170d2448 100644 --- a/drivers/acpi/acpica/evgpe.c +++ b/drivers/acpi/acpica/evgpe.c @@ -137,7 +137,7 @@ acpi_ev_enable_gpe(struct acpi_gpe_event_info *gpe_event_info) /******************************************************************************* * - * FUNCTION: acpi_raw_enable_gpe + * FUNCTION: acpi_ev_add_gpe_reference * * PARAMETERS: gpe_event_info - GPE to enable * @@ -148,10 +148,12 @@ acpi_ev_enable_gpe(struct acpi_gpe_event_info *gpe_event_info) * ******************************************************************************/ -acpi_status acpi_raw_enable_gpe(struct acpi_gpe_event_info *gpe_event_info) +acpi_status acpi_ev_add_gpe_reference(struct acpi_gpe_event_info *gpe_event_info) { acpi_status status = AE_OK; + ACPI_FUNCTION_TRACE(ev_add_gpe_reference); + if (gpe_event_info->runtime_count == ACPI_UINT8_MAX) { return_ACPI_STATUS(AE_LIMIT); } @@ -173,7 +175,7 @@ acpi_status acpi_raw_enable_gpe(struct acpi_gpe_event_info *gpe_event_info) /******************************************************************************* * - * FUNCTION: acpi_raw_disable_gpe + * FUNCTION: acpi_ev_remove_gpe_reference * * PARAMETERS: gpe_event_info - GPE to disable * @@ -184,10 +186,12 @@ acpi_status acpi_raw_enable_gpe(struct acpi_gpe_event_info *gpe_event_info) * ******************************************************************************/ -acpi_status acpi_raw_disable_gpe(struct acpi_gpe_event_info *gpe_event_info) +acpi_status acpi_ev_remove_gpe_reference(struct acpi_gpe_event_info *gpe_event_info) { acpi_status status = AE_OK; + ACPI_FUNCTION_TRACE(ev_remove_gpe_reference); + if (!gpe_event_info->runtime_count) { return_ACPI_STATUS(AE_LIMIT); } @@ -529,7 +533,7 @@ static void acpi_ev_asynch_enable_gpe(void *context) * Enable this GPE, conditionally. This means that the GPE will only be * physically enabled if the enable_for_run bit is set in the event_info */ - (void)acpi_hw_low_set_gpe(gpe_event_info, ACPI_GPE_COND_ENABLE); + (void)acpi_hw_low_set_gpe(gpe_event_info, ACPI_GPE_CONDITIONAL_ENABLE); return_VOID; } diff --git a/drivers/acpi/acpica/evgpeblk.c b/drivers/acpi/acpica/evgpeblk.c index 020add3eee1c..2a445dfcfdb4 100644 --- a/drivers/acpi/acpica/evgpeblk.c +++ b/drivers/acpi/acpica/evgpeblk.c @@ -386,7 +386,7 @@ acpi_ev_create_gpe_block(struct acpi_namespace_node *gpe_device, return_ACPI_STATUS(status); } - acpi_all_gpes_initialized = FALSE; + acpi_gbl_all_gpes_initialized = FALSE; /* Find all GPE methods (_Lxx or_Exx) for this block */ @@ -479,7 +479,7 @@ acpi_ev_initialize_gpe_block(struct acpi_gpe_xrupt_info *gpe_xrupt_info, continue; } - status = acpi_raw_enable_gpe(gpe_event_info); + status = acpi_ev_add_gpe_reference(gpe_event_info); if (ACPI_FAILURE(status)) { ACPI_EXCEPTION((AE_INFO, status, "Could not enable GPE 0x%02X", diff --git a/drivers/acpi/acpica/evxface.c b/drivers/acpi/acpica/evxface.c index 36af222cac65..042a6d69f38e 100644 --- a/drivers/acpi/acpica/evxface.c +++ b/drivers/acpi/acpica/evxface.c @@ -722,7 +722,7 @@ acpi_install_gpe_handler(acpi_handle gpe_device, handler->address = address; handler->context = context; handler->method_node = gpe_event_info->dispatch.method_node; - handler->orig_flags = gpe_event_info->flags & + handler->original_flags = gpe_event_info->flags & (ACPI_GPE_XRUPT_TYPE_MASK | ACPI_GPE_DISPATCH_MASK); /* @@ -731,10 +731,10 @@ acpi_install_gpe_handler(acpi_handle gpe_device, * disabled now to avoid spurious execution of the handler. */ - if ((handler->orig_flags & ACPI_GPE_DISPATCH_METHOD) + if ((handler->original_flags & ACPI_GPE_DISPATCH_METHOD) && gpe_event_info->runtime_count) { - handler->orig_enabled = 1; - (void)acpi_raw_disable_gpe(gpe_event_info); + handler->originally_enabled = 1; + (void)acpi_ev_remove_gpe_reference(gpe_event_info); } /* Install the handler */ @@ -835,7 +835,7 @@ acpi_remove_gpe_handler(acpi_handle gpe_device, gpe_event_info->dispatch.method_node = handler->method_node; gpe_event_info->flags &= ~(ACPI_GPE_XRUPT_TYPE_MASK | ACPI_GPE_DISPATCH_MASK); - gpe_event_info->flags |= handler->orig_flags; + gpe_event_info->flags |= handler->original_flags; /* * If the GPE was previously associated with a method and it was @@ -843,9 +843,9 @@ acpi_remove_gpe_handler(acpi_handle gpe_device, * post-initialization configuration. */ - if ((handler->orig_flags & ACPI_GPE_DISPATCH_METHOD) - && handler->orig_enabled) - (void)acpi_raw_enable_gpe(gpe_event_info); + if ((handler->original_flags & ACPI_GPE_DISPATCH_METHOD) + && handler->originally_enabled) + (void)acpi_ev_add_gpe_reference(gpe_event_info); /* Now we can free the handler object */ diff --git a/drivers/acpi/acpica/evxfgpe.c b/drivers/acpi/acpica/evxfgpe.c index 8068065d90eb..99f77ab29729 100644 --- a/drivers/acpi/acpica/evxfgpe.c +++ b/drivers/acpi/acpica/evxfgpe.c @@ -51,7 +51,7 @@ ACPI_MODULE_NAME("evxfgpe") /****************************************************************************** * - * FUNCTION: acpi_update_gpes + * FUNCTION: acpi_update_all_gpes * * PARAMETERS: None * @@ -65,30 +65,34 @@ ACPI_MODULE_NAME("evxfgpe") * ******************************************************************************/ -acpi_status acpi_update_gpes(void) +acpi_status acpi_update_all_gpes(void) { acpi_status status; - ACPI_FUNCTION_TRACE(acpi_update_gpes); + ACPI_FUNCTION_TRACE(acpi_update_all_gpes); status = acpi_ut_acquire_mutex(ACPI_MTX_EVENTS); if (ACPI_FAILURE(status)) { return_ACPI_STATUS(status); - } else if (acpi_all_gpes_initialized) { - goto unlock; + } + + if (acpi_gbl_all_gpes_initialized) { + goto unlock_and_exit; } status = acpi_ev_walk_gpe_list(acpi_ev_initialize_gpe_block, NULL); if (ACPI_SUCCESS(status)) { - acpi_all_gpes_initialized = TRUE; + acpi_gbl_all_gpes_initialized = TRUE; } -unlock: +unlock_and_exit: (void)acpi_ut_release_mutex(ACPI_MTX_EVENTS); return_ACPI_STATUS(status); } +ACPI_EXPORT_SYMBOL(acpi_update_all_gpes) + /******************************************************************************* * * FUNCTION: acpi_enable_gpe @@ -117,7 +121,7 @@ acpi_status acpi_enable_gpe(acpi_handle gpe_device, u32 gpe_number) gpe_event_info = acpi_ev_get_gpe_event_info(gpe_device, gpe_number); if (gpe_event_info) { - status = acpi_raw_enable_gpe(gpe_event_info); + status = acpi_ev_add_gpe_reference(gpe_event_info); } acpi_os_release_lock(acpi_gbl_gpe_lock, flags); @@ -154,7 +158,7 @@ acpi_status acpi_disable_gpe(acpi_handle gpe_device, u32 gpe_number) gpe_event_info = acpi_ev_get_gpe_event_info(gpe_device, gpe_number); if (gpe_event_info) { - status = acpi_raw_disable_gpe(gpe_event_info) ; + status = acpi_ev_remove_gpe_reference(gpe_event_info) ; } acpi_os_release_lock(acpi_gbl_gpe_lock, flags); @@ -164,7 +168,7 @@ ACPI_EXPORT_SYMBOL(acpi_disable_gpe) /******************************************************************************* * - * FUNCTION: acpi_gpe_can_wake + * FUNCTION: acpi_setup_gpe_for_wake * * PARAMETERS: gpe_device - Parent GPE Device. NULL for GPE0/GPE1 * gpe_number - GPE level within the GPE block @@ -178,13 +182,13 @@ ACPI_EXPORT_SYMBOL(acpi_disable_gpe) * to be initially disabled). * ******************************************************************************/ -acpi_status acpi_gpe_can_wake(acpi_handle gpe_device, u32 gpe_number) +acpi_status acpi_setup_gpe_for_wake(acpi_handle gpe_device, u32 gpe_number) { acpi_status status = AE_OK; struct acpi_gpe_event_info *gpe_event_info; acpi_cpu_flags flags; - ACPI_FUNCTION_TRACE(acpi_gpe_can_wake); + ACPI_FUNCTION_TRACE(acpi_setup_gpe_for_wake); flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock); @@ -200,11 +204,11 @@ acpi_status acpi_gpe_can_wake(acpi_handle gpe_device, u32 gpe_number) acpi_os_release_lock(acpi_gbl_gpe_lock, flags); return_ACPI_STATUS(status); } -ACPI_EXPORT_SYMBOL(acpi_gpe_can_wake) +ACPI_EXPORT_SYMBOL(acpi_setup_gpe_for_wake) /******************************************************************************* * - * FUNCTION: acpi_gpe_wakeup + * FUNCTION: acpi_set_gpe_wake_mask * * PARAMETERS: gpe_device - Parent GPE Device. NULL for GPE0/GPE1 * gpe_number - GPE level within the GPE block @@ -216,7 +220,7 @@ ACPI_EXPORT_SYMBOL(acpi_gpe_can_wake) * ******************************************************************************/ -acpi_status acpi_gpe_wakeup(acpi_handle gpe_device, u32 gpe_number, u8 action) +acpi_status acpi_set_gpe_wake_mask(acpi_handle gpe_device, u32 gpe_number, u8 action) { acpi_status status = AE_OK; struct acpi_gpe_event_info *gpe_event_info; @@ -224,18 +228,23 @@ acpi_status acpi_gpe_wakeup(acpi_handle gpe_device, u32 gpe_number, u8 action) acpi_cpu_flags flags; u32 register_bit; - ACPI_FUNCTION_TRACE(acpi_gpe_wakeup); + ACPI_FUNCTION_TRACE(acpi_set_gpe_wake_mask); flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock); /* Ensure that we have a valid GPE number */ gpe_event_info = acpi_ev_get_gpe_event_info(gpe_device, gpe_number); - if (!gpe_event_info || !(gpe_event_info->flags & ACPI_GPE_CAN_WAKE)) { + if (!gpe_event_info) { status = AE_BAD_PARAMETER; goto unlock_and_exit; } + if (!(gpe_event_info->flags & ACPI_GPE_CAN_WAKE)) { + status = AE_TYPE; + goto unlock_and_exit; + } + gpe_register_info = gpe_event_info->register_info; if (!gpe_register_info) { status = AE_NOT_EXIST; @@ -269,7 +278,7 @@ unlock_and_exit: return_ACPI_STATUS(status); } -ACPI_EXPORT_SYMBOL(acpi_gpe_wakeup) +ACPI_EXPORT_SYMBOL(acpi_set_gpe_wake_mask) /******************************************************************************* * @@ -387,6 +396,8 @@ acpi_status acpi_disable_all_gpes(void) return_ACPI_STATUS(status); } +ACPI_EXPORT_SYMBOL(acpi_disable_all_gpes) + /****************************************************************************** * * FUNCTION: acpi_enable_all_runtime_gpes @@ -416,6 +427,8 @@ acpi_status acpi_enable_all_runtime_gpes(void) return_ACPI_STATUS(status); } +ACPI_EXPORT_SYMBOL(acpi_enable_all_runtime_gpes) + /******************************************************************************* * * FUNCTION: acpi_install_gpe_block @@ -435,7 +448,7 @@ acpi_install_gpe_block(acpi_handle gpe_device, struct acpi_generic_address *gpe_block_address, u32 register_count, u32 interrupt_number) { - acpi_status status = AE_OK; + acpi_status status; union acpi_operand_object *obj_desc; struct acpi_namespace_node *node; struct acpi_gpe_block_info *gpe_block; @@ -603,7 +616,7 @@ acpi_get_gpe_device(u32 index, acpi_handle *gpe_device) return_ACPI_STATUS(status); } - *gpe_device = info.gpe_device; + *gpe_device = ACPI_CAST_PTR(acpi_handle, info.gpe_device); return_ACPI_STATUS(info.status); } diff --git a/drivers/acpi/acpica/hwgpe.c b/drivers/acpi/acpica/hwgpe.c index 14750db2a1b8..7c6d4858bb49 100644 --- a/drivers/acpi/acpica/hwgpe.c +++ b/drivers/acpi/acpica/hwgpe.c @@ -118,7 +118,7 @@ acpi_hw_low_set_gpe(struct acpi_gpe_event_info *gpe_event_info, u8 action) register_bit = acpi_hw_get_gpe_register_bit(gpe_event_info, gpe_register_info); switch (action) { - case ACPI_GPE_COND_ENABLE: + case ACPI_GPE_CONDITIONAL_ENABLE: if (!(register_bit & gpe_register_info->enable_for_run)) return (AE_BAD_PARAMETER); diff --git a/drivers/acpi/acpica/utglobal.c b/drivers/acpi/acpica/utglobal.c index e87bc6760be6..a99c32aa48a4 100644 --- a/drivers/acpi/acpica/utglobal.c +++ b/drivers/acpi/acpica/utglobal.c @@ -768,7 +768,7 @@ acpi_status acpi_ut_init_globals(void) acpi_gbl_gpe_fadt_blocks[0] = NULL; acpi_gbl_gpe_fadt_blocks[1] = NULL; acpi_current_gpe_count = 0; - acpi_all_gpes_initialized = FALSE; + acpi_gbl_all_gpes_initialized = FALSE; /* Global handlers */ diff --git a/drivers/acpi/dock.c b/drivers/acpi/dock.c index 81514a4918cc..1864ad3cf895 100644 --- a/drivers/acpi/dock.c +++ b/drivers/acpi/dock.c @@ -725,7 +725,7 @@ static void dock_notify(acpi_handle handle, u32 event, void *data) complete_dock(ds); dock_event(ds, event, DOCK_EVENT); dock_lock(ds, 1); - acpi_update_gpes(); + acpi_update_all_gpes(); break; } if (dock_present(ds) || dock_in_progress(ds)) diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 29ef505c487b..70249b3d2934 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -778,7 +778,7 @@ acpi_bus_extract_wakeup_device_power_package(acpi_handle handle, wakeup->resources.handles[i] = element->reference.handle; } - acpi_gpe_can_wake(wakeup->gpe_device, wakeup->gpe_number); + acpi_setup_gpe_for_wake(wakeup->gpe_device, wakeup->gpe_number); out: kfree(buffer.pointer); @@ -1467,7 +1467,7 @@ int acpi_bus_start(struct acpi_device *device) result = acpi_bus_scan(device->handle, &ops, NULL); - acpi_update_gpes(); + acpi_update_all_gpes(); return result; } @@ -1584,7 +1584,7 @@ int __init acpi_scan_init(void) if (result) acpi_device_unregister(acpi_root, ACPI_BUS_REMOVAL_NORMAL); else - acpi_update_gpes(); + acpi_update_all_gpes(); return result; } diff --git a/drivers/acpi/wakeup.c b/drivers/acpi/wakeup.c index f62a50c3ed34..c6cb68e0a80c 100644 --- a/drivers/acpi/wakeup.c +++ b/drivers/acpi/wakeup.c @@ -45,7 +45,7 @@ void acpi_enable_wakeup_devices(u8 sleep_state) acpi_enable_wakeup_device_power(dev, sleep_state); /* The wake-up power should have been enabled already. */ - acpi_gpe_wakeup(dev->wakeup.gpe_device, dev->wakeup.gpe_number, + acpi_set_gpe_wake_mask(dev->wakeup.gpe_device, dev->wakeup.gpe_number, ACPI_GPE_ENABLE); } } @@ -67,7 +67,7 @@ void acpi_disable_wakeup_devices(u8 sleep_state) || (sleep_state > (u32) dev->wakeup.sleep_state)) continue; - acpi_gpe_wakeup(dev->wakeup.gpe_device, dev->wakeup.gpe_number, + acpi_set_gpe_wake_mask(dev->wakeup.gpe_device, dev->wakeup.gpe_number, ACPI_GPE_DISABLE); if (dev->wakeup.state.enabled) diff --git a/include/acpi/acpixf.h b/include/acpi/acpixf.h index 53b7cfd924a3..5f8ccf1b5ae9 100644 --- a/include/acpi/acpixf.h +++ b/include/acpi/acpixf.h @@ -292,11 +292,11 @@ acpi_status acpi_enable_gpe(acpi_handle gpe_device, u32 gpe_number); acpi_status acpi_disable_gpe(acpi_handle gpe_device, u32 gpe_number); -acpi_status acpi_gpe_can_wake(acpi_handle gpe_device, u32 gpe_number); +acpi_status acpi_setup_gpe_for_wake(acpi_handle gpe_device, u32 gpe_number); acpi_status acpi_clear_gpe(acpi_handle gpe_device, u32 gpe_number); -acpi_status acpi_gpe_wakeup(acpi_handle gpe_device, u32 gpe_number, u8 action); +acpi_status acpi_set_gpe_wake_mask(acpi_handle gpe_device, u32 gpe_number, u8 action); acpi_status acpi_get_gpe_status(acpi_handle gpe_device, @@ -315,7 +315,7 @@ acpi_install_gpe_block(acpi_handle gpe_device, acpi_status acpi_remove_gpe_block(acpi_handle gpe_device); -acpi_status acpi_update_gpes(void); +acpi_status acpi_update_all_gpes(void); /* * Resource interfaces diff --git a/include/acpi/actypes.h b/include/acpi/actypes.h index 2b134b691e34..e73893994b5d 100644 --- a/include/acpi/actypes.h +++ b/include/acpi/actypes.h @@ -656,11 +656,11 @@ typedef u32 acpi_event_status; #define ACPI_GPE_MAX 0xFF #define ACPI_NUM_GPE 256 -/* Actions for acpi_gpe_wakeup, acpi_hw_low_set_gpe */ +/* Actions for acpi_set_gpe_wake_mask, acpi_hw_low_set_gpe */ #define ACPI_GPE_ENABLE 0 #define ACPI_GPE_DISABLE 1 -#define ACPI_GPE_COND_ENABLE 2 +#define ACPI_GPE_CONDITIONAL_ENABLE 2 /* * GPE info flags - Per GPE -- cgit v1.2.3 From 8b6cd8ad18def34bfc5045b2a0234329bf94cf78 Mon Sep 17 00:00:00 2001 From: Lin Ming Date: Mon, 13 Dec 2010 13:38:46 +0800 Subject: ACPICA: New GPE handler callback definition The new GPE handler callback has 2 additional parameters, gpe_device and gpe_number. typedef u32 (*acpi_gpe_handler) (acpi_handle gpe_device, u32 gpe_number, void *context); Signed-off-by: Lin Ming Signed-off-by: Len Brown --- drivers/acpi/acpica/acevents.h | 3 ++- drivers/acpi/acpica/aclocal.h | 6 +++--- drivers/acpi/acpica/evgpe.c | 14 ++++++++++---- drivers/acpi/acpica/evxface.c | 10 +++++----- drivers/acpi/ec.c | 3 ++- drivers/char/ipmi/ipmi_si_intf.c | 3 ++- include/acpi/acpixf.h | 4 ++-- include/acpi/actypes.h | 3 +++ 8 files changed, 29 insertions(+), 17 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/acpica/acevents.h b/drivers/acpi/acpica/acevents.h index ce2af07f62b3..ebb467fe3678 100644 --- a/drivers/acpi/acpica/acevents.h +++ b/drivers/acpi/acpica/acevents.h @@ -112,7 +112,8 @@ acpi_ev_initialize_gpe_block(struct acpi_gpe_xrupt_info *gpe_xrupt_info, acpi_status acpi_ev_delete_gpe_block(struct acpi_gpe_block_info *gpe_block); u32 -acpi_ev_gpe_dispatch(struct acpi_gpe_event_info *gpe_event_info, +acpi_ev_gpe_dispatch(struct acpi_namespace_node *gpe_device, + struct acpi_gpe_event_info *gpe_event_info, u32 gpe_number); /* diff --git a/drivers/acpi/acpica/aclocal.h b/drivers/acpi/acpica/aclocal.h index 2f841d019a3a..6a71f8e673e9 100644 --- a/drivers/acpi/acpica/aclocal.h +++ b/drivers/acpi/acpica/aclocal.h @@ -408,8 +408,8 @@ struct acpi_predefined_data { /* Dispatch info for each GPE -- either a method or handler, cannot be both */ -struct acpi_handler_info { - acpi_event_handler address; /* Address of handler, if any */ +struct acpi_gpe_handler_info { + acpi_gpe_handler address; /* Address of handler, if any */ void *context; /* Context to be passed to handler */ struct acpi_namespace_node *method_node; /* Method node for this GPE level (saved) */ u8 original_flags; /* Original (pre-handler) GPE info */ @@ -418,7 +418,7 @@ struct acpi_handler_info { union acpi_gpe_dispatch_info { struct acpi_namespace_node *method_node; /* Method node for this GPE level */ - struct acpi_handler_info *handler; /* Installed GPE handler */ + struct acpi_gpe_handler_info *handler; /* Installed GPE handler */ }; /* diff --git a/drivers/acpi/acpica/evgpe.c b/drivers/acpi/acpica/evgpe.c index 005c170d2448..3fb621eec2fa 100644 --- a/drivers/acpi/acpica/evgpe.c +++ b/drivers/acpi/acpica/evgpe.c @@ -409,7 +409,9 @@ u32 acpi_ev_gpe_detect(struct acpi_gpe_xrupt_info * gpe_xrupt_list) * or method. */ int_status |= - acpi_ev_gpe_dispatch(&gpe_block-> + acpi_ev_gpe_dispatch(gpe_block-> + node, + &gpe_block-> event_info[((acpi_size) i * ACPI_GPE_REGISTER_WIDTH) + j], j + gpe_register_info->base_gpe_number); } } @@ -542,7 +544,8 @@ static void acpi_ev_asynch_enable_gpe(void *context) * * FUNCTION: acpi_ev_gpe_dispatch * - * PARAMETERS: gpe_event_info - Info for this GPE + * PARAMETERS: gpe_device - Device node. NULL for GPE0/GPE1 + * gpe_event_info - Info for this GPE * gpe_number - Number relative to the parent GPE block * * RETURN: INTERRUPT_HANDLED or INTERRUPT_NOT_HANDLED @@ -555,7 +558,8 @@ static void acpi_ev_asynch_enable_gpe(void *context) ******************************************************************************/ u32 -acpi_ev_gpe_dispatch(struct acpi_gpe_event_info *gpe_event_info, u32 gpe_number) +acpi_ev_gpe_dispatch(struct acpi_namespace_node *gpe_device, + struct acpi_gpe_event_info *gpe_event_info, u32 gpe_number) { acpi_status status; @@ -593,7 +597,9 @@ acpi_ev_gpe_dispatch(struct acpi_gpe_event_info *gpe_event_info, u32 gpe_number) * Ignore return status for now. * TBD: leave GPE disabled on error? */ - (void)gpe_event_info->dispatch.handler->address(gpe_event_info-> + (void)gpe_event_info->dispatch.handler->address(gpe_device, + gpe_number, + gpe_event_info-> dispatch. handler-> context); diff --git a/drivers/acpi/acpica/evxface.c b/drivers/acpi/acpica/evxface.c index 042a6d69f38e..d193b905dfa2 100644 --- a/drivers/acpi/acpica/evxface.c +++ b/drivers/acpi/acpica/evxface.c @@ -671,10 +671,10 @@ ACPI_EXPORT_SYMBOL(acpi_remove_notify_handler) acpi_status acpi_install_gpe_handler(acpi_handle gpe_device, u32 gpe_number, - u32 type, acpi_event_handler address, void *context) + u32 type, acpi_gpe_handler address, void *context) { struct acpi_gpe_event_info *gpe_event_info; - struct acpi_handler_info *handler; + struct acpi_gpe_handler_info *handler; acpi_status status; acpi_cpu_flags flags; @@ -693,7 +693,7 @@ acpi_install_gpe_handler(acpi_handle gpe_device, /* Allocate memory for the handler object */ - handler = ACPI_ALLOCATE_ZEROED(sizeof(struct acpi_handler_info)); + handler = ACPI_ALLOCATE_ZEROED(sizeof(struct acpi_gpe_handler_info)); if (!handler) { status = AE_NO_MEMORY; goto unlock_and_exit; @@ -777,10 +777,10 @@ ACPI_EXPORT_SYMBOL(acpi_install_gpe_handler) ******************************************************************************/ acpi_status acpi_remove_gpe_handler(acpi_handle gpe_device, - u32 gpe_number, acpi_event_handler address) + u32 gpe_number, acpi_gpe_handler address) { struct acpi_gpe_event_info *gpe_event_info; - struct acpi_handler_info *handler; + struct acpi_gpe_handler_info *handler; acpi_status status; acpi_cpu_flags flags; diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index 302b31ed31f1..81887724de80 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -606,7 +606,8 @@ static int ec_check_sci(struct acpi_ec *ec, u8 state) return 0; } -static u32 acpi_ec_gpe_handler(void *data) +static u32 acpi_ec_gpe_handler(acpi_handle gpe_device, + u32 gpe_number, void *data) { struct acpi_ec *ec = data; diff --git a/drivers/char/ipmi/ipmi_si_intf.c b/drivers/char/ipmi/ipmi_si_intf.c index 035da9e64a17..c4bacc6ec450 100644 --- a/drivers/char/ipmi/ipmi_si_intf.c +++ b/drivers/char/ipmi/ipmi_si_intf.c @@ -1928,7 +1928,8 @@ static void __devinit hardcode_find_bmc(void) static int acpi_failure; /* For GPE-type interrupts. */ -static u32 ipmi_acpi_gpe(void *context) +static u32 ipmi_acpi_gpe(acpi_handle gpe_device, + u32 gpe_number, void *context) { struct smi_info *smi_info = context; unsigned long flags; diff --git a/include/acpi/acpixf.h b/include/acpi/acpixf.h index 5f8ccf1b5ae9..b806e5611c90 100644 --- a/include/acpi/acpixf.h +++ b/include/acpi/acpixf.h @@ -258,11 +258,11 @@ acpi_remove_address_space_handler(acpi_handle device, acpi_status acpi_install_gpe_handler(acpi_handle gpe_device, u32 gpe_number, - u32 type, acpi_event_handler address, void *context); + u32 type, acpi_gpe_handler address, void *context); acpi_status acpi_remove_gpe_handler(acpi_handle gpe_device, - u32 gpe_number, acpi_event_handler address); + u32 gpe_number, acpi_gpe_handler address); #ifdef ACPI_FUTURE_USAGE acpi_status acpi_install_exception_handler(acpi_exception_handler handler); diff --git a/include/acpi/actypes.h b/include/acpi/actypes.h index e73893994b5d..b9575ad9b748 100644 --- a/include/acpi/actypes.h +++ b/include/acpi/actypes.h @@ -896,6 +896,9 @@ typedef void */ typedef u32(*acpi_event_handler) (void *context); +typedef +u32 (*acpi_gpe_handler) (acpi_handle gpe_device, u32 gpe_number, void *context); + typedef void (*acpi_notify_handler) (acpi_handle device, u32 value, void *context); -- cgit v1.2.3 From 84f6b2a681cd2cc55ecc3fa94bfbe672d7ef4126 Mon Sep 17 00:00:00 2001 From: Lin Ming Date: Mon, 13 Dec 2010 13:38:55 +0800 Subject: ACPICA: Remove unused function declarations Acked-by: Rafael J. Wysocki Signed-off-by: Lin Ming Signed-off-by: Len Brown --- drivers/acpi/acpica/acevents.h | 6 ------ 1 file changed, 6 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/acpica/acevents.h b/drivers/acpi/acpica/acevents.h index ebb467fe3678..04a83fe47d6f 100644 --- a/drivers/acpi/acpica/acevents.h +++ b/drivers/acpi/acpica/acevents.h @@ -51,8 +51,6 @@ acpi_status acpi_ev_initialize_events(void); acpi_status acpi_ev_install_xrupt_handlers(void); -acpi_status acpi_ev_install_fadt_gpes(void); - u32 acpi_ev_fixed_event_detect(void); /* @@ -127,10 +125,6 @@ acpi_status acpi_ev_match_gpe_method(acpi_handle obj_handle, u32 level, void *context, void **return_value); -acpi_status -acpi_ev_match_prw_and_gpe(acpi_handle obj_handle, - u32 level, void *context, void **return_value); - /* * evgpeutil - GPE utilities */ -- cgit v1.2.3 From 5a284cd75d635e3c5db0210dc9a9a44c6839f460 Mon Sep 17 00:00:00 2001 From: Lin Ming Date: Mon, 13 Dec 2010 13:39:07 +0800 Subject: ACPICA: Fix local variable mess in acpi_ev_asynch_execute_gpe_method Change the local variable in acpi_ev_asynch_execute_gpe_method() back into a pointer as ACPICA code base does. Signed-off-by: Lin Ming Signed-off-by: Len Brown --- drivers/acpi/acpica/evgpe.c | 57 +++++++++++++++++++++++++++++++++++---------- 1 file changed, 45 insertions(+), 12 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/acpica/evgpe.c b/drivers/acpi/acpica/evgpe.c index 3fb621eec2fa..c25999546c8c 100644 --- a/drivers/acpi/acpica/evgpe.c +++ b/drivers/acpi/acpica/evgpe.c @@ -52,6 +52,8 @@ ACPI_MODULE_NAME("evgpe") /* Local prototypes */ static void ACPI_SYSTEM_XFACE acpi_ev_asynch_execute_gpe_method(void *context); +static void ACPI_SYSTEM_XFACE acpi_ev_asynch_enable_gpe(void *context); + /******************************************************************************* * * FUNCTION: acpi_ev_update_gpe_enable_mask @@ -441,17 +443,25 @@ 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) { - struct acpi_gpe_event_info *gpe_event_info = (void *)context; + struct acpi_gpe_event_info *gpe_event_info = context; acpi_status status; - struct acpi_gpe_event_info local_gpe_event_info; + struct acpi_gpe_event_info *local_gpe_event_info; struct acpi_evaluate_info *info; ACPI_FUNCTION_TRACE(ev_asynch_execute_gpe_method); + /* Allocate a local GPE block */ + + local_gpe_event_info = + ACPI_ALLOCATE_ZEROED(sizeof(struct acpi_gpe_event_info)); + if (!local_gpe_event_info) { + ACPI_EXCEPTION((AE_INFO, AE_NO_MEMORY, "while handling a GPE")); + return_VOID; + } + status = acpi_ut_acquire_mutex(ACPI_MTX_EVENTS); if (ACPI_FAILURE(status)) { return_VOID; @@ -468,7 +478,7 @@ static void ACPI_SYSTEM_XFACE acpi_ev_asynch_execute_gpe_method(void *context) * Take a snapshot of the GPE info for this level - we copy the info to * prevent a race condition with remove_handler/remove_block. */ - ACPI_MEMCPY(&local_gpe_event_info, gpe_event_info, + ACPI_MEMCPY(local_gpe_event_info, gpe_event_info, sizeof(struct acpi_gpe_event_info)); status = acpi_ut_release_mutex(ACPI_MTX_EVENTS); @@ -480,7 +490,7 @@ static void ACPI_SYSTEM_XFACE acpi_ev_asynch_execute_gpe_method(void *context) * Must check for control method type dispatch one more time to avoid a * race with ev_gpe_install_handler */ - if ((local_gpe_event_info.flags & ACPI_GPE_DISPATCH_MASK) == + if ((local_gpe_event_info->flags & ACPI_GPE_DISPATCH_MASK) == ACPI_GPE_DISPATCH_METHOD) { /* Allocate the evaluation information block */ @@ -494,7 +504,7 @@ static void ACPI_SYSTEM_XFACE acpi_ev_asynch_execute_gpe_method(void *context) * control method that corresponds to this GPE */ info->prefix_node = - local_gpe_event_info.dispatch.method_node; + local_gpe_event_info->dispatch.method_node; info->flags = ACPI_IGNORE_RETURN_VALUE; status = acpi_ns_evaluate(info); @@ -505,20 +515,41 @@ static void ACPI_SYSTEM_XFACE acpi_ev_asynch_execute_gpe_method(void *context) ACPI_EXCEPTION((AE_INFO, status, "while evaluating GPE method [%4.4s]", acpi_ut_get_node_name - (local_gpe_event_info.dispatch. + (local_gpe_event_info->dispatch. 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); + + status = acpi_os_execute(OSL_NOTIFY_HANDLER, + acpi_ev_asynch_enable_gpe, + local_gpe_event_info); + if (ACPI_FAILURE(status)) { + ACPI_FREE(local_gpe_event_info); + } return_VOID; } -static void acpi_ev_asynch_enable_gpe(void *context) +/******************************************************************************* + * + * FUNCTION: acpi_ev_asynch_enable_gpe + * + * PARAMETERS: Context (gpe_event_info) - Info for this GPE + * Callback from acpi_os_execute + * + * RETURN: None + * + * DESCRIPTION: Asynchronous clear/enable for GPE. This allows the GPE to + * complete. + * + ******************************************************************************/ + +static void ACPI_SYSTEM_XFACE 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) { /* @@ -527,7 +558,7 @@ static void acpi_ev_asynch_enable_gpe(void *context) */ status = acpi_hw_clear_gpe(gpe_event_info); if (ACPI_FAILURE(status)) { - return_VOID; + goto exit; } } @@ -537,7 +568,9 @@ static void acpi_ev_asynch_enable_gpe(void *context) */ (void)acpi_hw_low_set_gpe(gpe_event_info, ACPI_GPE_CONDITIONAL_ENABLE); - return_VOID; +exit: + ACPI_FREE(gpe_event_info); + return; } /******************************************************************************* -- cgit v1.2.3 From bba63a296ffab20e08d9e8252d2f0d99050ac859 Mon Sep 17 00:00:00 2001 From: Lin Ming Date: Mon, 13 Dec 2010 13:39:17 +0800 Subject: ACPICA: Implicit notify support This feature provides an automatic device notification for wake devices when a wakeup GPE occurs and there is no corresponding GPE method or handler. Rather than ignoring such a GPE, an implicit AML Notify operation is performed on the parent device object. This feature is not part of the ACPI specification and is provided for Windows compatibility only. Signed-off-by: Lin Ming Signed-off-by: Len Brown --- drivers/acpi/acpica/acevents.h | 2 + drivers/acpi/acpica/aclocal.h | 1 + drivers/acpi/acpica/evgpe.c | 172 +++++++++++++++++++++++----------------- drivers/acpi/acpica/evgpeblk.c | 11 ++- drivers/acpi/acpica/evgpeinit.c | 1 + drivers/acpi/acpica/evxfgpe.c | 58 +++++++++++--- drivers/acpi/ec.c | 2 +- drivers/acpi/scan.c | 2 +- include/acpi/acpixf.h | 6 +- include/acpi/actypes.h | 37 +++++---- 10 files changed, 186 insertions(+), 106 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/acpica/acevents.h b/drivers/acpi/acpica/acevents.h index 04a83fe47d6f..70e0b28801aa 100644 --- a/drivers/acpi/acpica/acevents.h +++ b/drivers/acpi/acpica/acevents.h @@ -91,6 +91,8 @@ struct acpi_gpe_event_info *acpi_ev_low_get_gpe_info(u32 gpe_number, struct acpi_gpe_block_info *gpe_block); +acpi_status acpi_ev_finish_gpe(struct acpi_gpe_event_info *gpe_event_info); + /* * evgpeblk - Upper-level GPE block support */ diff --git a/drivers/acpi/acpica/aclocal.h b/drivers/acpi/acpica/aclocal.h index 6a71f8e673e9..74000f5b7dab 100644 --- a/drivers/acpi/acpica/aclocal.h +++ b/drivers/acpi/acpica/aclocal.h @@ -419,6 +419,7 @@ struct acpi_gpe_handler_info { union acpi_gpe_dispatch_info { struct acpi_namespace_node *method_node; /* Method node for this GPE level */ struct acpi_gpe_handler_info *handler; /* Installed GPE handler */ + struct acpi_namespace_node *device_node; /* Parent _PRW device for implicit notify */ }; /* diff --git a/drivers/acpi/acpica/evgpe.c b/drivers/acpi/acpica/evgpe.c index c25999546c8c..2dbca95979ce 100644 --- a/drivers/acpi/acpica/evgpe.c +++ b/drivers/acpi/acpica/evgpe.c @@ -115,12 +115,13 @@ acpi_ev_enable_gpe(struct acpi_gpe_event_info *gpe_event_info) ACPI_FUNCTION_TRACE(ev_enable_gpe); /* - * We will only allow a GPE to be enabled if it has either an - * associated method (_Lxx/_Exx) or a handler. Otherwise, the - * GPE will be immediately disabled by acpi_ev_gpe_dispatch the - * first time it fires. + * We will only allow a GPE to be enabled if it has either an associated + * method (_Lxx/_Exx) or a handler, or is using the implicit notify + * feature. Otherwise, the GPE will be immediately disabled by + * acpi_ev_gpe_dispatch the first time it fires. */ - if (!(gpe_event_info->flags & ACPI_GPE_DISPATCH_MASK)) { + if ((gpe_event_info->flags & ACPI_GPE_DISPATCH_MASK) == + ACPI_GPE_DISPATCH_NONE) { return_ACPI_STATUS(AE_NO_HANDLER); } @@ -486,12 +487,26 @@ static void ACPI_SYSTEM_XFACE acpi_ev_asynch_execute_gpe_method(void *context) return_VOID; } - /* - * Must check for control method type dispatch one more time to avoid a - * race with ev_gpe_install_handler - */ - if ((local_gpe_event_info->flags & ACPI_GPE_DISPATCH_MASK) == - ACPI_GPE_DISPATCH_METHOD) { + /* Do the correct dispatch - normal method or implicit notify */ + + switch (local_gpe_event_info->flags & ACPI_GPE_DISPATCH_MASK) { + case ACPI_GPE_DISPATCH_NOTIFY: + + /* + * Implicit notify. + * Dispatch a DEVICE_WAKE notify to the appropriate handler. + * NOTE: the request is queued for execution after this method + * completes. The notify handlers are NOT invoked synchronously + * from this thread -- because handlers may in turn run other + * control methods. + */ + status = + acpi_ev_queue_notify_request(local_gpe_event_info->dispatch. + device_node, + ACPI_NOTIFY_DEVICE_WAKE); + break; + + case ACPI_GPE_DISPATCH_METHOD: /* Allocate the evaluation information block */ @@ -518,6 +533,11 @@ static void ACPI_SYSTEM_XFACE acpi_ev_asynch_execute_gpe_method(void *context) (local_gpe_event_info->dispatch. method_node))); } + + break; + + default: + return_VOID; /* Should never happen */ } /* Defer enabling of GPE until all notify handlers are done */ @@ -531,6 +551,7 @@ static void ACPI_SYSTEM_XFACE acpi_ev_asynch_execute_gpe_method(void *context) return_VOID; } + /******************************************************************************* * * FUNCTION: acpi_ev_asynch_enable_gpe @@ -541,38 +562,60 @@ static void ACPI_SYSTEM_XFACE acpi_ev_asynch_execute_gpe_method(void *context) * RETURN: None * * DESCRIPTION: Asynchronous clear/enable for GPE. This allows the GPE to - * complete. + * complete (i.e., finish execution of Notify) * ******************************************************************************/ static void ACPI_SYSTEM_XFACE acpi_ev_asynch_enable_gpe(void *context) { struct acpi_gpe_event_info *gpe_event_info = context; + + (void)acpi_ev_finish_gpe(gpe_event_info); + + ACPI_FREE(gpe_event_info); + return; +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ev_finish_gpe + * + * PARAMETERS: gpe_event_info - Info for this GPE + * + * RETURN: Status + * + * DESCRIPTION: Clear/Enable a GPE. Common code that is used after execution + * of a GPE method or a synchronous or asynchronous GPE handler. + * + ******************************************************************************/ + +acpi_status acpi_ev_finish_gpe(struct acpi_gpe_event_info *gpe_event_info) +{ 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. + * GPE is level-triggered, we clear the GPE status bit after + * handling the event. */ status = acpi_hw_clear_gpe(gpe_event_info); if (ACPI_FAILURE(status)) { - goto exit; + return (status); } } /* - * Enable this GPE, conditionally. This means that the GPE will only be - * physically enabled if the enable_for_run bit is set in the event_info + * Enable this GPE, conditionally. This means that the GPE will + * only be physically enabled if the enable_for_run bit is set + * in the event_info. */ (void)acpi_hw_low_set_gpe(gpe_event_info, ACPI_GPE_CONDITIONAL_ENABLE); - -exit: - ACPI_FREE(gpe_event_info); - return; + return (AE_OK); } + /******************************************************************************* * * FUNCTION: acpi_ev_gpe_dispatch @@ -595,6 +638,7 @@ acpi_ev_gpe_dispatch(struct acpi_namespace_node *gpe_device, struct acpi_gpe_event_info *gpe_event_info, u32 gpe_number) { acpi_status status; + u32 return_value; ACPI_FUNCTION_TRACE(ev_gpe_dispatch); @@ -616,54 +660,49 @@ acpi_ev_gpe_dispatch(struct acpi_namespace_node *gpe_device, } /* - * Dispatch the GPE to either an installed handler, or the control method - * associated with this GPE (_Lxx or _Exx). If a handler exists, we invoke - * it and do not attempt to run the method. If there is neither a handler - * nor a method, we disable this GPE to prevent further such pointless - * events from firing. + * Always disable the GPE so that it does not keep firing before + * any asynchronous activity completes (either from the execution + * of a GPE method or an asynchronous GPE handler.) + * + * If there is no handler or method to run, just disable the + * GPE and leave it disabled permanently to prevent further such + * pointless events from firing. + */ + status = acpi_hw_low_set_gpe(gpe_event_info, ACPI_GPE_DISABLE); + if (ACPI_FAILURE(status)) { + ACPI_EXCEPTION((AE_INFO, status, + "Unable to disable GPE%02X", gpe_number)); + return_UINT32(ACPI_INTERRUPT_NOT_HANDLED); + } + + /* + * Dispatch the GPE to either an installed handler or the control + * method associated with this GPE (_Lxx or _Exx). If a handler + * exists, we invoke it and do not attempt to run the method. + * If there is neither a handler nor a method, leave the GPE + * disabled. */ switch (gpe_event_info->flags & ACPI_GPE_DISPATCH_MASK) { case ACPI_GPE_DISPATCH_HANDLER: - /* - * Invoke the installed handler (at interrupt level) - * Ignore return status for now. - * TBD: leave GPE disabled on error? - */ - (void)gpe_event_info->dispatch.handler->address(gpe_device, - gpe_number, - gpe_event_info-> - dispatch. - handler-> - context); - - /* It is now safe to clear level-triggered events. */ - - if ((gpe_event_info->flags & ACPI_GPE_XRUPT_TYPE_MASK) == - ACPI_GPE_LEVEL_TRIGGERED) { - status = acpi_hw_clear_gpe(gpe_event_info); - if (ACPI_FAILURE(status)) { - ACPI_EXCEPTION((AE_INFO, status, - "Unable to clear GPE[0x%2X]", - gpe_number)); - return_UINT32(ACPI_INTERRUPT_NOT_HANDLED); - } + /* Invoke the installed handler (at interrupt level) */ + + return_value = + gpe_event_info->dispatch.handler->address(gpe_device, + gpe_number, + gpe_event_info-> + dispatch.handler-> + context); + + /* If requested, clear (if level-triggered) and reenable the GPE */ + + if (return_value & ACPI_REENABLE_GPE) { + (void)acpi_ev_finish_gpe(gpe_event_info); } break; case ACPI_GPE_DISPATCH_METHOD: - - /* - * Disable the GPE, so it doesn't keep firing before the method has a - * chance to run (it runs asynchronously with interrupts enabled). - */ - status = acpi_hw_low_set_gpe(gpe_event_info, ACPI_GPE_DISABLE); - if (ACPI_FAILURE(status)) { - ACPI_EXCEPTION((AE_INFO, status, - "Unable to disable GPE[0x%2X]", - gpe_number)); - return_UINT32(ACPI_INTERRUPT_NOT_HANDLED); - } + case ACPI_GPE_DISPATCH_NOTIFY: /* * Execute the method associated with the GPE @@ -690,17 +729,6 @@ acpi_ev_gpe_dispatch(struct acpi_namespace_node *gpe_device, "No handler or method for GPE[0x%2X], disabling event", gpe_number)); - /* - * Disable the GPE. The GPE will remain disabled a handler - * is installed or ACPICA is restarted. - */ - status = acpi_hw_low_set_gpe(gpe_event_info, ACPI_GPE_DISABLE); - if (ACPI_FAILURE(status)) { - ACPI_EXCEPTION((AE_INFO, status, - "Unable to disable GPE[0x%2X]", - gpe_number)); - return_UINT32(ACPI_INTERRUPT_NOT_HANDLED); - } break; } diff --git a/drivers/acpi/acpica/evgpeblk.c b/drivers/acpi/acpica/evgpeblk.c index 2a445dfcfdb4..e2e8164bb811 100644 --- a/drivers/acpi/acpica/evgpeblk.c +++ b/drivers/acpi/acpica/evgpeblk.c @@ -472,9 +472,14 @@ acpi_ev_initialize_gpe_block(struct acpi_gpe_xrupt_info *gpe_xrupt_info, gpe_index = (i * ACPI_GPE_REGISTER_WIDTH) + j; gpe_event_info = &gpe_block->event_info[gpe_index]; - /* Ignore GPEs that have no corresponding _Lxx/_Exx method */ - - if (!(gpe_event_info->flags & ACPI_GPE_DISPATCH_METHOD) + /* + * Ignore GPEs that have no corresponding _Lxx/_Exx method + * and GPEs that are used to wake the system + */ + if (((gpe_event_info->flags & ACPI_GPE_DISPATCH_MASK) == + ACPI_GPE_DISPATCH_NONE) + || ((gpe_event_info->flags & ACPI_GPE_DISPATCH_MASK) + == ACPI_GPE_DISPATCH_HANDLER) || (gpe_event_info->flags & ACPI_GPE_CAN_WAKE)) { continue; } diff --git a/drivers/acpi/acpica/evgpeinit.c b/drivers/acpi/acpica/evgpeinit.c index 4c8dea513b66..734a494bd705 100644 --- a/drivers/acpi/acpica/evgpeinit.c +++ b/drivers/acpi/acpica/evgpeinit.c @@ -415,6 +415,7 @@ acpi_ev_match_gpe_method(acpi_handle obj_handle, * Add the GPE information from above to the gpe_event_info block for * use during dispatch of this GPE. */ + gpe_event_info->flags &= ~(ACPI_GPE_DISPATCH_MASK); gpe_event_info->flags |= (u8)(type | ACPI_GPE_DISPATCH_METHOD); gpe_event_info->dispatch.method_node = method_node; diff --git a/drivers/acpi/acpica/evxfgpe.c b/drivers/acpi/acpica/evxfgpe.c index 99f77ab29729..fcf4a5cdc9a4 100644 --- a/drivers/acpi/acpica/evxfgpe.c +++ b/drivers/acpi/acpica/evxfgpe.c @@ -166,39 +166,75 @@ acpi_status acpi_disable_gpe(acpi_handle gpe_device, u32 gpe_number) } ACPI_EXPORT_SYMBOL(acpi_disable_gpe) + /******************************************************************************* * * FUNCTION: acpi_setup_gpe_for_wake * - * PARAMETERS: gpe_device - Parent GPE Device. NULL for GPE0/GPE1 - * gpe_number - GPE level within the GPE block + * PARAMETERS: wake_device - Device associated with the GPE (via _PRW) + * gpe_device - Parent GPE Device. NULL for GPE0/GPE1 + * gpe_number - GPE level within the GPE block * * RETURN: Status * - * DESCRIPTION: Set the ACPI_GPE_CAN_WAKE flag for the given GPE. If the GPE - * has a corresponding method and is currently enabled, disable it - * (GPEs with corresponding methods are enabled unconditionally - * during initialization, but GPEs that can wake up are expected - * to be initially disabled). + * DESCRIPTION: Mark a GPE as having the ability to wake the system. This + * interface is intended to be used as the host executes the + * _PRW methods (Power Resources for Wake) in the system tables. + * Each _PRW appears under a Device Object (The wake_device), and + * contains the info for the wake GPE associated with the + * wake_device. * ******************************************************************************/ -acpi_status acpi_setup_gpe_for_wake(acpi_handle gpe_device, u32 gpe_number) +acpi_status +acpi_setup_gpe_for_wake(acpi_handle wake_device, + acpi_handle gpe_device, u32 gpe_number) { - acpi_status status = AE_OK; + acpi_status status = AE_BAD_PARAMETER; struct acpi_gpe_event_info *gpe_event_info; + struct acpi_namespace_node *device_node; acpi_cpu_flags flags; ACPI_FUNCTION_TRACE(acpi_setup_gpe_for_wake); + /* Parameter Validation */ + + if (!wake_device) { + /* + * By forcing wake_device to be valid, we automatically enable the + * implicit notify feature on all hosts. + */ + return_ACPI_STATUS(AE_BAD_PARAMETER); + } + + /* Validate wake_device is of type Device */ + + device_node = ACPI_CAST_PTR(struct acpi_namespace_node, wake_device); + if (device_node->type != ACPI_TYPE_DEVICE) { + return_ACPI_STATUS(AE_BAD_PARAMETER); + } + flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock); /* Ensure that we have a valid GPE number */ gpe_event_info = acpi_ev_get_gpe_event_info(gpe_device, gpe_number); if (gpe_event_info) { + /* + * If there is no method or handler for this GPE, then the + * wake_device will be notified whenever this GPE fires (aka + * "implicit notify") Note: The GPE is assumed to be + * level-triggered (for windows compatibility). + */ + if ((gpe_event_info->flags & ACPI_GPE_DISPATCH_MASK) == + ACPI_GPE_DISPATCH_NONE) { + gpe_event_info->flags = + (ACPI_GPE_DISPATCH_NOTIFY | + ACPI_GPE_LEVEL_TRIGGERED); + gpe_event_info->dispatch.device_node = device_node; + } + gpe_event_info->flags |= ACPI_GPE_CAN_WAKE; - } else { - status = AE_BAD_PARAMETER; + status = AE_OK; } acpi_os_release_lock(acpi_gbl_gpe_lock, flags); diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index 81887724de80..fa848c4116a8 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -619,7 +619,7 @@ static u32 acpi_ec_gpe_handler(acpi_handle gpe_device, wake_up(&ec->wait); ec_check_sci(ec, acpi_ec_read_status(ec)); } - return ACPI_INTERRUPT_HANDLED; + return ACPI_INTERRUPT_HANDLED | ACPI_REENABLE_GPE; } /* -------------------------------------------------------------------------- diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 70249b3d2934..ce6741ee1965 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -778,7 +778,7 @@ acpi_bus_extract_wakeup_device_power_package(acpi_handle handle, wakeup->resources.handles[i] = element->reference.handle; } - acpi_setup_gpe_for_wake(wakeup->gpe_device, wakeup->gpe_number); + acpi_setup_gpe_for_wake(handle, wakeup->gpe_device, wakeup->gpe_number); out: kfree(buffer.pointer); diff --git a/include/acpi/acpixf.h b/include/acpi/acpixf.h index b806e5611c90..9de6a17cbd4c 100644 --- a/include/acpi/acpixf.h +++ b/include/acpi/acpixf.h @@ -292,10 +292,12 @@ acpi_status acpi_enable_gpe(acpi_handle gpe_device, u32 gpe_number); acpi_status acpi_disable_gpe(acpi_handle gpe_device, u32 gpe_number); -acpi_status acpi_setup_gpe_for_wake(acpi_handle gpe_device, u32 gpe_number); - acpi_status acpi_clear_gpe(acpi_handle gpe_device, u32 gpe_number); +acpi_status +acpi_setup_gpe_for_wake(acpi_handle parent_device, + acpi_handle gpe_device, u32 gpe_number); + acpi_status acpi_set_gpe_wake_mask(acpi_handle gpe_device, u32 gpe_number, u8 action); acpi_status diff --git a/include/acpi/actypes.h b/include/acpi/actypes.h index b9575ad9b748..d7274ee4474f 100644 --- a/include/acpi/actypes.h +++ b/include/acpi/actypes.h @@ -664,25 +664,26 @@ typedef u32 acpi_event_status; /* * GPE info flags - Per GPE - * +-------+---+-+-+ - * | 7:4 |3:2|1|0| - * +-------+---+-+-+ - * | | | | - * | | | +--- Interrupt type: edge or level triggered - * | | +----- GPE can wake the system - * | +-------- Type of dispatch:to method, handler, or none - * +-------------- + * +-------+-+-+---+ + * | 7:4 |3|2|1:0| + * +-------+-+-+---+ + * | | | | + * | | | +-- Type of dispatch:to method, handler, notify, or none + * | | +----- Interrupt type: edge or level triggered + * | +------- Is a Wake GPE + * +------------ */ -#define ACPI_GPE_XRUPT_TYPE_MASK (u8) 0x01 -#define ACPI_GPE_LEVEL_TRIGGERED (u8) 0x01 -#define ACPI_GPE_EDGE_TRIGGERED (u8) 0x00 +#define ACPI_GPE_DISPATCH_NONE (u8) 0x00 +#define ACPI_GPE_DISPATCH_METHOD (u8) 0x01 +#define ACPI_GPE_DISPATCH_HANDLER (u8) 0x02 +#define ACPI_GPE_DISPATCH_NOTIFY (u8) 0x03 +#define ACPI_GPE_DISPATCH_MASK (u8) 0x03 -#define ACPI_GPE_CAN_WAKE (u8) 0x02 +#define ACPI_GPE_LEVEL_TRIGGERED (u8) 0x04 +#define ACPI_GPE_EDGE_TRIGGERED (u8) 0x00 +#define ACPI_GPE_XRUPT_TYPE_MASK (u8) 0x04 -#define ACPI_GPE_DISPATCH_MASK (u8) 0x0C -#define ACPI_GPE_DISPATCH_HANDLER (u8) 0x04 -#define ACPI_GPE_DISPATCH_METHOD (u8) 0x08 -#define ACPI_GPE_DISPATCH_NOT_USED (u8) 0x00 +#define ACPI_GPE_CAN_WAKE (u8) 0x08 /* * Flags for GPE and Lock interfaces @@ -954,6 +955,10 @@ u32 (*acpi_interface_handler) (acpi_string interface_name, u32 supported); #define ACPI_INTERRUPT_NOT_HANDLED 0x00 #define ACPI_INTERRUPT_HANDLED 0x01 +/* GPE handler return values */ + +#define ACPI_REENABLE_GPE 0x80 + /* Length of 32-bit EISAID values when converted back to a string */ #define ACPI_EISAID_STRING_SIZE 8 /* Includes null terminator */ -- cgit v1.2.3 From a0fcdb237fcd4eaa7e5009b28ef5be07415f287d Mon Sep 17 00:00:00 2001 From: Lin Ming Date: Mon, 13 Dec 2010 13:39:26 +0800 Subject: ACPICA: Global event handler The global event handler is called whenever a general purpose or fixed ACPI event occurs. Also update Linux OSL to collect events counter with global event handler. Signed-off-by: Lin Ming Signed-off-by: Len Brown --- drivers/acpi/acpica/acglobal.h | 5 +++++ drivers/acpi/acpica/evevent.c | 12 ++++++++-- drivers/acpi/acpica/evgpe.c | 9 +++++++- drivers/acpi/acpica/evxface.c | 51 ++++++++++++++++++++++++++++++++++++++++++ drivers/acpi/acpica/utglobal.c | 1 + drivers/acpi/sysfs.c | 19 ++++++++++++++-- include/acpi/acpixf.h | 4 ++++ include/acpi/actypes.h | 8 +++++++ 8 files changed, 104 insertions(+), 5 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/acpica/acglobal.h b/drivers/acpi/acpica/acglobal.h index fc694647699f..9bb69c59bb12 100644 --- a/drivers/acpi/acpica/acglobal.h +++ b/drivers/acpi/acpica/acglobal.h @@ -146,6 +146,9 @@ u8 acpi_gbl_system_awake_and_running; extern u32 acpi_gbl_nesting_level; +ACPI_EXTERN u32 acpi_gpe_count; +ACPI_EXTERN u32 acpi_fixed_event_count[ACPI_NUM_FIXED_EVENTS]; + /* Support for dynamic control method tracing mechanism */ ACPI_EXTERN u32 acpi_gbl_original_dbg_level; @@ -371,6 +374,8 @@ ACPI_EXTERN struct acpi_gpe_xrupt_info *acpi_gbl_gpe_xrupt_list_head; ACPI_EXTERN struct acpi_gpe_block_info *acpi_gbl_gpe_fadt_blocks[ACPI_MAX_GPE_BLOCKS]; ACPI_EXTERN u8 acpi_gbl_all_gpes_initialized; +ACPI_EXTERN ACPI_GBL_EVENT_HANDLER acpi_gbl_global_event_handler; +ACPI_EXTERN void *acpi_gbl_global_event_handler_context; /***************************************************************************** * diff --git a/drivers/acpi/acpica/evevent.c b/drivers/acpi/acpica/evevent.c index c61c3039c31a..e5e313c663a5 100644 --- a/drivers/acpi/acpica/evevent.c +++ b/drivers/acpi/acpica/evevent.c @@ -217,9 +217,17 @@ u32 acpi_ev_fixed_event_detect(void) status_bit_mask) && (fixed_enable & acpi_gbl_fixed_event_info[i]. enable_bit_mask)) { + /* + * Found an active (signalled) event. Invoke global event + * handler if present. + */ + acpi_fixed_event_count[i]++; + if (acpi_gbl_global_event_handler) { + acpi_gbl_global_event_handler + (ACPI_EVENT_TYPE_FIXED, NULL, i, + acpi_gbl_global_event_handler_context); + } - /* Found an active (signalled) event */ - acpi_os_fixed_event_count(i); int_status |= acpi_ev_fixed_event_dispatch(i); } } diff --git a/drivers/acpi/acpica/evgpe.c b/drivers/acpi/acpica/evgpe.c index 2dbca95979ce..3bcf5ef74c62 100644 --- a/drivers/acpi/acpica/evgpe.c +++ b/drivers/acpi/acpica/evgpe.c @@ -642,7 +642,14 @@ acpi_ev_gpe_dispatch(struct acpi_namespace_node *gpe_device, ACPI_FUNCTION_TRACE(ev_gpe_dispatch); - acpi_os_gpe_count(gpe_number); + /* Invoke global event handler if present */ + + acpi_gpe_count++; + if (acpi_gbl_global_event_handler) { + acpi_gbl_global_event_handler(ACPI_EVENT_TYPE_GPE, gpe_device, + gpe_number, + acpi_gbl_global_event_handler_context); + } /* * If edge-triggered, clear the GPE status bit now. Note that diff --git a/drivers/acpi/acpica/evxface.c b/drivers/acpi/acpica/evxface.c index d193b905dfa2..1226689bdb1b 100644 --- a/drivers/acpi/acpica/evxface.c +++ b/drivers/acpi/acpica/evxface.c @@ -92,6 +92,57 @@ acpi_status acpi_install_exception_handler(acpi_exception_handler handler) ACPI_EXPORT_SYMBOL(acpi_install_exception_handler) #endif /* ACPI_FUTURE_USAGE */ + +/******************************************************************************* + * + * FUNCTION: acpi_install_global_event_handler + * + * PARAMETERS: Handler - Pointer to the global event handler function + * Context - Value passed to the handler on each event + * + * RETURN: Status + * + * DESCRIPTION: Saves the pointer to the handler function. The global handler + * is invoked upon each incoming GPE and Fixed Event. It is + * invoked at interrupt level at the time of the event dispatch. + * Can be used to update event counters, etc. + * + ******************************************************************************/ +acpi_status +acpi_install_global_event_handler(ACPI_GBL_EVENT_HANDLER handler, void *context) +{ + acpi_status status; + + ACPI_FUNCTION_TRACE(acpi_install_global_event_handler); + + /* Parameter validation */ + + if (!handler) { + return_ACPI_STATUS(AE_BAD_PARAMETER); + } + + status = acpi_ut_acquire_mutex(ACPI_MTX_EVENTS); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + /* Don't allow two handlers. */ + + if (acpi_gbl_global_event_handler) { + status = AE_ALREADY_EXISTS; + goto cleanup; + } + + acpi_gbl_global_event_handler = handler; + acpi_gbl_global_event_handler_context = context; + + cleanup: + (void)acpi_ut_release_mutex(ACPI_MTX_EVENTS); + return_ACPI_STATUS(status); +} + +ACPI_EXPORT_SYMBOL(acpi_install_global_event_handler) + /******************************************************************************* * * FUNCTION: acpi_install_fixed_event_handler diff --git a/drivers/acpi/acpica/utglobal.c b/drivers/acpi/acpica/utglobal.c index a99c32aa48a4..508537f884ac 100644 --- a/drivers/acpi/acpica/utglobal.c +++ b/drivers/acpi/acpica/utglobal.c @@ -778,6 +778,7 @@ acpi_status acpi_ut_init_globals(void) acpi_gbl_init_handler = NULL; acpi_gbl_table_handler = NULL; acpi_gbl_interface_handler = NULL; + acpi_gbl_global_event_handler = NULL; /* Global Lock support */ diff --git a/drivers/acpi/sysfs.c b/drivers/acpi/sysfs.c index f8588f81048a..61891e75583d 100644 --- a/drivers/acpi/sysfs.c +++ b/drivers/acpi/sysfs.c @@ -438,7 +438,7 @@ static void delete_gpe_attr_array(void) return; } -void acpi_os_gpe_count(u32 gpe_number) +static void gpe_count(u32 gpe_number) { acpi_gpe_count++; @@ -454,7 +454,7 @@ void acpi_os_gpe_count(u32 gpe_number) return; } -void acpi_os_fixed_event_count(u32 event_number) +static void fixed_event_count(u32 event_number) { if (!all_counters) return; @@ -468,6 +468,16 @@ void acpi_os_fixed_event_count(u32 event_number) return; } +static void acpi_gbl_event_handler(u32 event_type, acpi_handle device, + u32 event_number, void *context) +{ + if (event_type == ACPI_EVENT_TYPE_GPE) + gpe_count(event_number); + + if (event_type == ACPI_EVENT_TYPE_FIXED) + fixed_event_count(event_number); +} + static int get_status(u32 index, acpi_event_status *status, acpi_handle *handle) { @@ -601,6 +611,7 @@ end: void acpi_irq_stats_init(void) { + acpi_status status; int i; if (all_counters) @@ -619,6 +630,10 @@ void acpi_irq_stats_init(void) if (all_counters == NULL) goto fail; + status = acpi_install_global_event_handler(acpi_gbl_event_handler, NULL); + if (ACPI_FAILURE(status)) + goto fail; + counter_attrs = kzalloc(sizeof(struct kobj_attribute) * (num_counters), GFP_KERNEL); if (counter_attrs == NULL) diff --git a/include/acpi/acpixf.h b/include/acpi/acpixf.h index 9de6a17cbd4c..e8142d5ebab9 100644 --- a/include/acpi/acpixf.h +++ b/include/acpi/acpixf.h @@ -228,6 +228,10 @@ acpi_status acpi_get_parent(acpi_handle object, acpi_handle * out_handle); acpi_status acpi_install_initialization_handler(acpi_init_handler handler, u32 function); +acpi_status +acpi_install_global_event_handler(ACPI_GBL_EVENT_HANDLER handler, + void *context); + acpi_status acpi_install_fixed_event_handler(u32 acpi_event, acpi_event_handler handler, void *context); diff --git a/include/acpi/actypes.h b/include/acpi/actypes.h index d7274ee4474f..939a431a6ab6 100644 --- a/include/acpi/actypes.h +++ b/include/acpi/actypes.h @@ -895,6 +895,14 @@ typedef void /* * Various handlers and callback procedures */ +typedef +void (*ACPI_GBL_EVENT_HANDLER) (u32 event_type, + acpi_handle device, + u32 event_number, void *context); + +#define ACPI_EVENT_TYPE_GPE 0 +#define ACPI_EVENT_TYPE_FIXED 1 + typedef u32(*acpi_event_handler) (void *context); typedef -- cgit v1.2.3 From da50337373c90c15c6db6ed4239e87c5a3806f9a Mon Sep 17 00:00:00 2001 From: Lin Ming Date: Mon, 13 Dec 2010 13:39:37 +0800 Subject: ACPICA: Misc comments to minimize code divergence Modify/add some comments to minimize ACPICA/linux GPE code divergence. Acked-by: Rafael J. Wysocki Signed-off-by: Lin Ming Signed-off-by: Len Brown --- drivers/acpi/acpica/achware.h | 2 +- drivers/acpi/acpica/evgpe.c | 21 +++++++++++++-------- drivers/acpi/acpica/evgpeblk.c | 18 ++++++++---------- drivers/acpi/acpica/evgpeinit.c | 24 ++++++++++++++++++++---- drivers/acpi/acpica/evxfgpe.c | 32 +++++++++++++++++++++----------- drivers/acpi/acpica/hwgpe.c | 30 ++++++++++++++++++------------ 6 files changed, 81 insertions(+), 46 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/acpica/achware.h b/drivers/acpi/acpica/achware.h index 167470ad2d21..258d628793ea 100644 --- a/drivers/acpi/acpica/achware.h +++ b/drivers/acpi/acpica/achware.h @@ -94,7 +94,7 @@ u32 acpi_hw_get_gpe_register_bit(struct acpi_gpe_event_info *gpe_event_info, struct acpi_gpe_register_info *gpe_register_info); acpi_status -acpi_hw_low_set_gpe(struct acpi_gpe_event_info *gpe_event_info, u8 action); +acpi_hw_low_set_gpe(struct acpi_gpe_event_info *gpe_event_info, u32 action); acpi_status acpi_hw_disable_gpe_block(struct acpi_gpe_xrupt_info *gpe_xrupt_info, diff --git a/drivers/acpi/acpica/evgpe.c b/drivers/acpi/acpica/evgpe.c index 3bcf5ef74c62..7c339d34ab42 100644 --- a/drivers/acpi/acpica/evgpe.c +++ b/drivers/acpi/acpica/evgpe.c @@ -104,7 +104,7 @@ acpi_ev_update_gpe_enable_mask(struct acpi_gpe_event_info *gpe_event_info) * * RETURN: Status * - * DESCRIPTION: Clear the given GPE from stale events and enable it. + * DESCRIPTION: Clear a GPE of stale events and enable it. * ******************************************************************************/ acpi_status @@ -142,7 +142,7 @@ acpi_ev_enable_gpe(struct acpi_gpe_event_info *gpe_event_info) * * FUNCTION: acpi_ev_add_gpe_reference * - * PARAMETERS: gpe_event_info - GPE to enable + * PARAMETERS: gpe_event_info - Add a reference to this GPE * * RETURN: Status * @@ -163,6 +163,9 @@ acpi_status acpi_ev_add_gpe_reference(struct acpi_gpe_event_info *gpe_event_info gpe_event_info->runtime_count++; if (gpe_event_info->runtime_count == 1) { + + /* Enable on first reference */ + status = acpi_ev_update_gpe_enable_mask(gpe_event_info); if (ACPI_SUCCESS(status)) { status = acpi_ev_enable_gpe(gpe_event_info); @@ -180,7 +183,7 @@ acpi_status acpi_ev_add_gpe_reference(struct acpi_gpe_event_info *gpe_event_info * * FUNCTION: acpi_ev_remove_gpe_reference * - * PARAMETERS: gpe_event_info - GPE to disable + * PARAMETERS: gpe_event_info - Remove a reference to this GPE * * RETURN: Status * @@ -201,6 +204,9 @@ acpi_status acpi_ev_remove_gpe_reference(struct acpi_gpe_event_info *gpe_event_i gpe_event_info->runtime_count--; if (!gpe_event_info->runtime_count) { + + /* Disable on last reference */ + status = acpi_ev_update_gpe_enable_mask(gpe_event_info); if (ACPI_SUCCESS(status)) { status = acpi_hw_low_set_gpe(gpe_event_info, @@ -386,7 +392,7 @@ u32 acpi_ev_gpe_detect(struct acpi_gpe_xrupt_info * gpe_xrupt_list) } ACPI_DEBUG_PRINT((ACPI_DB_INTERRUPTS, - "Read GPE Register at GPE%X: Status=%02X, Enable=%02X\n", + "Read GPE Register at GPE%02X: Status=%02X, Enable=%02X\n", gpe_register_info->base_gpe_number, status_reg, enable_reg)); @@ -660,8 +666,7 @@ acpi_ev_gpe_dispatch(struct acpi_namespace_node *gpe_device, status = acpi_hw_clear_gpe(gpe_event_info); if (ACPI_FAILURE(status)) { ACPI_EXCEPTION((AE_INFO, status, - "Unable to clear GPE[0x%2X]", - gpe_number)); + "Unable to clear GPE%02X", gpe_number)); return_UINT32(ACPI_INTERRUPT_NOT_HANDLED); } } @@ -720,7 +725,7 @@ acpi_ev_gpe_dispatch(struct acpi_namespace_node *gpe_device, gpe_event_info); if (ACPI_FAILURE(status)) { ACPI_EXCEPTION((AE_INFO, status, - "Unable to queue handler for GPE[0x%2X] - event disabled", + "Unable to queue handler for GPE%2X - event disabled", gpe_number)); } break; @@ -733,7 +738,7 @@ acpi_ev_gpe_dispatch(struct acpi_namespace_node *gpe_device, * a GPE to be enabled if it has no handler or method. */ ACPI_ERROR((AE_INFO, - "No handler or method for GPE[0x%2X], disabling event", + "No handler or method for GPE%02X, disabling event", gpe_number)); break; diff --git a/drivers/acpi/acpica/evgpeblk.c b/drivers/acpi/acpica/evgpeblk.c index e2e8164bb811..9acb86958c09 100644 --- a/drivers/acpi/acpica/evgpeblk.c +++ b/drivers/acpi/acpica/evgpeblk.c @@ -361,9 +361,9 @@ acpi_ev_create_gpe_block(struct acpi_namespace_node *gpe_device, gpe_block->node = gpe_device; gpe_block->gpe_count = (u16)(register_count * ACPI_GPE_REGISTER_WIDTH); + gpe_block->initialized = FALSE; gpe_block->register_count = register_count; gpe_block->block_base_number = gpe_block_base_number; - gpe_block->initialized = FALSE; ACPI_MEMCPY(&gpe_block->block_address, gpe_block_address, sizeof(struct acpi_generic_address)); @@ -423,14 +423,12 @@ acpi_ev_create_gpe_block(struct acpi_namespace_node *gpe_device, * * FUNCTION: acpi_ev_initialize_gpe_block * - * PARAMETERS: gpe_device - Handle to the parent GPE block - * gpe_block - Gpe Block info + * PARAMETERS: acpi_gpe_callback * * RETURN: Status * - * DESCRIPTION: Initialize and enable a GPE block. First find and run any - * _PRT methods associated with the block, then enable the - * appropriate GPEs. + * DESCRIPTION: Initialize and enable a GPE block. Enable GPEs that have + * associated methods. * Note: Assumes namespace is locked. * ******************************************************************************/ @@ -450,8 +448,8 @@ acpi_ev_initialize_gpe_block(struct acpi_gpe_xrupt_info *gpe_xrupt_info, ACPI_FUNCTION_TRACE(ev_initialize_gpe_block); /* - * Ignore a null GPE block (e.g., if no GPE block 1 exists) and - * GPE blocks that have been initialized already. + * Ignore a null GPE block (e.g., if no GPE block 1 exists), and + * any GPE blocks that have been initialized already. */ if (!gpe_block || gpe_block->initialized) { return_ACPI_STATUS(AE_OK); @@ -459,8 +457,8 @@ acpi_ev_initialize_gpe_block(struct acpi_gpe_xrupt_info *gpe_xrupt_info, /* * Enable all GPEs that have a corresponding method and have the - * ACPI_GPE_CAN_WAKE flag unset. Any other GPEs within this block must - * be enabled via the acpi_enable_gpe() interface. + * ACPI_GPE_CAN_WAKE flag unset. Any other GPEs within this block + * must be enabled via the acpi_enable_gpe() interface. */ gpe_enabled_count = 0; diff --git a/drivers/acpi/acpica/evgpeinit.c b/drivers/acpi/acpica/evgpeinit.c index 734a494bd705..c59dc2340593 100644 --- a/drivers/acpi/acpica/evgpeinit.c +++ b/drivers/acpi/acpica/evgpeinit.c @@ -45,11 +45,27 @@ #include "accommon.h" #include "acevents.h" #include "acnamesp.h" -#include "acinterp.h" #define _COMPONENT ACPI_EVENTS ACPI_MODULE_NAME("evgpeinit") +/* + * Note: History of _PRW support in ACPICA + * + * Originally (2000 - 2010), the GPE initialization code performed a walk of + * the entire namespace to execute the _PRW methods and detect all GPEs + * capable of waking the system. + * + * As of 10/2010, the _PRW method execution has been removed since it is + * actually unnecessary. The host OS must in fact execute all _PRW methods + * in order to identify the device/power-resource dependencies. We now put + * the onus on the host OS to identify the wake GPEs as part of this process + * and to inform ACPICA of these GPEs via the acpi_setup_gpe_for_wake interface. This + * not only reduces the complexity of the ACPICA initialization code, but in + * some cases (on systems with very large namespaces) it should reduce the + * kernel boot time as well. + */ + /******************************************************************************* * * FUNCTION: acpi_ev_gpe_initialize @@ -222,7 +238,7 @@ void acpi_ev_update_gpes(acpi_owner_id table_owner_id) acpi_status status = AE_OK; /* - * 2) Find any _Lxx/_Exx GPE methods that have just been loaded. + * Find any _Lxx/_Exx GPE methods that have just been loaded. * * Any GPEs that correspond to new _Lxx/_Exx methods are immediately * enabled. @@ -235,9 +251,9 @@ void acpi_ev_update_gpes(acpi_owner_id table_owner_id) return; } + walk_info.count = 0; walk_info.owner_id = table_owner_id; walk_info.execute_by_owner_id = TRUE; - walk_info.count = 0; /* Walk the interrupt level descriptor list */ @@ -298,7 +314,7 @@ void acpi_ev_update_gpes(acpi_owner_id table_owner_id) * xx - is the GPE number [in HEX] * * If walk_info->execute_by_owner_id is TRUE, we only execute examine GPE methods - * with that owner. + * with that owner. * ******************************************************************************/ diff --git a/drivers/acpi/acpica/evxfgpe.c b/drivers/acpi/acpica/evxfgpe.c index fcf4a5cdc9a4..416845bc9c1f 100644 --- a/drivers/acpi/acpica/evxfgpe.c +++ b/drivers/acpi/acpica/evxfgpe.c @@ -55,13 +55,19 @@ ACPI_MODULE_NAME("evxfgpe") * * PARAMETERS: None * - * RETURN: None + * RETURN: Status + * + * DESCRIPTION: Complete GPE initialization and enable all GPEs that have + * associated _Lxx or _Exx methods and are not pointed to by any + * device _PRW methods (this indicates that these GPEs are + * generally intended for system or device wakeup. Such GPEs + * have to be enabled directly when the devices whose _PRW + * methods point to them are set up for wakeup signaling.) * - * DESCRIPTION: Enable all GPEs that have associated _Lxx or _Exx methods and - * are not pointed to by any device _PRW methods indicating that - * these GPEs are generally intended for system or device wakeup - * (such GPEs have to be enabled directly when the devices whose - * _PRW methods point to them are set up for wakeup signaling). + * NOTE: Should be called after any GPEs are added to the system. Primarily, + * after the system _PRW methods have been run, but also after a GPE Block + * Device has been added or if any new GPE methods have been added via a + * dynamic table load. * ******************************************************************************/ @@ -252,7 +258,8 @@ ACPI_EXPORT_SYMBOL(acpi_setup_gpe_for_wake) * * RETURN: Status * - * DESCRIPTION: Set or clear the GPE's wakeup enable mask bit. + * DESCRIPTION: Set or clear the GPE's wakeup enable mask bit. The GPE must + * already be marked as a WAKE GPE. * ******************************************************************************/ @@ -268,8 +275,10 @@ acpi_status acpi_set_gpe_wake_mask(acpi_handle gpe_device, u32 gpe_number, u8 ac flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock); - /* Ensure that we have a valid GPE number */ - + /* + * Ensure that we have a valid GPE number and that this GPE is in + * fact a wake GPE + */ gpe_event_info = acpi_ev_get_gpe_event_info(gpe_device, gpe_number); if (!gpe_event_info) { status = AE_BAD_PARAMETER; @@ -366,7 +375,7 @@ ACPI_EXPORT_SYMBOL(acpi_clear_gpe) * * RETURN: Status * - * DESCRIPTION: Get status of an event (general purpose) + * DESCRIPTION: Get the current status of a GPE (signalled/not_signalled) * ******************************************************************************/ acpi_status @@ -476,7 +485,8 @@ ACPI_EXPORT_SYMBOL(acpi_enable_all_runtime_gpes) * * RETURN: Status * - * DESCRIPTION: Create and Install a block of GPE registers + * DESCRIPTION: Create and Install a block of GPE registers. The GPEs are not + * enabled here. * ******************************************************************************/ acpi_status diff --git a/drivers/acpi/acpica/hwgpe.c b/drivers/acpi/acpica/hwgpe.c index 7c6d4858bb49..85c3cbd4304d 100644 --- a/drivers/acpi/acpica/hwgpe.c +++ b/drivers/acpi/acpica/hwgpe.c @@ -62,10 +62,10 @@ acpi_hw_enable_wakeup_gpe_block(struct acpi_gpe_xrupt_info *gpe_xrupt_info, * PARAMETERS: gpe_event_info - Info block for the GPE * gpe_register_info - Info block for the GPE register * - * RETURN: Status + * RETURN: Register mask with a one in the GPE bit position * - * DESCRIPTION: Compute GPE enable mask with one bit corresponding to the given - * GPE set. + * DESCRIPTION: Compute the register mask for this GPE. One bit is set in the + * correct position for the input GPE. * ******************************************************************************/ @@ -85,12 +85,12 @@ u32 acpi_hw_get_gpe_register_bit(struct acpi_gpe_event_info *gpe_event_info, * * RETURN: Status * - * DESCRIPTION: Enable or disable a single GPE in its enable register. + * DESCRIPTION: Enable or disable a single GPE in the parent enable register. * ******************************************************************************/ acpi_status -acpi_hw_low_set_gpe(struct acpi_gpe_event_info *gpe_event_info, u8 action) +acpi_hw_low_set_gpe(struct acpi_gpe_event_info *gpe_event_info, u32 action) { struct acpi_gpe_register_info *gpe_register_info; acpi_status status; @@ -113,14 +113,20 @@ acpi_hw_low_set_gpe(struct acpi_gpe_event_info *gpe_event_info, u8 action) return (status); } - /* Set ot clear just the bit that corresponds to this GPE */ + /* Set or clear just the bit that corresponds to this GPE */ register_bit = acpi_hw_get_gpe_register_bit(gpe_event_info, gpe_register_info); switch (action) { case ACPI_GPE_CONDITIONAL_ENABLE: - if (!(register_bit & gpe_register_info->enable_for_run)) + + /* Only enable if the enable_for_run bit is set */ + + if (!(register_bit & gpe_register_info->enable_for_run)) { return (AE_BAD_PARAMETER); + } + + /*lint -fallthrough */ case ACPI_GPE_ENABLE: ACPI_SET_BIT(enable_mask, register_bit); @@ -131,7 +137,7 @@ acpi_hw_low_set_gpe(struct acpi_gpe_event_info *gpe_event_info, u8 action) break; default: - ACPI_ERROR((AE_INFO, "Invalid action\n")); + ACPI_ERROR((AE_INFO, "Invalid GPE Action, %u\n", action)); return (AE_BAD_PARAMETER); } @@ -168,13 +174,13 @@ acpi_status acpi_hw_clear_gpe(struct acpi_gpe_event_info * gpe_event_info) return (AE_NOT_EXIST); } - register_bit = acpi_hw_get_gpe_register_bit(gpe_event_info, - gpe_register_info); - /* * Write a one to the appropriate bit in the status register to * clear this GPE. */ + register_bit = + acpi_hw_get_gpe_register_bit(gpe_event_info, gpe_register_info); + status = acpi_hw_write(register_bit, &gpe_register_info->status_address); @@ -201,8 +207,8 @@ acpi_hw_get_gpe_status(struct acpi_gpe_event_info * gpe_event_info, u32 in_byte; u32 register_bit; struct acpi_gpe_register_info *gpe_register_info; - acpi_status status; acpi_event_status local_event_status = 0; + acpi_status status; ACPI_FUNCTION_ENTRY(); -- cgit v1.2.3 From 32a00d274e877eab3ea7ab196b75c9be5170d25e Mon Sep 17 00:00:00 2001 From: Rafael J. Wysocki Date: Thu, 25 Nov 2010 00:05:17 +0100 Subject: ACPI / PM: Prevent acpi_power_get_inferred_state() from making changes acpi_power_get_inferred_state() should not update device->power.state behind the back of its caller, so make it return the state via a pointer instead. Signed-off-by: Rafael J. Wysocki Signed-off-by: Len Brown --- drivers/acpi/bus.c | 3 ++- drivers/acpi/internal.h | 2 +- drivers/acpi/power.c | 12 ++++-------- 3 files changed, 7 insertions(+), 10 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c index d68bd61072bb..a9fe8e6bc40e 100644 --- a/drivers/acpi/bus.c +++ b/drivers/acpi/bus.c @@ -222,7 +222,8 @@ int acpi_bus_get_power(acpi_handle handle, int *state) * indirectly (via power resources). */ if (device->power.flags.power_resources) { - result = acpi_power_get_inferred_state(device); + result = acpi_power_get_inferred_state(device, + &device->power.state); if (result) return result; } else if (device->power.flags.explicit_get) { diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h index a212bfeddf8c..2cc01489dccf 100644 --- a/drivers/acpi/internal.h +++ b/drivers/acpi/internal.h @@ -41,7 +41,7 @@ static inline int acpi_debugfs_init(void) { return 0; } int acpi_power_init(void); int acpi_device_sleep_wake(struct acpi_device *dev, int enable, int sleep_state, int dev_state); -int acpi_power_get_inferred_state(struct acpi_device *device); +int acpi_power_get_inferred_state(struct acpi_device *device, int *state); int acpi_power_transition(struct acpi_device *device, int state); extern int acpi_power_nocheck; diff --git a/drivers/acpi/power.c b/drivers/acpi/power.c index 4c9c2fb5d98f..9bd1b6044b55 100644 --- a/drivers/acpi/power.c +++ b/drivers/acpi/power.c @@ -423,19 +423,16 @@ int acpi_disable_wakeup_device_power(struct acpi_device *dev) Device Power Management -------------------------------------------------------------------------- */ -int acpi_power_get_inferred_state(struct acpi_device *device) +int acpi_power_get_inferred_state(struct acpi_device *device, int *state) { int result = 0; struct acpi_handle_list *list = NULL; int list_state = 0; int i = 0; - - if (!device) + if (!device || !state) return -EINVAL; - device->power.state = ACPI_STATE_UNKNOWN; - /* * We know a device's inferred power state when all the resources * required for a given D-state are 'on'. @@ -450,13 +447,12 @@ int acpi_power_get_inferred_state(struct acpi_device *device) return result; if (list_state == ACPI_POWER_RESOURCE_STATE_ON) { - device->power.state = i; + *state = i; return 0; } } - device->power.state = ACPI_STATE_D3; - + *state = ACPI_STATE_D3; return 0; } -- cgit v1.2.3 From d2ef555b57292cd818934636ac8e3414cc2a6762 Mon Sep 17 00:00:00 2001 From: Rafael J. Wysocki Date: Thu, 25 Nov 2010 00:06:09 +0100 Subject: ACPI / PM: Add functions for manipulating lists of power resources ACPI device power resources should be reference counted during device initialization, so that their reference counters are always up to date. It is convenient to do that with the help of a function that will reference count and possibly turn on power resources in a given list, so introduce that function, acpi_power_on_list(). For symmetry, introduce acpi_power_off_list() for performing the reverse operation and use the both of them to simplify acpi_power_transition(). Signed-off-by: Rafael J. Wysocki Signed-off-by: Len Brown --- drivers/acpi/power.c | 67 +++++++++++++++++++++++++++++----------------------- 1 file changed, 38 insertions(+), 29 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/power.c b/drivers/acpi/power.c index 9bd1b6044b55..95fedbdf4fb8 100644 --- a/drivers/acpi/power.c +++ b/drivers/acpi/power.c @@ -266,6 +266,35 @@ static int acpi_power_off_device(acpi_handle handle) return result; } +static void __acpi_power_off_list(struct acpi_handle_list *list, int num_res) +{ + int i; + + for (i = num_res - 1; i >= 0 ; i--) + acpi_power_off_device(list->handles[i]); +} + +static void acpi_power_off_list(struct acpi_handle_list *list) +{ + __acpi_power_off_list(list, list->count); +} + +static int acpi_power_on_list(struct acpi_handle_list *list) +{ + int result = 0; + int i; + + for (i = 0; i < list->count; i++) { + result = acpi_power_on(list->handles[i]); + if (result) { + __acpi_power_off_list(list, i); + break; + } + } + + return result; +} + /** * acpi_device_sleep_wake - execute _DSW (Device Sleep Wake) or (deprecated in * ACPI 3.0) _PSW (Power State Wake) @@ -458,10 +487,7 @@ int acpi_power_get_inferred_state(struct acpi_device *device, int *state) int acpi_power_transition(struct acpi_device *device, int state) { - int result = 0; - struct acpi_handle_list *cl = NULL; /* Current Resources */ - struct acpi_handle_list *tl = NULL; /* Target Resources */ - int i = 0; + int result; if (!device || (state < ACPI_STATE_D0) || (state > ACPI_STATE_D3)) return -EINVAL; @@ -473,37 +499,20 @@ int acpi_power_transition(struct acpi_device *device, int state) || (device->power.state > ACPI_STATE_D3)) return -ENODEV; - cl = &device->power.states[device->power.state].resources; - tl = &device->power.states[state].resources; - /* TBD: Resources must be ordered. */ /* * First we reference all power resources required in the target list - * (e.g. so the device doesn't lose power while transitioning). + * (e.g. so the device doesn't lose power while transitioning). Then, + * we dereference all power resources used in the current list. */ - for (i = 0; i < tl->count; i++) { - result = acpi_power_on(tl->handles[i]); - if (result) - goto end; - } + result = acpi_power_on_list(&device->power.states[state].resources); + if (!result) + acpi_power_off_list( + &device->power.states[device->power.state].resources); - /* - * Then we dereference all power resources used in the current list. - */ - for (i = 0; i < cl->count; i++) { - result = acpi_power_off_device(cl->handles[i]); - if (result) - goto end; - } - - end: - if (result) - device->power.state = ACPI_STATE_UNKNOWN; - else { - /* We shouldn't change the state till all above operations succeed */ - device->power.state = state; - } + /* We shouldn't change the state unless the above operations succeed. */ + device->power.state = result ? ACPI_STATE_UNKNOWN : state; return result; } -- cgit v1.2.3 From 30d3df41b32b1ea63d3ebc52ef5644cbe41520f4 Mon Sep 17 00:00:00 2001 From: Rafael J. Wysocki Date: Thu, 25 Nov 2010 00:06:55 +0100 Subject: ACPI / PM: Introduce function for refcounting device power resources Introduce function acpi_power_on_resources() that reference counts and possibly turns on ACPI power resources for a given device and a given power state of it. This function will be used for reference counting device power resources during initialization. Signed-off-by: Rafael J. Wysocki Signed-off-by: Len Brown --- drivers/acpi/internal.h | 1 + drivers/acpi/power.c | 8 ++++++++ 2 files changed, 9 insertions(+) (limited to 'drivers/acpi') diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h index 2cc01489dccf..433a8ee8ac2c 100644 --- a/drivers/acpi/internal.h +++ b/drivers/acpi/internal.h @@ -42,6 +42,7 @@ int acpi_power_init(void); int acpi_device_sleep_wake(struct acpi_device *dev, int enable, int sleep_state, int dev_state); int acpi_power_get_inferred_state(struct acpi_device *device, int *state); +int acpi_power_on_resources(struct acpi_device *device, int state); int acpi_power_transition(struct acpi_device *device, int state); extern int acpi_power_nocheck; diff --git a/drivers/acpi/power.c b/drivers/acpi/power.c index 95fedbdf4fb8..0cb4eab50d73 100644 --- a/drivers/acpi/power.c +++ b/drivers/acpi/power.c @@ -485,6 +485,14 @@ int acpi_power_get_inferred_state(struct acpi_device *device, int *state) return 0; } +int acpi_power_on_resources(struct acpi_device *device, int state) +{ + if (!device || state < ACPI_STATE_D0 || state > ACPI_STATE_D3) + return -EINVAL; + + return acpi_power_on_list(&device->power.states[state].resources); +} + int acpi_power_transition(struct acpi_device *device, int state) { int result; -- cgit v1.2.3 From 5e6d4fe4296782f1f095575b8213a97c3e925a16 Mon Sep 17 00:00:00 2001 From: Rafael J. Wysocki Date: Thu, 25 Nov 2010 00:07:56 +0100 Subject: ACPI / PM: Introduce __acpi_bus_get_power() It sometimes is necessary to get the power state of an ACPI device without updating its device->power.state field, for example to avoid inconsistencies between device->power.state and the reference counters of the device's power resources. For this purpose introduce __acpi_bus_get_power() that will return the given device's power state via a pointer (instead of modifying device->power.state) and make acpi_bus_get_power() use it. Signed-off-by: Rafael J. Wysocki Signed-off-by: Len Brown --- drivers/acpi/bus.c | 48 +++++++++++++++++++++++++++++------------------- 1 file changed, 29 insertions(+), 19 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c index a9fe8e6bc40e..9657abc4a7fb 100644 --- a/drivers/acpi/bus.c +++ b/drivers/acpi/bus.c @@ -196,34 +196,24 @@ EXPORT_SYMBOL(acpi_bus_get_private_data); Power Management -------------------------------------------------------------------------- */ -int acpi_bus_get_power(acpi_handle handle, int *state) +static int __acpi_bus_get_power(struct acpi_device *device, int *state) { int result = 0; acpi_status status = 0; - struct acpi_device *device = NULL; unsigned long long psc = 0; - - result = acpi_bus_get_device(handle, &device); - if (result) - return result; + if (!device || !state) + return -EINVAL; *state = ACPI_STATE_UNKNOWN; - if (!device->flags.power_manageable) { - /* TBD: Non-recursive algorithm for walking up hierarchy */ - if (device->parent) - *state = device->parent->power.state; - else - *state = ACPI_STATE_D0; - } else { + if (device->flags.power_manageable) { /* * Get the device's power state either directly (via _PSC) or * indirectly (via power resources). */ if (device->power.flags.power_resources) { - result = acpi_power_get_inferred_state(device, - &device->power.state); + result = acpi_power_get_inferred_state(device, state); if (result) return result; } else if (device->power.flags.explicit_get) { @@ -231,20 +221,40 @@ int acpi_bus_get_power(acpi_handle handle, int *state) NULL, &psc); if (ACPI_FAILURE(status)) return -ENODEV; - device->power.state = (int)psc; + *state = (int)psc; } - - *state = device->power.state; + } else { + /* TBD: Non-recursive algorithm for walking up hierarchy. */ + *state = device->parent ? + device->parent->power.state : ACPI_STATE_D0; } ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device [%s] power state is D%d\n", - device->pnp.bus_id, device->power.state)); + device->pnp.bus_id, *state)); return 0; } + +int acpi_bus_get_power(acpi_handle handle, int *state) +{ + struct acpi_device *device; + int result; + + result = acpi_bus_get_device(handle, &device); + if (result) + return result; + + result = __acpi_bus_get_power(device, state); + if (result) + return result; + + device->power.state = *state; + return 0; +} EXPORT_SYMBOL(acpi_bus_get_power); + int acpi_bus_set_power(acpi_handle handle, int state) { int result = 0; -- cgit v1.2.3 From ade3e7fef794781c0798d0cf0f046123842ba550 Mon Sep 17 00:00:00 2001 From: Rafael J. Wysocki Date: Thu, 25 Nov 2010 00:08:36 +0100 Subject: ACPI / PM: Add function for device power state initialization Add function acpi_bus_init_power() for getting the initial power state of an ACPI device and reference counting its power resources as appropriate. Make acpi_bus_get_power_flags() use the new function instead of acpi_bus_get_power() that updates device->power.state without reference counting the device's power resources. Signed-off-by: Rafael J. Wysocki Signed-off-by: Len Brown --- drivers/acpi/bus.c | 25 +++++++++++++++++++++++++ drivers/acpi/internal.h | 1 + drivers/acpi/scan.c | 5 +---- 3 files changed, 27 insertions(+), 4 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c index 9657abc4a7fb..453451090502 100644 --- a/drivers/acpi/bus.c +++ b/drivers/acpi/bus.c @@ -364,6 +364,31 @@ int acpi_bus_set_power(acpi_handle handle, int state) EXPORT_SYMBOL(acpi_bus_set_power); + +int acpi_bus_init_power(struct acpi_device *device) +{ + int state; + int result; + + if (!device) + return -EINVAL; + + device->power.state = ACPI_STATE_UNKNOWN; + + result = __acpi_bus_get_power(device, &state); + if (result) + return result; + + if (device->power.flags.power_resources) + result = acpi_power_on_resources(device, state); + + if (!result) + device->power.state = state; + + return result; +} + + bool acpi_bus_power_manageable(acpi_handle handle) { struct acpi_device *device; diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h index 433a8ee8ac2c..7493e6c7a003 100644 --- a/drivers/acpi/internal.h +++ b/drivers/acpi/internal.h @@ -44,6 +44,7 @@ int acpi_device_sleep_wake(struct acpi_device *dev, int acpi_power_get_inferred_state(struct acpi_device *device, int *state); int acpi_power_on_resources(struct acpi_device *device, int state); int acpi_power_transition(struct acpi_device *device, int state); +int acpi_bus_init_power(struct acpi_device *device); extern int acpi_power_nocheck; int acpi_wakeup_device_init(void); diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 29ef505c487b..ef8e659771e1 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -901,10 +901,7 @@ static int acpi_bus_get_power_flags(struct acpi_device *device) device->power.states[ACPI_STATE_D3].flags.valid = 1; device->power.states[ACPI_STATE_D3].power = 0; - /* TBD: System wake support and resource requirements. */ - - device->power.state = ACPI_STATE_UNKNOWN; - acpi_bus_get_power(device->handle, &(device->power.state)); + acpi_bus_init_power(device); return 0; } -- cgit v1.2.3 From 25eed40720fc9005c63a1f436e5f8a78836c26ff Mon Sep 17 00:00:00 2001 From: Rafael J. Wysocki Date: Thu, 25 Nov 2010 00:09:15 +0100 Subject: ACPI / PM: Add function for updating device power state consistently Add function acpi_bus_update_power() for reading the actual power state of an ACPI device and updating its device->power.state field in such a way that its power resources' reference counters will remain consistent with that field. For this purpose introduce __acpi_bus_set_power() setting the power state of an ACPI device without updating its device->power.state field and make acpi_bus_set_power() and acpi_bus_update_power() use it (acpi_bus_set_power() retains the current behavior for now). Signed-off-by: Rafael J. Wysocki Signed-off-by: Len Brown --- drivers/acpi/bus.c | 90 +++++++++++++++++++++++++++++++++---------------- include/acpi/acpi_bus.h | 1 + 2 files changed, 62 insertions(+), 29 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c index 453451090502..19decee72e92 100644 --- a/drivers/acpi/bus.c +++ b/drivers/acpi/bus.c @@ -255,44 +255,17 @@ int acpi_bus_get_power(acpi_handle handle, int *state) EXPORT_SYMBOL(acpi_bus_get_power); -int acpi_bus_set_power(acpi_handle handle, int state) +static int __acpi_bus_set_power(struct acpi_device *device, int state) { int result = 0; acpi_status status = AE_OK; - struct acpi_device *device = NULL; char object_name[5] = { '_', 'P', 'S', '0' + state, '\0' }; - - result = acpi_bus_get_device(handle, &device); - if (result) - return result; - - if ((state < ACPI_STATE_D0) || (state > ACPI_STATE_D3)) + if (!device || (state < ACPI_STATE_D0) || (state > ACPI_STATE_D3)) return -EINVAL; /* Make sure this is a valid target state */ - if (!device->flags.power_manageable) { - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device `[%s]' is not power manageable\n", - kobject_name(&device->dev.kobj))); - return -ENODEV; - } - /* - * Get device's current power state - */ - if (!acpi_power_nocheck) { - /* - * Maybe the incorrect power state is returned on the bogus - * bios, which is different with the real power state. - * For example: the bios returns D0 state and the real power - * state is D3. OS expects to set the device to D0 state. In - * such case if OS uses the power state returned by the BIOS, - * the device can't be transisted to the correct power state. - * So if the acpi_power_nocheck is set, it is unnecessary to - * get the power state by calling acpi_bus_get_power. - */ - acpi_bus_get_power(device->handle, &device->power.state); - } if ((state == device->power.state) && !device->flags.force_power_state) { ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device is already at D%d\n", state)); @@ -362,6 +335,42 @@ int acpi_bus_set_power(acpi_handle handle, int state) return result; } + +int acpi_bus_set_power(acpi_handle handle, int state) +{ + struct acpi_device *device; + int result; + + result = acpi_bus_get_device(handle, &device); + if (result) + return result; + + if (!device->flags.power_manageable) { + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Device [%s] is not power manageable\n", + dev_name(&device->dev))); + return -ENODEV; + } + + /* + * Get device's current power state + */ + if (!acpi_power_nocheck) { + /* + * Maybe the incorrect power state is returned on the bogus + * bios, which is different with the real power state. + * For example: the bios returns D0 state and the real power + * state is D3. OS expects to set the device to D0 state. In + * such case if OS uses the power state returned by the BIOS, + * the device can't be transisted to the correct power state. + * So if the acpi_power_nocheck is set, it is unnecessary to + * get the power state by calling acpi_bus_get_power. + */ + __acpi_bus_get_power(device, &device->power.state); + } + + return __acpi_bus_set_power(device, state); +} EXPORT_SYMBOL(acpi_bus_set_power); @@ -389,6 +398,29 @@ int acpi_bus_init_power(struct acpi_device *device) } +int acpi_bus_update_power(acpi_handle handle, int *state_p) +{ + struct acpi_device *device; + int state; + int result; + + result = acpi_bus_get_device(handle, &device); + if (result) + return result; + + result = __acpi_bus_get_power(device, &state); + if (result) + return result; + + result = __acpi_bus_set_power(device, state); + if (!result && state_p) + *state_p = state; + + return result; +} +EXPORT_SYMBOL_GPL(acpi_bus_update_power); + + bool acpi_bus_power_manageable(acpi_handle handle) { struct acpi_device *device; diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index 359ef11725a6..5d2c4c540063 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -330,6 +330,7 @@ acpi_status acpi_bus_get_status_handle(acpi_handle handle, int acpi_bus_get_status(struct acpi_device *device); int acpi_bus_get_power(acpi_handle handle, int *state); int acpi_bus_set_power(acpi_handle handle, int state); +int acpi_bus_update_power(acpi_handle handle, int *state_p); bool acpi_bus_power_manageable(acpi_handle handle); bool acpi_bus_can_wakeup(acpi_handle handle); #ifdef CONFIG_ACPI_PROC_EVENT -- cgit v1.2.3 From 97d9a9e9f5ee68f20005ca5aa77c6b684e7cace8 Mon Sep 17 00:00:00 2001 From: Rafael J. Wysocki Date: Thu, 25 Nov 2010 00:10:02 +0100 Subject: ACPI / PM: Register acpi_power_driver early The ACPI device driver used for handling power resources, acpi_power_driver, creates a struct acpi_power_resource object for each ACPI device representing a power resource. These objects are then used when setting and reading the power states of devices using the corresponding power resources. Unfortunately, acpi_power_driver is registered after acpi_scan_init() that may add devices using the power resources before acpi_power_driver has a chance to create struct acpi_power_resource objects for them (specifically, the power resources may be referred to during the scanning process through acpi_bus_get_power() before they have been initialized). As the first step towards fixing this issue, move the registration of acpi_power_driver into acpi_scan_init() so that power resource devices can be initialized by it as soon as they have been found in the namespace. Signed-off-by: Rafael J. Wysocki Signed-off-by: Len Brown --- drivers/acpi/bus.c | 1 - drivers/acpi/scan.c | 2 ++ 2 files changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c index 19decee72e92..47864013c0df 100644 --- a/drivers/acpi/bus.c +++ b/drivers/acpi/bus.c @@ -1099,7 +1099,6 @@ static int __init acpi_init(void) acpi_scan_init(); acpi_ec_init(); - acpi_power_init(); acpi_debugfs_init(); acpi_sleep_proc_init(); acpi_wakeup_device_init(); diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index ef8e659771e1..2951a27303cb 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -1570,6 +1570,8 @@ int __init acpi_scan_init(void) printk(KERN_ERR PREFIX "Could not register bus type\n"); } + acpi_power_init(); + /* * Enumerate devices in the ACPI namespace. */ -- cgit v1.2.3 From bf325f9538d8c89312be305b9779edbcb436af00 Mon Sep 17 00:00:00 2001 From: Rafael J. Wysocki Date: Thu, 25 Nov 2010 00:10:44 +0100 Subject: ACPI / PM: Register power resource devices as soon as they are needed Depending on the organization of the ACPI namespace, power resource device objects may generally be scanned after the "regular" device objects that they are referred from through _PRn. This, in turn, may cause acpi_bus_get_power_flags() to attempt to access them through acpi_bus_init_power() before they are registered (and initialized by acpi_power_driver). [This is not a theoretical issue, it actually happens for one PnP device on my testbed HP nx6325.] To fix this problem, make acpi_bus_get_power_flags() attempt to register power resource devices as soon as they have been found in the _PRn output for any other devices. Signed-off-by: Rafael J. Wysocki Signed-off-by: Len Brown --- drivers/acpi/scan.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) (limited to 'drivers/acpi') diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 2951a27303cb..cb7956c23ca4 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -847,6 +847,8 @@ end: return 0; } +static void acpi_bus_add_power_resource(acpi_handle handle); + static int acpi_bus_get_power_flags(struct acpi_device *device) { acpi_status status = 0; @@ -875,8 +877,12 @@ static int acpi_bus_get_power_flags(struct acpi_device *device) acpi_evaluate_reference(device->handle, object_name, NULL, &ps->resources); if (ps->resources.count) { + int j; + device->power.flags.power_resources = 1; ps->flags.valid = 1; + for (j = 0; j < ps->resources.count; j++) + acpi_bus_add_power_resource(ps->resources.handles[j]); } /* Evaluate "_PSx" to see if we can do explicit sets */ @@ -1323,6 +1329,20 @@ end: #define ACPI_STA_DEFAULT (ACPI_STA_DEVICE_PRESENT | ACPI_STA_DEVICE_ENABLED | \ ACPI_STA_DEVICE_UI | ACPI_STA_DEVICE_FUNCTIONING) +static void acpi_bus_add_power_resource(acpi_handle handle) +{ + struct acpi_bus_ops ops = { + .acpi_op_add = 1, + .acpi_op_start = 1, + }; + struct acpi_device *device = NULL; + + acpi_bus_get_device(handle, &device); + if (!device) + acpi_add_single_object(&device, handle, ACPI_BUS_TYPE_POWER, + ACPI_STA_DEFAULT, &ops); +} + static int acpi_bus_type_and_status(acpi_handle handle, int *type, unsigned long long *sta) { -- cgit v1.2.3 From 488a76c52606199100adf09c8eb7cbedbd94e9d9 Mon Sep 17 00:00:00 2001 From: Rafael J. Wysocki Date: Thu, 25 Nov 2010 00:11:24 +0100 Subject: ACPI / Fan: Rework the handling of power resources Use the new function acpi_bus_update_power() for manipulating power resources used by ACPI fan devices, which allows them to be put into the right state during initialization and resume. Consequently, remove the flags.force_power_state field from struct acpi_device, which is not necessary any more. Signed-off-by: Rafael J. Wysocki Signed-off-by: Len Brown --- drivers/acpi/bus.c | 2 +- drivers/acpi/fan.c | 27 +++++++-------------------- drivers/acpi/thermal.c | 5 +++-- include/acpi/acpi_bus.h | 3 +-- 4 files changed, 12 insertions(+), 25 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c index 47864013c0df..2ee83b5fff97 100644 --- a/drivers/acpi/bus.c +++ b/drivers/acpi/bus.c @@ -266,7 +266,7 @@ static int __acpi_bus_set_power(struct acpi_device *device, int state) /* Make sure this is a valid target state */ - if ((state == device->power.state) && !device->flags.force_power_state) { + if (state == device->power.state) { ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device is already at D%d\n", state)); return 0; diff --git a/drivers/acpi/fan.c b/drivers/acpi/fan.c index 60049080c869..467479f07c1f 100644 --- a/drivers/acpi/fan.c +++ b/drivers/acpi/fan.c @@ -86,7 +86,7 @@ static int fan_get_cur_state(struct thermal_cooling_device *cdev, unsigned long if (!device) return -EINVAL; - result = acpi_bus_get_power(device->handle, &acpi_state); + result = acpi_bus_update_power(device->handle, &acpi_state); if (result) return result; @@ -123,7 +123,6 @@ static struct thermal_cooling_device_ops fan_cooling_ops = { static int acpi_fan_add(struct acpi_device *device) { int result = 0; - int state = 0; struct thermal_cooling_device *cdev; if (!device) @@ -132,16 +131,12 @@ static int acpi_fan_add(struct acpi_device *device) strcpy(acpi_device_name(device), "Fan"); strcpy(acpi_device_class(device), ACPI_FAN_CLASS); - result = acpi_bus_get_power(device->handle, &state); + result = acpi_bus_update_power(device->handle, NULL); if (result) { - printk(KERN_ERR PREFIX "Reading power state\n"); + printk(KERN_ERR PREFIX "Setting initial power state\n"); goto end; } - device->flags.force_power_state = 1; - acpi_bus_set_power(device->handle, state); - device->flags.force_power_state = 0; - cdev = thermal_cooling_device_register("Fan", device, &fan_cooling_ops); if (IS_ERR(cdev)) { @@ -200,22 +195,14 @@ static int acpi_fan_suspend(struct acpi_device *device, pm_message_t state) static int acpi_fan_resume(struct acpi_device *device) { - int result = 0; - int power_state = 0; + int result; if (!device) return -EINVAL; - result = acpi_bus_get_power(device->handle, &power_state); - if (result) { - printk(KERN_ERR PREFIX - "Error reading fan power state\n"); - return result; - } - - device->flags.force_power_state = 1; - acpi_bus_set_power(device->handle, power_state); - device->flags.force_power_state = 0; + result = acpi_bus_update_power(device->handle, NULL); + if (result) + printk(KERN_ERR PREFIX "Error updating fan power state\n"); return result; } diff --git a/drivers/acpi/thermal.c b/drivers/acpi/thermal.c index 5a27b0a31315..2607e17b520f 100644 --- a/drivers/acpi/thermal.c +++ b/drivers/acpi/thermal.c @@ -1059,8 +1059,9 @@ static int acpi_thermal_resume(struct acpi_device *device) break; tz->trips.active[i].flags.enabled = 1; for (j = 0; j < tz->trips.active[i].devices.count; j++) { - result = acpi_bus_get_power(tz->trips.active[i].devices. - handles[j], &power_state); + result = acpi_bus_update_power( + tz->trips.active[i].devices.handles[j], + &power_state); if (result || (power_state != ACPI_STATE_D0)) { tz->trips.active[i].flags.enabled = 0; break; diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index 5d2c4c540063..89125808fbd5 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -149,8 +149,7 @@ struct acpi_device_flags { u32 power_manageable:1; u32 performance_manageable:1; u32 wake_capable:1; /* Wakeup(_PRW) supported? */ - u32 force_power_state:1; - u32 reserved:22; + u32 reserved:23; }; /* File System */ -- cgit v1.2.3 From f6767dcf2a4f6e62960912d0affec1e15a246191 Mon Sep 17 00:00:00 2001 From: Rafael J. Wysocki Date: Sat, 11 Dec 2010 23:44:39 +0100 Subject: ACPI / PM: Drop acpi_bus_get_power() There are no more users of acpi_bus_get_power(), so it can be dropped. Moreover, it should be dropped, because it modifies the device->power.state field of an ACPI device without updating the reference counters of the device's power resources, which is wrong. Signed-off-by: Rafael J. Wysocki Signed-off-by: Len Brown --- drivers/acpi/bus.c | 19 ------------------- include/acpi/acpi_bus.h | 1 - 2 files changed, 20 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c index 2ee83b5fff97..0baa5f97734a 100644 --- a/drivers/acpi/bus.c +++ b/drivers/acpi/bus.c @@ -236,25 +236,6 @@ static int __acpi_bus_get_power(struct acpi_device *device, int *state) } -int acpi_bus_get_power(acpi_handle handle, int *state) -{ - struct acpi_device *device; - int result; - - result = acpi_bus_get_device(handle, &device); - if (result) - return result; - - result = __acpi_bus_get_power(device, state); - if (result) - return result; - - device->power.state = *state; - return 0; -} -EXPORT_SYMBOL(acpi_bus_get_power); - - static int __acpi_bus_set_power(struct acpi_device *device, int state) { int result = 0; diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index 89125808fbd5..673a3f4d1f07 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -327,7 +327,6 @@ void acpi_bus_data_handler(acpi_handle handle, void *context); acpi_status acpi_bus_get_status_handle(acpi_handle handle, unsigned long long *sta); int acpi_bus_get_status(struct acpi_device *device); -int acpi_bus_get_power(acpi_handle handle, int *state); int acpi_bus_set_power(acpi_handle handle, int state); int acpi_bus_update_power(acpi_handle handle, int *state_p); bool acpi_bus_power_manageable(acpi_handle handle); -- cgit v1.2.3 From 53eac700b0df1fef8c957b9eedfd7f48120425e3 Mon Sep 17 00:00:00 2001 From: Rafael J. Wysocki Date: Sat, 11 Dec 2010 23:45:30 +0100 Subject: ACPI / PM: Drop acpi_power_nocheck Since acpi_bus_set_power() should not use __acpi_bus_get_power() to update the device's device->power.state field before changing its power state (this may cause device->power.state to be inconsistent with the device power resources' reference counters), remove this call from it. In consequence, the acpi_power_nocheck variable is not necessary any more, so it can be dropped along with the DMI table used for setting that variable for HP Pavilion 05. Signed-off-by: Rafael J. Wysocki Signed-off-by: Len Brown --- drivers/acpi/bus.c | 39 --------------------------------------- drivers/acpi/internal.h | 1 - drivers/acpi/power.c | 3 --- 3 files changed, 43 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c index 0baa5f97734a..7ced61f39492 100644 --- a/drivers/acpi/bus.c +++ b/drivers/acpi/bus.c @@ -52,22 +52,6 @@ EXPORT_SYMBOL(acpi_root_dir); #define STRUCT_TO_INT(s) (*((int*)&s)) -static int set_power_nocheck(const struct dmi_system_id *id) -{ - printk(KERN_NOTICE PREFIX "%s detected - " - "disable power check in power transition\n", id->ident); - acpi_power_nocheck = 1; - return 0; -} -static struct dmi_system_id __cpuinitdata power_nocheck_dmi_table[] = { - { - set_power_nocheck, "HP Pavilion 05", { - DMI_MATCH(DMI_BIOS_VENDOR, "Phoenix Technologies LTD"), - DMI_MATCH(DMI_SYS_VENDOR, "HP Pavilion 05"), - DMI_MATCH(DMI_PRODUCT_VERSION, "2001211RE101GLEND") }, NULL}, - {}, -}; - #ifdef CONFIG_X86 static int set_copy_dsdt(const struct dmi_system_id *id) @@ -333,23 +317,6 @@ int acpi_bus_set_power(acpi_handle handle, int state) return -ENODEV; } - /* - * Get device's current power state - */ - if (!acpi_power_nocheck) { - /* - * Maybe the incorrect power state is returned on the bogus - * bios, which is different with the real power state. - * For example: the bios returns D0 state and the real power - * state is D3. OS expects to set the device to D0 state. In - * such case if OS uses the power state returned by the BIOS, - * the device can't be transisted to the correct power state. - * So if the acpi_power_nocheck is set, it is unnecessary to - * get the power state by calling acpi_bus_get_power. - */ - __acpi_bus_get_power(device, &device->power.state); - } - return __acpi_bus_set_power(device, state); } EXPORT_SYMBOL(acpi_bus_set_power); @@ -1072,12 +1039,6 @@ static int __init acpi_init(void) if (acpi_disabled) return result; - /* - * If the laptop falls into the DMI check table, the power state check - * will be disabled in the course of device power transition. - */ - dmi_check_system(power_nocheck_dmi_table); - acpi_scan_init(); acpi_ec_init(); acpi_debugfs_init(); diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h index 7493e6c7a003..8df5d7061a45 100644 --- a/drivers/acpi/internal.h +++ b/drivers/acpi/internal.h @@ -45,7 +45,6 @@ int acpi_power_get_inferred_state(struct acpi_device *device, int *state); int acpi_power_on_resources(struct acpi_device *device, int state); int acpi_power_transition(struct acpi_device *device, int state); int acpi_bus_init_power(struct acpi_device *device); -extern int acpi_power_nocheck; int acpi_wakeup_device_init(void); void acpi_early_processor_set_pdc(void); diff --git a/drivers/acpi/power.c b/drivers/acpi/power.c index 0cb4eab50d73..0003f1009885 100644 --- a/drivers/acpi/power.c +++ b/drivers/acpi/power.c @@ -56,9 +56,6 @@ ACPI_MODULE_NAME("power"); #define ACPI_POWER_RESOURCE_STATE_ON 0x01 #define ACPI_POWER_RESOURCE_STATE_UNKNOWN 0xFF -int acpi_power_nocheck; -module_param_named(power_nocheck, acpi_power_nocheck, bool, 000); - static int acpi_power_add(struct acpi_device *device); static int acpi_power_remove(struct acpi_device *device, int type); static int acpi_power_resume(struct acpi_device *device); -- cgit v1.2.3 From 36237fa0a711c309a38d7a7a9aed727e0eb76449 Mon Sep 17 00:00:00 2001 From: Rafael J. Wysocki Date: Thu, 6 Jan 2011 23:38:04 +0100 Subject: ACPI / PM: Rename acpi_power_off_device() Rename acpi_power_off_device() to acpi_power_off() in analogy with acpi_power_on(). Signed-off-by: Rafael J. Wysocki Signed-off-by: Len Brown --- drivers/acpi/power.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/power.c b/drivers/acpi/power.c index 0003f1009885..ac02af419b32 100644 --- a/drivers/acpi/power.c +++ b/drivers/acpi/power.c @@ -219,7 +219,7 @@ static int acpi_power_on(acpi_handle handle) return result; } -static int acpi_power_off_device(acpi_handle handle) +static int acpi_power_off(acpi_handle handle) { int result = 0; acpi_status status = AE_OK; @@ -268,7 +268,7 @@ static void __acpi_power_off_list(struct acpi_handle_list *list, int num_res) int i; for (i = num_res - 1; i >= 0 ; i--) - acpi_power_off_device(list->handles[i]); + acpi_power_off(list->handles[i]); } static void acpi_power_off_list(struct acpi_handle_list *list) @@ -430,8 +430,7 @@ int acpi_disable_wakeup_device_power(struct acpi_device *dev) /* Close power resource */ for (i = 0; i < dev->wakeup.resources.count; i++) { - int ret = acpi_power_off_device( - dev->wakeup.resources.handles[i]); + int ret = acpi_power_off(dev->wakeup.resources.handles[i]); if (ret) { printk(KERN_ERR PREFIX "Transition power state\n"); dev->wakeup.flags.valid = 0; -- cgit v1.2.3 From d0515d9fec68bace144fda57a69f4268fb875209 Mon Sep 17 00:00:00 2001 From: Rafael J. Wysocki Date: Thu, 6 Jan 2011 23:38:57 +0100 Subject: ACPI / PM: Check status of power resources under mutexes It certainly is not a good idea to execute _ON or _OFF and _STA for the same power resource at the same time which may happen in some circumstances in theory. To prevent that from happening, read the power state of each power resource under its mutex, as that will prevent the state from being changed at the same time. Signed-off-by: Rafael J. Wysocki Signed-off-by: Len Brown --- drivers/acpi/power.c | 33 ++++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/power.c b/drivers/acpi/power.c index ac02af419b32..9ac2a9fa90ff 100644 --- a/drivers/acpi/power.c +++ b/drivers/acpi/power.c @@ -145,9 +145,8 @@ static int acpi_power_get_state(acpi_handle handle, int *state) static int acpi_power_get_list_state(struct acpi_handle_list *list, int *state) { - int result = 0, state1; - u32 i = 0; - + int cur_state; + int i = 0; if (!list || !state) return -EINVAL; @@ -155,25 +154,33 @@ static int acpi_power_get_list_state(struct acpi_handle_list *list, int *state) /* The state of the list is 'on' IFF all resources are 'on'. */ for (i = 0; i < list->count; i++) { - /* - * The state of the power resource can be obtained by - * using the ACPI handle. In such case it is unnecessary to - * get the Power resource first and then get its state again. - */ - result = acpi_power_get_state(list->handles[i], &state1); + struct acpi_power_resource *resource; + acpi_handle handle = list->handles[i]; + int result; + + result = acpi_power_get_context(handle, &resource); if (result) return result; - *state = state1; + mutex_lock(&resource->resource_lock); - if (*state != ACPI_POWER_RESOURCE_STATE_ON) + result = acpi_power_get_state(handle, &cur_state); + + mutex_unlock(&resource->resource_lock); + + if (result) + return result; + + if (cur_state != ACPI_POWER_RESOURCE_STATE_ON) break; } ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Resource list is %s\n", - *state ? "on" : "off")); + cur_state ? "on" : "off")); - return result; + *state = cur_state; + + return 0; } static int __acpi_power_on(struct acpi_power_resource *resource) -- cgit v1.2.3 From 86e4e20e8a5301ff7104a4f40f35fd5bee408186 Mon Sep 17 00:00:00 2001 From: Rafael J. Wysocki Date: Thu, 6 Jan 2011 23:40:00 +0100 Subject: ACPI: Always check if _PRW is present before trying to evaluate it Before evaluating _PRW for devices that are reported as inactive or not present by their _STA control methods we should check if those methods are actually present (otherwise the evaulation of _PRW will obviously fail and a scary message will be printed unnecessarily). Reported-by: Andreas Mohr Reported-by: Maciej Rutecki Signed-off-by: Rafael J. Wysocki Signed-off-by: Len Brown --- drivers/acpi/scan.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 64d4da0d6d52..b5e4dedb497d 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -1388,7 +1388,6 @@ static acpi_status acpi_bus_check_add(acpi_handle handle, u32 lvl, struct acpi_bus_ops *ops = context; int type; unsigned long long sta; - struct acpi_device_wakeup wakeup; struct acpi_device *device; acpi_status status; int result; @@ -1399,7 +1398,13 @@ static acpi_status acpi_bus_check_add(acpi_handle handle, u32 lvl, if (!(sta & ACPI_STA_DEVICE_PRESENT) && !(sta & ACPI_STA_DEVICE_FUNCTIONING)) { - acpi_bus_extract_wakeup_device_power_package(handle, &wakeup); + struct acpi_device_wakeup wakeup; + acpi_handle temp; + + status = acpi_get_handle(handle, "_PRW", &temp); + if (ACPI_SUCCESS(status)) + acpi_bus_extract_wakeup_device_power_package(handle, + &wakeup); return AE_CTRL_DEPTH; } -- cgit v1.2.3 From d57d09a480e1db38eeee7629c81289b00f338a15 Mon Sep 17 00:00:00 2001 From: Rafael J. Wysocki Date: Thu, 6 Jan 2011 23:41:27 +0100 Subject: ACPI: Drop device flag wake_capable The wake_capable ACPI device flag is not necessary, because it is only used in scan.c for recording the information that _PRW is present for the given device. That information is only used by acpi_add_single_object() to decide whether or not to call acpi_bus_get_wakeup_device_flags(), so the flag may be dropped if the _PRW check is moved to acpi_bus_get_wakeup_device_flags(). Moreover, acpi_bus_get_wakeup_device_flags() always returns 0, so it really should be void. Signed-off-by: Rafael J. Wysocki Signed-off-by: Len Brown --- drivers/acpi/scan.c | 26 +++++++++----------------- include/acpi/acpi_bus.h | 3 +-- 2 files changed, 10 insertions(+), 19 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index b5e4dedb497d..b99e62494607 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -815,16 +815,22 @@ static void acpi_bus_set_run_wake_flags(struct acpi_device *device) !!(event_status & ACPI_EVENT_FLAG_HANDLE); } -static int acpi_bus_get_wakeup_device_flags(struct acpi_device *device) +static void acpi_bus_get_wakeup_device_flags(struct acpi_device *device) { + acpi_handle temp; acpi_status status = 0; int psw_error; + /* Presence of _PRW indicates wake capable */ + status = acpi_get_handle(device->handle, "_PRW", &temp); + if (ACPI_FAILURE(status)) + return; + status = acpi_bus_extract_wakeup_device_power_package(device->handle, &device->wakeup); if (ACPI_FAILURE(status)) { ACPI_EXCEPTION((AE_INFO, status, "Extracting _PRW package")); - goto end; + return; } device->wakeup.flags.valid = 1; @@ -840,11 +846,6 @@ static int acpi_bus_get_wakeup_device_flags(struct acpi_device *device) if (psw_error) ACPI_DEBUG_PRINT((ACPI_DB_INFO, "error in _DSW or _PSW evaluation\n")); - -end: - if (ACPI_FAILURE(status)) - device->flags.wake_capable = 0; - return 0; } static void acpi_bus_add_power_resource(acpi_handle handle); @@ -950,11 +951,6 @@ static int acpi_bus_get_flags(struct acpi_device *device) if (ACPI_SUCCESS(status)) device->flags.power_manageable = 1; - /* Presence of _PRW indicates wake capable */ - status = acpi_get_handle(device->handle, "_PRW", &temp); - if (ACPI_SUCCESS(status)) - device->flags.wake_capable = 1; - /* TBD: Performance management */ return 0; @@ -1281,11 +1277,7 @@ static int acpi_add_single_object(struct acpi_device **child, * Wakeup device management *----------------------- */ - if (device->flags.wake_capable) { - result = acpi_bus_get_wakeup_device_flags(device); - if (result) - goto end; - } + acpi_bus_get_wakeup_device_flags(device); /* * Performance Management diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index 20b05cd68663..78ca429929f7 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -148,8 +148,7 @@ struct acpi_device_flags { u32 suprise_removal_ok:1; u32 power_manageable:1; u32 performance_manageable:1; - u32 wake_capable:1; /* Wakeup(_PRW) supported? */ - u32 reserved:23; + u32 reserved:24; }; /* File System */ -- cgit v1.2.3 From da8aeb92d4853f37e281f11fddf61f9c7d84c3cd Mon Sep 17 00:00:00 2001 From: Rafael J. Wysocki Date: Thu, 6 Jan 2011 23:42:27 +0100 Subject: ACPI / Battery: Update information on info notification and resume A notification event 0x81 from an ACPI battery device requires us to re-read the battery information structure. Follow this requirement and remove and re-create the battery's attibutes in sysfs so that they reflect the reporting units used by the battery at the moment (those units may actually change sometimes at run time, which happens on some Thinkpads). The approach used in this patch was suggested by Matthew Garrett. Signed-off-by: Rafael J. Wysocki Reported-by: Matthew Garrett Signed-off-by: Len Brown --- drivers/acpi/battery.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'drivers/acpi') diff --git a/drivers/acpi/battery.c b/drivers/acpi/battery.c index 2a31421e0d75..68bc227e7c4c 100644 --- a/drivers/acpi/battery.c +++ b/drivers/acpi/battery.c @@ -631,6 +631,17 @@ static int acpi_battery_update(struct acpi_battery *battery) return result; } +static void acpi_battery_refresh(struct acpi_battery *battery) +{ + if (!battery->bat.dev) + return; + + acpi_battery_get_info(battery); + /* The battery may have changed its reporting units. */ + sysfs_remove_battery(battery); + sysfs_add_battery(battery); +} + /* -------------------------------------------------------------------------- FS Interface (/proc) -------------------------------------------------------------------------- */ @@ -916,6 +927,8 @@ static void acpi_battery_notify(struct acpi_device *device, u32 event) if (!battery) return; old = battery->bat.dev; + if (event == ACPI_BATTERY_NOTIFY_INFO) + acpi_battery_refresh(battery); acpi_battery_update(battery); acpi_bus_generate_proc_event(device, event, acpi_battery_present(battery)); @@ -985,6 +998,7 @@ static int acpi_battery_resume(struct acpi_device *device) if (!device) return -EINVAL; battery = acpi_driver_data(device); + acpi_battery_refresh(battery); battery->update_time = 0; acpi_battery_update(battery); return 0; -- cgit v1.2.3 From 106d1a0ab2a354b97df3e232be7dedbfaf8e901d Mon Sep 17 00:00:00 2001 From: Thomas Renninger Date: Mon, 20 Dec 2010 12:11:45 +0100 Subject: ACPI: fix resource check message printk("%pR",...) is for formatting struct resource only. But the list built up in drivers/acpi/osl.c uses it's own struct: struct acpi_res_list {} Without this patch you can see wrongly formatted resources (SMRG is of IO type): ACPI: resource 0000:00:1f.3 [io 0x0400-0x041f] conflicts with AC PI region SMRG [mem 0x00000400-0x0000040f 64bit pref disabled] https://bugzilla.kernel.org/show_bug.cgi?id=26342 Signed-off-by: Thomas Renninger CC: Matthew Wilcox Signed-off-by: Len Brown --- drivers/acpi/osl.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c index 966feddf6b1b..6eaa13cdf767 100644 --- a/drivers/acpi/osl.c +++ b/drivers/acpi/osl.c @@ -1192,8 +1192,7 @@ __setup("acpi_enforce_resources=", acpi_enforce_resources_setup); int acpi_check_resource_conflict(const struct resource *res) { struct acpi_res_list *res_list_elem; - int ioport; - int clash = 0; + int ioport = 0, clash = 0; if (acpi_enforce_resources == ENFORCE_RESOURCES_NO) return 0; @@ -1223,9 +1222,13 @@ int acpi_check_resource_conflict(const struct resource *res) if (clash) { if (acpi_enforce_resources != ENFORCE_RESOURCES_NO) { printk(KERN_WARNING "ACPI: resource %s %pR" - " conflicts with ACPI region %s %pR\n", + " conflicts with ACPI region %s " + "[%s 0x%zx-0x%zx]\n", res->name, res, res_list_elem->name, - res_list_elem); + (res_list_elem->resource_type == + ACPI_ADR_SPACE_SYSTEM_IO) ? "io" : "mem", + (size_t) res_list_elem->start, + (size_t) res_list_elem->end); if (acpi_enforce_resources == ENFORCE_RESOURCES_LAX) printk(KERN_NOTICE "ACPI: This conflict may" " cause random problems and system" -- cgit v1.2.3 From 6fed05c9c9812b5882bc708f4da4fa8d5df2875c Mon Sep 17 00:00:00 2001 From: Rafael J. Wysocki Date: Wed, 12 Jan 2011 22:03:20 +0100 Subject: ACPI / PM: Fix build problems for !CONFIG_ACPI related to NVS rework The recent rework of the NVS saving/restoring code introduced two build issues for !CONFIG_ACPI, a warning in drivers/acpi/internal.h and an error in arch/x86/kernel/e820.c. Fix them by providing suitable static inline definitions of the relevant functions. Signed-off-by: Rafael J. Wysocki Acked-by: Randy Dunlap Signed-off-by: Len Brown --- drivers/acpi/internal.h | 2 +- include/linux/acpi.h | 19 ++++++++++--------- 2 files changed, 11 insertions(+), 10 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h index 7c23b76e8eca..6d69bbedf97a 100644 --- a/drivers/acpi/internal.h +++ b/drivers/acpi/internal.h @@ -90,7 +90,7 @@ void suspend_nvs_restore(void); static inline int acpi_sleep_proc_init(void) { return 0; } static inline int suspend_nvs_alloc(void) { return 0; } static inline void suspend_nvs_free(void) {} -static inline int suspend_nvs_save(void) {} +static inline int suspend_nvs_save(void) { return 0; } static inline void suspend_nvs_restore(void) {} #endif diff --git a/include/linux/acpi.h b/include/linux/acpi.h index fa7ed6a983d0..eb176bb1b15b 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -254,15 +254,6 @@ void __init acpi_old_suspend_ordering(void); void __init acpi_nvs_nosave(void); #endif /* CONFIG_PM_SLEEP */ -#ifdef CONFIG_ACPI_SLEEP -int suspend_nvs_register(unsigned long start, unsigned long size); -#else -static inline int suspend_nvs_register(unsigned long a, unsigned long b) -{ - return 0; -} -#endif - struct acpi_osc_context { char *uuid_str; /* uuid string */ int rev; @@ -361,4 +352,14 @@ static inline int acpi_table_parse(char *id, return -1; } #endif /* !CONFIG_ACPI */ + +#ifdef CONFIG_ACPI_SLEEP +int suspend_nvs_register(unsigned long start, unsigned long size); +#else +static inline int suspend_nvs_register(unsigned long a, unsigned long b) +{ + return 0; +} +#endif + #endif /*_LINUX_ACPI_H*/ -- cgit v1.2.3