diff options
Diffstat (limited to 'drivers/acpi')
-rw-r--r-- | drivers/acpi/acpica/aclocal.h | 7 | ||||
-rw-r--r-- | drivers/acpi/acpica/evgpe.c | 17 | ||||
-rw-r--r-- | drivers/acpi/acpica/evxfgpe.c | 42 | ||||
-rw-r--r-- | drivers/acpi/apei/Kconfig | 7 | ||||
-rw-r--r-- | drivers/acpi/apei/cper.c | 18 | ||||
-rw-r--r-- | drivers/acpi/apei/erst-dbg.c | 24 | ||||
-rw-r--r-- | drivers/acpi/apei/erst.c | 235 | ||||
-rw-r--r-- | drivers/acpi/button.c | 174 | ||||
-rw-r--r-- | drivers/acpi/debugfs.c | 20 | ||||
-rw-r--r-- | drivers/acpi/nvs.c | 22 | ||||
-rw-r--r-- | drivers/acpi/osl.c | 139 | ||||
-rw-r--r-- | drivers/acpi/scan.c | 1 | ||||
-rw-r--r-- | drivers/acpi/sleep.c | 28 |
13 files changed, 469 insertions, 265 deletions
diff --git a/drivers/acpi/acpica/aclocal.h b/drivers/acpi/acpica/aclocal.h index 01bcab1c5cc..c7f743ca395 100644 --- a/drivers/acpi/acpica/aclocal.h +++ b/drivers/acpi/acpica/aclocal.h @@ -397,10 +397,15 @@ struct acpi_gpe_handler_info { u8 originally_enabled; /* True if GPE was originally enabled */ }; +struct acpi_gpe_notify_object { + struct acpi_namespace_node *node; + struct acpi_gpe_notify_object *next; +}; + 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 */ + struct acpi_gpe_notify_object device; /* List of _PRW devices for implicit notify */ }; /* diff --git a/drivers/acpi/acpica/evgpe.c b/drivers/acpi/acpica/evgpe.c index 67d44ef2fbc..65c79add3b1 100644 --- a/drivers/acpi/acpica/evgpe.c +++ b/drivers/acpi/acpica/evgpe.c @@ -466,6 +466,7 @@ static void ACPI_SYSTEM_XFACE acpi_ev_asynch_execute_gpe_method(void *context) acpi_status status; struct acpi_gpe_event_info *local_gpe_event_info; struct acpi_evaluate_info *info; + struct acpi_gpe_notify_object *notify_object; ACPI_FUNCTION_TRACE(ev_asynch_execute_gpe_method); @@ -517,10 +518,18 @@ static void ACPI_SYSTEM_XFACE acpi_ev_asynch_execute_gpe_method(void *context) * 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); + status = acpi_ev_queue_notify_request( + local_gpe_event_info->dispatch.device.node, + ACPI_NOTIFY_DEVICE_WAKE); + + notify_object = local_gpe_event_info->dispatch.device.next; + while (ACPI_SUCCESS(status) && notify_object) { + status = acpi_ev_queue_notify_request( + notify_object->node, + ACPI_NOTIFY_DEVICE_WAKE); + notify_object = notify_object->next; + } + break; case ACPI_GPE_DISPATCH_METHOD: diff --git a/drivers/acpi/acpica/evxfgpe.c b/drivers/acpi/acpica/evxfgpe.c index 3b20a3401b6..52aaff3df56 100644 --- a/drivers/acpi/acpica/evxfgpe.c +++ b/drivers/acpi/acpica/evxfgpe.c @@ -198,7 +198,9 @@ acpi_setup_gpe_for_wake(acpi_handle wake_device, acpi_status status = AE_BAD_PARAMETER; struct acpi_gpe_event_info *gpe_event_info; struct acpi_namespace_node *device_node; + struct acpi_gpe_notify_object *notify_object; acpi_cpu_flags flags; + u8 gpe_dispatch_mask; ACPI_FUNCTION_TRACE(acpi_setup_gpe_for_wake); @@ -221,27 +223,49 @@ acpi_setup_gpe_for_wake(acpi_handle wake_device, goto unlock_and_exit; } + if (wake_device == ACPI_ROOT_OBJECT) { + goto out; + } + /* * 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) && (wake_device != ACPI_ROOT_OBJECT)) { + gpe_dispatch_mask = gpe_event_info->flags & ACPI_GPE_DISPATCH_MASK; + if (gpe_dispatch_mask != ACPI_GPE_DISPATCH_NONE + && gpe_dispatch_mask != ACPI_GPE_DISPATCH_NOTIFY) { + goto out; + } - /* Validate wake_device is of type Device */ + /* 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) { - goto unlock_and_exit; - } + device_node = ACPI_CAST_PTR(struct acpi_namespace_node, wake_device); + if (device_node->type != ACPI_TYPE_DEVICE) { + goto unlock_and_exit; + } + + if (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->dispatch.device.node = device_node; + gpe_event_info->dispatch.device.next = NULL; + } else { + /* There are multiple devices to notify implicitly. */ + + notify_object = ACPI_ALLOCATE_ZEROED(sizeof(*notify_object)); + if (!notify_object) { + status = AE_NO_MEMORY; + goto unlock_and_exit; + } + + notify_object->node = device_node; + notify_object->next = gpe_event_info->dispatch.device.next; + gpe_event_info->dispatch.device.next = notify_object; } + out: gpe_event_info->flags |= ACPI_GPE_CAN_WAKE; status = AE_OK; diff --git a/drivers/acpi/apei/Kconfig b/drivers/acpi/apei/Kconfig index fca34ccfd29..9ecf6feae83 100644 --- a/drivers/acpi/apei/Kconfig +++ b/drivers/acpi/apei/Kconfig @@ -21,6 +21,13 @@ config ACPI_APEI_GHES by firmware to produce more valuable hardware error information for Linux. +config ACPI_APEI_PCIEAER + bool "APEI PCIe AER logging/recovering support" + depends on ACPI_APEI && PCIEAER + help + PCIe AER errors may be reported via APEI firmware first mode. + Turn on this option to enable the corresponding support. + config ACPI_APEI_EINJ tristate "APEI Error INJection (EINJ)" depends on ACPI_APEI && DEBUG_FS diff --git a/drivers/acpi/apei/cper.c b/drivers/acpi/apei/cper.c index 31464a006d7..5d4189464d6 100644 --- a/drivers/acpi/apei/cper.c +++ b/drivers/acpi/apei/cper.c @@ -29,6 +29,7 @@ #include <linux/time.h> #include <linux/cper.h> #include <linux/acpi.h> +#include <linux/aer.h> /* * CPER record ID need to be unique even after reboot, because record @@ -70,8 +71,8 @@ static const char *cper_severity_str(unsigned int severity) * 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) +void cper_print_bits(const char *pfx, unsigned int bits, + const char *strs[], unsigned int strs_size) { int i, len = 0; const char *str; @@ -81,6 +82,8 @@ static void cper_print_bits(const char *pfx, unsigned int bits, if (!(bits & (1U << i))) continue; str = strs[i]; + if (!str) + continue; if (len && len + strlen(str) + 2 > 80) { printk("%s\n", buf); len = 0; @@ -243,7 +246,8 @@ static const char *cper_pcie_port_type_strs[] = { "root complex event collector", }; -static void cper_print_pcie(const char *pfx, const struct cper_sec_pcie *pcie) +static void cper_print_pcie(const char *pfx, const struct cper_sec_pcie *pcie, + const struct acpi_hest_generic_data *gdata) { if (pcie->validation_bits & CPER_PCIE_VALID_PORT_TYPE) printk("%s""port_type: %d, %s\n", pfx, pcie->port_type, @@ -276,6 +280,12 @@ static void cper_print_pcie(const char *pfx, const struct cper_sec_pcie *pcie) printk( "%s""bridge: secondary_status: 0x%04x, control: 0x%04x\n", pfx, pcie->bridge.secondary_status, pcie->bridge.control); +#ifdef CONFIG_ACPI_APEI_PCIEAER + if (pcie->validation_bits & CPER_PCIE_VALID_AER_INFO) { + struct aer_capability_regs *aer_regs = (void *)pcie->aer_info; + cper_print_aer(pfx, gdata->error_severity, aer_regs); + } +#endif } static const char *apei_estatus_section_flag_strs[] = { @@ -322,7 +332,7 @@ static void apei_estatus_print_section( 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); + cper_print_pcie(pfx, pcie, gdata); else goto err_section_too_small; } else diff --git a/drivers/acpi/apei/erst-dbg.c b/drivers/acpi/apei/erst-dbg.c index de73caf3ceb..a4cfb64c86a 100644 --- a/drivers/acpi/apei/erst-dbg.c +++ b/drivers/acpi/apei/erst-dbg.c @@ -43,12 +43,27 @@ static DEFINE_MUTEX(erst_dbg_mutex); static int erst_dbg_open(struct inode *inode, struct file *file) { + int rc, *pos; + if (erst_disable) return -ENODEV; + pos = (int *)&file->private_data; + + rc = erst_get_record_id_begin(pos); + if (rc) + return rc; + return nonseekable_open(inode, file); } +static int erst_dbg_release(struct inode *inode, struct file *file) +{ + erst_get_record_id_end(); + + return 0; +} + static long erst_dbg_ioctl(struct file *f, unsigned int cmd, unsigned long arg) { int rc; @@ -79,18 +94,20 @@ static long erst_dbg_ioctl(struct file *f, unsigned int cmd, unsigned long arg) static ssize_t erst_dbg_read(struct file *filp, char __user *ubuf, size_t usize, loff_t *off) { - int rc; + int rc, *pos; ssize_t len = 0; u64 id; - if (*off != 0) + if (*off) return -EINVAL; if (mutex_lock_interruptible(&erst_dbg_mutex) != 0) return -EINTR; + pos = (int *)&filp->private_data; + retry_next: - rc = erst_get_next_record_id(&id); + rc = erst_get_record_id_next(pos, &id); if (rc) goto out; /* no more record */ @@ -181,6 +198,7 @@ out: static const struct file_operations erst_dbg_ops = { .owner = THIS_MODULE, .open = erst_dbg_open, + .release = erst_dbg_release, .read = erst_dbg_read, .write = erst_dbg_write, .unlocked_ioctl = erst_dbg_ioctl, diff --git a/drivers/acpi/apei/erst.c b/drivers/acpi/apei/erst.c index cf6db6b7662..8ff8c32fef5 100644 --- a/drivers/acpi/apei/erst.c +++ b/drivers/acpi/apei/erst.c @@ -429,6 +429,22 @@ ssize_t erst_get_record_count(void) } EXPORT_SYMBOL_GPL(erst_get_record_count); +#define ERST_RECORD_ID_CACHE_SIZE_MIN 16 +#define ERST_RECORD_ID_CACHE_SIZE_MAX 1024 + +struct erst_record_id_cache { + struct mutex lock; + u64 *entries; + int len; + int size; + int refcount; +}; + +static struct erst_record_id_cache erst_record_id_cache = { + .lock = __MUTEX_INITIALIZER(erst_record_id_cache.lock), + .refcount = 0, +}; + static int __erst_get_next_record_id(u64 *record_id) { struct apei_exec_context ctx; @@ -443,26 +459,179 @@ static int __erst_get_next_record_id(u64 *record_id) return 0; } +int erst_get_record_id_begin(int *pos) +{ + int rc; + + if (erst_disable) + return -ENODEV; + + rc = mutex_lock_interruptible(&erst_record_id_cache.lock); + if (rc) + return rc; + erst_record_id_cache.refcount++; + mutex_unlock(&erst_record_id_cache.lock); + + *pos = 0; + + return 0; +} +EXPORT_SYMBOL_GPL(erst_get_record_id_begin); + +/* erst_record_id_cache.lock must be held by caller */ +static int __erst_record_id_cache_add_one(void) +{ + u64 id, prev_id, first_id; + int i, rc; + u64 *entries; + unsigned long flags; + + id = prev_id = first_id = APEI_ERST_INVALID_RECORD_ID; +retry: + raw_spin_lock_irqsave(&erst_lock, flags); + rc = __erst_get_next_record_id(&id); + raw_spin_unlock_irqrestore(&erst_lock, flags); + if (rc == -ENOENT) + return 0; + if (rc) + return rc; + if (id == APEI_ERST_INVALID_RECORD_ID) + return 0; + /* can not skip current ID, or loop back to first ID */ + if (id == prev_id || id == first_id) + return 0; + if (first_id == APEI_ERST_INVALID_RECORD_ID) + first_id = id; + prev_id = id; + + entries = erst_record_id_cache.entries; + for (i = 0; i < erst_record_id_cache.len; i++) { + if (entries[i] == id) + break; + } + /* record id already in cache, try next */ + if (i < erst_record_id_cache.len) + goto retry; + if (erst_record_id_cache.len >= erst_record_id_cache.size) { + int new_size, alloc_size; + u64 *new_entries; + + new_size = erst_record_id_cache.size * 2; + new_size = clamp_val(new_size, ERST_RECORD_ID_CACHE_SIZE_MIN, + ERST_RECORD_ID_CACHE_SIZE_MAX); + if (new_size <= erst_record_id_cache.size) { + if (printk_ratelimit()) + pr_warning(FW_WARN ERST_PFX + "too many record ID!\n"); + return 0; + } + alloc_size = new_size * sizeof(entries[0]); + if (alloc_size < PAGE_SIZE) + new_entries = kmalloc(alloc_size, GFP_KERNEL); + else + new_entries = vmalloc(alloc_size); + if (!new_entries) + return -ENOMEM; + memcpy(new_entries, entries, + erst_record_id_cache.len * sizeof(entries[0])); + if (erst_record_id_cache.size < PAGE_SIZE) + kfree(entries); + else + vfree(entries); + erst_record_id_cache.entries = entries = new_entries; + erst_record_id_cache.size = new_size; + } + entries[i] = id; + erst_record_id_cache.len++; + + return 1; +} + /* * Get the record ID of an existing error record on the persistent * storage. If there is no error record on the persistent storage, the * returned record_id is APEI_ERST_INVALID_RECORD_ID. */ -int erst_get_next_record_id(u64 *record_id) +int erst_get_record_id_next(int *pos, u64 *record_id) { - int rc; - unsigned long flags; + int rc = 0; + u64 *entries; if (erst_disable) return -ENODEV; - raw_spin_lock_irqsave(&erst_lock, flags); - rc = __erst_get_next_record_id(record_id); - raw_spin_unlock_irqrestore(&erst_lock, flags); + /* must be enclosed by erst_get_record_id_begin/end */ + BUG_ON(!erst_record_id_cache.refcount); + BUG_ON(*pos < 0 || *pos > erst_record_id_cache.len); + + mutex_lock(&erst_record_id_cache.lock); + entries = erst_record_id_cache.entries; + for (; *pos < erst_record_id_cache.len; (*pos)++) + if (entries[*pos] != APEI_ERST_INVALID_RECORD_ID) + break; + /* found next record id in cache */ + if (*pos < erst_record_id_cache.len) { + *record_id = entries[*pos]; + (*pos)++; + goto out_unlock; + } + + /* Try to add one more record ID to cache */ + rc = __erst_record_id_cache_add_one(); + if (rc < 0) + goto out_unlock; + /* successfully add one new ID */ + if (rc == 1) { + *record_id = erst_record_id_cache.entries[*pos]; + (*pos)++; + rc = 0; + } else { + *pos = -1; + *record_id = APEI_ERST_INVALID_RECORD_ID; + } +out_unlock: + mutex_unlock(&erst_record_id_cache.lock); return rc; } -EXPORT_SYMBOL_GPL(erst_get_next_record_id); +EXPORT_SYMBOL_GPL(erst_get_record_id_next); + +/* erst_record_id_cache.lock must be held by caller */ +static void __erst_record_id_cache_compact(void) +{ + int i, wpos = 0; + u64 *entries; + + if (erst_record_id_cache.refcount) + return; + + entries = erst_record_id_cache.entries; + for (i = 0; i < erst_record_id_cache.len; i++) { + if (entries[i] == APEI_ERST_INVALID_RECORD_ID) + continue; + if (wpos != i) + memcpy(&entries[wpos], &entries[i], sizeof(entries[i])); + wpos++; + } + erst_record_id_cache.len = wpos; +} + +void erst_get_record_id_end(void) +{ + /* + * erst_disable != 0 should be detected by invoker via the + * return value of erst_get_record_id_begin/next, so this + * function should not be called for erst_disable != 0. + */ + BUG_ON(erst_disable); + + mutex_lock(&erst_record_id_cache.lock); + erst_record_id_cache.refcount--; + BUG_ON(erst_record_id_cache.refcount < 0); + __erst_record_id_cache_compact(); + mutex_unlock(&erst_record_id_cache.lock); +} +EXPORT_SYMBOL_GPL(erst_get_record_id_end); static int __erst_write_to_storage(u64 offset) { @@ -703,56 +872,34 @@ ssize_t erst_read(u64 record_id, struct cper_record_header *record, } EXPORT_SYMBOL_GPL(erst_read); -/* - * If return value > buflen, the buffer size is not big enough, - * else if return value = 0, there is no more record to read, - * else if return value < 0, something goes wrong, - * else everything is OK, and return value is record length - */ -ssize_t erst_read_next(struct cper_record_header *record, size_t buflen) -{ - int rc; - ssize_t len; - unsigned long flags; - u64 record_id; - - if (erst_disable) - return -ENODEV; - - raw_spin_lock_irqsave(&erst_lock, flags); - rc = __erst_get_next_record_id(&record_id); - if (rc) { - raw_spin_unlock_irqrestore(&erst_lock, flags); - return rc; - } - /* no more record */ - if (record_id == APEI_ERST_INVALID_RECORD_ID) { - raw_spin_unlock_irqrestore(&erst_lock, flags); - return 0; - } - - len = __erst_read(record_id, record, buflen); - raw_spin_unlock_irqrestore(&erst_lock, flags); - - return len; -} -EXPORT_SYMBOL_GPL(erst_read_next); - int erst_clear(u64 record_id) { - int rc; + int rc, i; unsigned long flags; + u64 *entries; if (erst_disable) return -ENODEV; + rc = mutex_lock_interruptible(&erst_record_id_cache.lock); + if (rc) + return rc; raw_spin_lock_irqsave(&erst_lock, flags); if (erst_erange.attr & ERST_RANGE_NVRAM) rc = __erst_clear_from_nvram(record_id); else rc = __erst_clear_from_storage(record_id); raw_spin_unlock_irqrestore(&erst_lock, flags); - + if (rc) + goto out; + entries = erst_record_id_cache.entries; + for (i = 0; i < erst_record_id_cache.len; i++) { + if (entries[i] == record_id) + entries[i] = APEI_ERST_INVALID_RECORD_ID; + } + __erst_record_id_cache_compact(); +out: + mutex_unlock(&erst_record_id_cache.lock); return rc; } EXPORT_SYMBOL_GPL(erst_clear); diff --git a/drivers/acpi/button.c b/drivers/acpi/button.c index 76bbb78a5ad..d27d072472f 100644 --- a/drivers/acpi/button.c +++ b/drivers/acpi/button.c @@ -78,8 +78,6 @@ static int acpi_button_add(struct acpi_device *device); static int acpi_button_remove(struct acpi_device *device, int type); static int acpi_button_resume(struct acpi_device *device); static void acpi_button_notify(struct acpi_device *device, u32 event); -static int acpi_button_info_open_fs(struct inode *inode, struct file *file); -static int acpi_button_state_open_fs(struct inode *inode, struct file *file); static struct acpi_driver acpi_button_driver = { .name = "button", @@ -98,22 +96,7 @@ struct acpi_button { struct input_dev *input; char phys[32]; /* for input device */ unsigned long pushed; -}; - -static const struct file_operations acpi_button_info_fops = { - .owner = THIS_MODULE, - .open = acpi_button_info_open_fs, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -static const struct file_operations acpi_button_state_fops = { - .owner = THIS_MODULE, - .open = acpi_button_state_open_fs, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, + bool wakeup_enabled; }; static BLOCKING_NOTIFIER_HEAD(acpi_lid_notifier); @@ -124,20 +107,7 @@ static struct acpi_device *lid_device; -------------------------------------------------------------------------- */ static struct proc_dir_entry *acpi_button_dir; - -static int acpi_button_info_seq_show(struct seq_file *seq, void *offset) -{ - struct acpi_device *device = seq->private; - - seq_printf(seq, "type: %s\n", - acpi_device_name(device)); - return 0; -} - -static int acpi_button_info_open_fs(struct inode *inode, struct file *file) -{ - return single_open(file, acpi_button_info_seq_show, PDE(inode)->data); -} +static struct proc_dir_entry *acpi_lid_dir; static int acpi_button_state_seq_show(struct seq_file *seq, void *offset) { @@ -157,77 +127,85 @@ static int acpi_button_state_open_fs(struct inode *inode, struct file *file) return single_open(file, acpi_button_state_seq_show, PDE(inode)->data); } -static struct proc_dir_entry *acpi_power_dir; -static struct proc_dir_entry *acpi_sleep_dir; -static struct proc_dir_entry *acpi_lid_dir; +static const struct file_operations acpi_button_state_fops = { + .owner = THIS_MODULE, + .open = acpi_button_state_open_fs, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; static int acpi_button_add_fs(struct acpi_device *device) { struct acpi_button *button = acpi_driver_data(device); struct proc_dir_entry *entry = NULL; + int ret = 0; - switch (button->type) { - case ACPI_BUTTON_TYPE_POWER: - if (!acpi_power_dir) - acpi_power_dir = proc_mkdir(ACPI_BUTTON_SUBCLASS_POWER, - acpi_button_dir); - entry = acpi_power_dir; - break; - case ACPI_BUTTON_TYPE_SLEEP: - if (!acpi_sleep_dir) - acpi_sleep_dir = proc_mkdir(ACPI_BUTTON_SUBCLASS_SLEEP, - acpi_button_dir); - entry = acpi_sleep_dir; - break; - case ACPI_BUTTON_TYPE_LID: - if (!acpi_lid_dir) - acpi_lid_dir = proc_mkdir(ACPI_BUTTON_SUBCLASS_LID, - acpi_button_dir); - entry = acpi_lid_dir; - break; + /* procfs I/F for ACPI lid device only */ + if (button->type != ACPI_BUTTON_TYPE_LID) + return 0; + + if (acpi_button_dir || acpi_lid_dir) { + printk(KERN_ERR PREFIX "More than one Lid device found!\n"); + return -EEXIST; } - if (!entry) + /* create /proc/acpi/button */ + acpi_button_dir = proc_mkdir(ACPI_BUTTON_CLASS, acpi_root_dir); + if (!acpi_button_dir) return -ENODEV; - acpi_device_dir(device) = proc_mkdir(acpi_device_bid(device), entry); - if (!acpi_device_dir(device)) - return -ENODEV; + /* create /proc/acpi/button/lid */ + acpi_lid_dir = proc_mkdir(ACPI_BUTTON_SUBCLASS_LID, acpi_button_dir); + if (!acpi_lid_dir) { + ret = -ENODEV; + goto remove_button_dir; + } - /* 'info' [R] */ - entry = proc_create_data(ACPI_BUTTON_FILE_INFO, - S_IRUGO, acpi_device_dir(device), - &acpi_button_info_fops, device); - if (!entry) - return -ENODEV; + /* create /proc/acpi/button/lid/LID/ */ + acpi_device_dir(device) = proc_mkdir(acpi_device_bid(device), acpi_lid_dir); + if (!acpi_device_dir(device)) { + ret = -ENODEV; + goto remove_lid_dir; + } - /* show lid state [R] */ - if (button->type == ACPI_BUTTON_TYPE_LID) { - entry = proc_create_data(ACPI_BUTTON_FILE_STATE, - S_IRUGO, acpi_device_dir(device), - &acpi_button_state_fops, device); - if (!entry) - return -ENODEV; + /* create /proc/acpi/button/lid/LID/state */ + entry = proc_create_data(ACPI_BUTTON_FILE_STATE, + S_IRUGO, acpi_device_dir(device), + &acpi_button_state_fops, device); + if (!entry) { + ret = -ENODEV; + goto remove_dev_dir; } - return 0; +done: + return ret; + +remove_dev_dir: + remove_proc_entry(acpi_device_bid(device), + acpi_lid_dir); + acpi_device_dir(device) = NULL; +remove_lid_dir: + remove_proc_entry(ACPI_BUTTON_SUBCLASS_LID, acpi_button_dir); +remove_button_dir: + remove_proc_entry(ACPI_BUTTON_CLASS, acpi_root_dir); + goto done; } static int acpi_button_remove_fs(struct acpi_device *device) { struct acpi_button *button = acpi_driver_data(device); - if (acpi_device_dir(device)) { - if (button->type == ACPI_BUTTON_TYPE_LID) - remove_proc_entry(ACPI_BUTTON_FILE_STATE, - acpi_device_dir(device)); - remove_proc_entry(ACPI_BUTTON_FILE_INFO, - acpi_device_dir(device)); + if (button->type != ACPI_BUTTON_TYPE_LID) + return 0; - remove_proc_entry(acpi_device_bid(device), - acpi_device_dir(device)->parent); - acpi_device_dir(device) = NULL; - } + remove_proc_entry(ACPI_BUTTON_FILE_STATE, + acpi_device_dir(device)); + remove_proc_entry(acpi_device_bid(device), + acpi_lid_dir); + acpi_device_dir(device) = NULL; + remove_proc_entry(ACPI_BUTTON_SUBCLASS_LID, acpi_button_dir); + remove_proc_entry(ACPI_BUTTON_CLASS, acpi_root_dir); return 0; } @@ -430,8 +408,10 @@ static int acpi_button_add(struct acpi_device *device) /* Button's GPE is run-wake GPE */ acpi_enable_gpe(device->wakeup.gpe_device, device->wakeup.gpe_number); - device->wakeup.run_wake_count++; - device_set_wakeup_enable(&device->dev, true); + if (!device_may_wakeup(&device->dev)) { + device_set_wakeup_enable(&device->dev, true); + button->wakeup_enabled = true; + } } printk(KERN_INFO PREFIX "%s [%s]\n", name, acpi_device_bid(device)); @@ -453,8 +433,8 @@ static int acpi_button_remove(struct acpi_device *device, int type) if (device->wakeup.flags.valid) { acpi_disable_gpe(device->wakeup.gpe_device, device->wakeup.gpe_number); - device->wakeup.run_wake_count--; - device_set_wakeup_enable(&device->dev, false); + if (button->wakeup_enabled) + device_set_wakeup_enable(&device->dev, false); } acpi_button_remove_fs(device); @@ -465,32 +445,12 @@ static int acpi_button_remove(struct acpi_device *device, int type) static int __init acpi_button_init(void) { - int result; - - acpi_button_dir = proc_mkdir(ACPI_BUTTON_CLASS, acpi_root_dir); - if (!acpi_button_dir) - return -ENODEV; - - result = acpi_bus_register_driver(&acpi_button_driver); - if (result < 0) { - remove_proc_entry(ACPI_BUTTON_CLASS, acpi_root_dir); - return -ENODEV; - } - - return 0; + return acpi_bus_register_driver(&acpi_button_driver); } static void __exit acpi_button_exit(void) { acpi_bus_unregister_driver(&acpi_button_driver); - - if (acpi_power_dir) - remove_proc_entry(ACPI_BUTTON_SUBCLASS_POWER, acpi_button_dir); - if (acpi_sleep_dir) - remove_proc_entry(ACPI_BUTTON_SUBCLASS_SLEEP, acpi_button_dir); - if (acpi_lid_dir) - remove_proc_entry(ACPI_BUTTON_SUBCLASS_LID, acpi_button_dir); - remove_proc_entry(ACPI_BUTTON_CLASS, acpi_root_dir); } module_init(acpi_button_init); diff --git a/drivers/acpi/debugfs.c b/drivers/acpi/debugfs.c index 5df67f1d6c6..384f7abcff7 100644 --- a/drivers/acpi/debugfs.c +++ b/drivers/acpi/debugfs.c @@ -26,7 +26,9 @@ static ssize_t cm_write(struct file *file, const char __user * user_buf, size_t count, loff_t *ppos) { static char *buf; - static int uncopied_bytes; + static u32 max_size; + static u32 uncopied_bytes; + struct acpi_table_header table; acpi_status status; @@ -37,19 +39,24 @@ static ssize_t cm_write(struct file *file, const char __user * user_buf, if (copy_from_user(&table, user_buf, sizeof(struct acpi_table_header))) return -EFAULT; - uncopied_bytes = table.length; - buf = kzalloc(uncopied_bytes, GFP_KERNEL); + uncopied_bytes = max_size = table.length; + buf = kzalloc(max_size, GFP_KERNEL); if (!buf) return -ENOMEM; } - if (uncopied_bytes < count) { - kfree(buf); + if (buf == NULL) + return -EINVAL; + + if ((*ppos > max_size) || + (*ppos + count > max_size) || + (*ppos + count < count) || + (count > uncopied_bytes)) return -EINVAL; - } if (copy_from_user(buf + (*ppos), user_buf, count)) { kfree(buf); + buf = NULL; return -EFAULT; } @@ -59,6 +66,7 @@ static ssize_t cm_write(struct file *file, const char __user * user_buf, if (!uncopied_bytes) { status = acpi_install_method(buf); kfree(buf); + buf = NULL; if (ACPI_FAILURE(status)) return -EINVAL; add_taint(TAINT_OVERRIDDEN_ACPI_TABLE); diff --git a/drivers/acpi/nvs.c b/drivers/acpi/nvs.c index fa5a1df42b7..096787b43c9 100644 --- a/drivers/acpi/nvs.c +++ b/drivers/acpi/nvs.c @@ -26,6 +26,7 @@ struct nvs_page { unsigned int size; void *kaddr; void *data; + bool unmap; struct list_head node; }; @@ -44,6 +45,9 @@ int suspend_nvs_register(unsigned long start, unsigned long size) { struct nvs_page *entry, *next; + pr_info("PM: Registering ACPI NVS region at %lx (%ld bytes)\n", + start, size); + while (size > 0) { unsigned int nr_bytes; @@ -81,7 +85,13 @@ void suspend_nvs_free(void) free_page((unsigned long)entry->data); entry->data = NULL; if (entry->kaddr) { - iounmap(entry->kaddr); + if (entry->unmap) { + iounmap(entry->kaddr); + entry->unmap = false; + } else { + acpi_os_unmap_memory(entry->kaddr, + entry->size); + } entry->kaddr = NULL; } } @@ -115,8 +125,14 @@ int suspend_nvs_save(void) list_for_each_entry(entry, &nvs_list, node) if (entry->data) { - entry->kaddr = acpi_os_ioremap(entry->phys_start, - entry->size); + unsigned long phys = entry->phys_start; + unsigned int size = entry->size; + + entry->kaddr = acpi_os_get_iomem(phys, size); + if (!entry->kaddr) { + entry->kaddr = acpi_os_ioremap(phys, size); + entry->unmap = !!entry->kaddr; + } if (!entry->kaddr) { suspend_nvs_free(); return -ENOMEM; diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c index c90c76aa7f8..45c6ac8790d 100644 --- a/drivers/acpi/osl.c +++ b/drivers/acpi/osl.c @@ -76,7 +76,6 @@ EXPORT_SYMBOL(acpi_in_debugger); extern char line_buf[80]; #endif /*ENABLE_DEBUGGER */ -static unsigned int acpi_irq_irq; static acpi_osd_handler acpi_irq_handler; static void *acpi_irq_context; static struct workqueue_struct *kacpid_wq; @@ -105,11 +104,11 @@ struct acpi_ioremap { void __iomem *virt; acpi_physical_address phys; acpi_size size; - struct kref ref; + unsigned long refcount; }; static LIST_HEAD(acpi_ioremaps); -static DEFINE_SPINLOCK(acpi_ioremap_lock); +static DEFINE_MUTEX(acpi_ioremap_lock); static void __init acpi_osi_setup_late(void); @@ -285,6 +284,22 @@ acpi_map_vaddr_lookup(acpi_physical_address phys, unsigned int size) return NULL; } +void __iomem *acpi_os_get_iomem(acpi_physical_address phys, unsigned int size) +{ + struct acpi_ioremap *map; + void __iomem *virt = NULL; + + mutex_lock(&acpi_ioremap_lock); + map = acpi_map_lookup(phys, size); + if (map) { + virt = map->virt + (phys - map->phys); + map->refcount++; + } + mutex_unlock(&acpi_ioremap_lock); + return virt; +} +EXPORT_SYMBOL_GPL(acpi_os_get_iomem); + /* Must be called with 'acpi_ioremap_lock' or RCU read lock held. */ static struct acpi_ioremap * acpi_map_lookup_virt(void __iomem *virt, acpi_size size) @@ -302,8 +317,7 @@ acpi_map_lookup_virt(void __iomem *virt, acpi_size size) void __iomem *__init_refok acpi_os_map_memory(acpi_physical_address phys, acpi_size size) { - struct acpi_ioremap *map, *tmp_map; - unsigned long flags; + struct acpi_ioremap *map; void __iomem *virt; acpi_physical_address pg_off; acpi_size pg_sz; @@ -316,14 +330,25 @@ acpi_os_map_memory(acpi_physical_address phys, acpi_size size) if (!acpi_gbl_permanent_mmap) return __acpi_map_table((unsigned long)phys, size); + mutex_lock(&acpi_ioremap_lock); + /* Check if there's a suitable mapping already. */ + map = acpi_map_lookup(phys, size); + if (map) { + map->refcount++; + goto out; + } + map = kzalloc(sizeof(*map), GFP_KERNEL); - if (!map) + if (!map) { + mutex_unlock(&acpi_ioremap_lock); return NULL; + } pg_off = round_down(phys, PAGE_SIZE); pg_sz = round_up(phys + size, PAGE_SIZE) - pg_off; virt = acpi_os_ioremap(pg_off, pg_sz); if (!virt) { + mutex_unlock(&acpi_ioremap_lock); kfree(map); return NULL; } @@ -332,62 +357,51 @@ acpi_os_map_memory(acpi_physical_address phys, acpi_size size) map->virt = virt; map->phys = pg_off; map->size = pg_sz; - kref_init(&map->ref); - - spin_lock_irqsave(&acpi_ioremap_lock, flags); - /* Check if page has already been mapped. */ - tmp_map = acpi_map_lookup(phys, size); - if (tmp_map) { - kref_get(&tmp_map->ref); - spin_unlock_irqrestore(&acpi_ioremap_lock, flags); - iounmap(map->virt); - kfree(map); - return tmp_map->virt + (phys - tmp_map->phys); - } + map->refcount = 1; + list_add_tail_rcu(&map->list, &acpi_ioremaps); - spin_unlock_irqrestore(&acpi_ioremap_lock, flags); + out: + mutex_unlock(&acpi_ioremap_lock); return map->virt + (phys - map->phys); } EXPORT_SYMBOL_GPL(acpi_os_map_memory); -static void acpi_kref_del_iomap(struct kref *ref) +static void acpi_os_drop_map_ref(struct acpi_ioremap *map) { - struct acpi_ioremap *map; + if (!--map->refcount) + list_del_rcu(&map->list); +} - map = container_of(ref, struct acpi_ioremap, ref); - list_del_rcu(&map->list); +static void acpi_os_map_cleanup(struct acpi_ioremap *map) +{ + if (!map->refcount) { + synchronize_rcu(); + iounmap(map->virt); + kfree(map); + } } void __ref acpi_os_unmap_memory(void __iomem *virt, acpi_size size) { struct acpi_ioremap *map; - unsigned long flags; - int del; if (!acpi_gbl_permanent_mmap) { __acpi_unmap_table(virt, size); return; } - spin_lock_irqsave(&acpi_ioremap_lock, flags); + mutex_lock(&acpi_ioremap_lock); map = acpi_map_lookup_virt(virt, size); if (!map) { - spin_unlock_irqrestore(&acpi_ioremap_lock, flags); - printk(KERN_ERR PREFIX "%s: bad address %p\n", __func__, virt); - dump_stack(); + mutex_unlock(&acpi_ioremap_lock); + WARN(true, PREFIX "%s: bad address %p\n", __func__, virt); return; } + acpi_os_drop_map_ref(map); + mutex_unlock(&acpi_ioremap_lock); - del = kref_put(&map->ref, acpi_kref_del_iomap); - spin_unlock_irqrestore(&acpi_ioremap_lock, flags); - - if (!del) - return; - - synchronize_rcu(); - iounmap(map->virt); - kfree(map); + acpi_os_map_cleanup(map); } EXPORT_SYMBOL_GPL(acpi_os_unmap_memory); @@ -397,7 +411,7 @@ void __init early_acpi_os_unmap_memory(void __iomem *virt, acpi_size size) __acpi_unmap_table(virt, size); } -int acpi_os_map_generic_address(struct acpi_generic_address *addr) +static int acpi_os_map_generic_address(struct acpi_generic_address *addr) { void __iomem *virt; @@ -413,13 +427,10 @@ int acpi_os_map_generic_address(struct acpi_generic_address *addr) return 0; } -EXPORT_SYMBOL_GPL(acpi_os_map_generic_address); -void acpi_os_unmap_generic_address(struct acpi_generic_address *addr) +static void acpi_os_unmap_generic_address(struct acpi_generic_address *addr) { - void __iomem *virt; - unsigned long flags; - acpi_size size = addr->bit_width / 8; + struct acpi_ioremap *map; if (addr->space_id != ACPI_ADR_SPACE_SYSTEM_MEMORY) return; @@ -427,13 +438,17 @@ void acpi_os_unmap_generic_address(struct acpi_generic_address *addr) if (!addr->address || !addr->bit_width) return; - spin_lock_irqsave(&acpi_ioremap_lock, flags); - virt = acpi_map_vaddr_lookup(addr->address, size); - spin_unlock_irqrestore(&acpi_ioremap_lock, flags); + mutex_lock(&acpi_ioremap_lock); + map = acpi_map_lookup(addr->address, addr->bit_width / 8); + if (!map) { + mutex_unlock(&acpi_ioremap_lock); + return; + } + acpi_os_drop_map_ref(map); + mutex_unlock(&acpi_ioremap_lock); - acpi_os_unmap_memory(virt, size); + acpi_os_map_cleanup(map); } -EXPORT_SYMBOL_GPL(acpi_os_unmap_generic_address); #ifdef ACPI_FUTURE_USAGE acpi_status @@ -516,11 +531,15 @@ acpi_os_install_interrupt_handler(u32 gsi, acpi_osd_handler handler, acpi_irq_stats_init(); /* - * Ignore the GSI from the core, and use the value in our copy of the - * FADT. It may not be the same if an interrupt source override exists - * for the SCI. + * ACPI interrupts different from the SCI in our copy of the FADT are + * not supported. */ - gsi = acpi_gbl_FADT.sci_interrupt; + if (gsi != acpi_gbl_FADT.sci_interrupt) + return AE_BAD_PARAMETER; + + if (acpi_irq_handler) + return AE_ALREADY_ACQUIRED; + if (acpi_gsi_to_irq(gsi, &irq) < 0) { printk(KERN_ERR PREFIX "SCI (ACPI GSI %d) not registered\n", gsi); @@ -531,20 +550,20 @@ acpi_os_install_interrupt_handler(u32 gsi, acpi_osd_handler handler, acpi_irq_context = context; if (request_irq(irq, acpi_irq, IRQF_SHARED, "acpi", acpi_irq)) { printk(KERN_ERR PREFIX "SCI (IRQ%d) allocation failed\n", irq); + acpi_irq_handler = NULL; return AE_NOT_ACQUIRED; } - acpi_irq_irq = irq; return AE_OK; } acpi_status acpi_os_remove_interrupt_handler(u32 irq, acpi_osd_handler handler) { - if (irq) { - free_irq(irq, acpi_irq); - acpi_irq_handler = NULL; - acpi_irq_irq = 0; - } + if (irq != acpi_gbl_FADT.sci_interrupt) + return AE_BAD_PARAMETER; + + free_irq(irq, acpi_irq); + acpi_irq_handler = NULL; return AE_OK; } @@ -1603,7 +1622,7 @@ acpi_status __init acpi_os_initialize1(void) acpi_status acpi_os_terminate(void) { if (acpi_irq_handler) { - acpi_os_remove_interrupt_handler(acpi_irq_irq, + acpi_os_remove_interrupt_handler(acpi_gbl_FADT.sci_interrupt, acpi_irq_handler); } diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index b99e6249460..b136c9c1e53 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -797,7 +797,6 @@ static void acpi_bus_set_run_wake_flags(struct acpi_device *device) acpi_status status; acpi_event_status event_status; - device->wakeup.run_wake_count = 0; device->wakeup.flags.notifier_present = 0; /* Power button, Lid switch always enable wakeup */ diff --git a/drivers/acpi/sleep.c b/drivers/acpi/sleep.c index d6a8cd14de2..84f57143ad7 100644 --- a/drivers/acpi/sleep.c +++ b/drivers/acpi/sleep.c @@ -199,8 +199,6 @@ static void acpi_pm_end(void) #endif /* CONFIG_ACPI_SLEEP */ #ifdef CONFIG_SUSPEND -extern void do_suspend_lowlevel(void); - static u32 acpi_suspend_states[] = { [PM_SUSPEND_ON] = ACPI_STATE_S0, [PM_SUSPEND_STANDBY] = ACPI_STATE_S1, @@ -243,20 +241,11 @@ static int acpi_suspend_begin(suspend_state_t pm_state) static int acpi_suspend_enter(suspend_state_t pm_state) { acpi_status status = AE_OK; - unsigned long flags = 0; u32 acpi_state = acpi_target_sleep_state; + int error; ACPI_FLUSH_CPU_CACHE(); - /* Do arch specific saving of state. */ - if (acpi_state == ACPI_STATE_S3) { - int error = acpi_save_state_mem(); - - if (error) - return error; - } - - local_irq_save(flags); switch (acpi_state) { case ACPI_STATE_S1: barrier(); @@ -264,7 +253,10 @@ static int acpi_suspend_enter(suspend_state_t pm_state) break; case ACPI_STATE_S3: - do_suspend_lowlevel(); + error = acpi_suspend_lowlevel(); + if (error) + return error; + pr_info(PREFIX "Low-level resume complete\n"); break; } @@ -290,13 +282,6 @@ static int acpi_suspend_enter(suspend_state_t pm_state) /* Allow EC transactions to happen. */ acpi_ec_unblock_transactions_early(); - local_irq_restore(flags); - printk(KERN_DEBUG "Back to C!\n"); - - /* restore processor state */ - if (acpi_state == ACPI_STATE_S3) - acpi_restore_state_mem(); - suspend_nvs_restore(); return ACPI_SUCCESS(status) ? 0 : -EFAULT; @@ -472,16 +457,13 @@ static int acpi_hibernation_begin(void) static int acpi_hibernation_enter(void) { acpi_status status = AE_OK; - unsigned long flags = 0; ACPI_FLUSH_CPU_CACHE(); - local_irq_save(flags); /* This shouldn't return. If it returns, we have a problem */ status = acpi_enter_sleep_state(ACPI_STATE_S4); /* Reprogram control registers and execute _BFS */ acpi_leave_sleep_state_prep(ACPI_STATE_S4); - local_irq_restore(flags); return ACPI_SUCCESS(status) ? 0 : -EFAULT; } |