diff options
Diffstat (limited to 'drivers/acpi')
-rw-r--r-- | drivers/acpi/Kconfig | 17 | ||||
-rw-r--r-- | drivers/acpi/Makefile | 1 | ||||
-rw-r--r-- | drivers/acpi/acpi_configfs.c | 267 | ||||
-rw-r--r-- | drivers/acpi/acpi_dbg.c | 4 | ||||
-rw-r--r-- | drivers/acpi/acpi_video.c | 3 | ||||
-rw-r--r-- | drivers/acpi/bus.c | 85 | ||||
-rw-r--r-- | drivers/acpi/button.c | 149 | ||||
-rw-r--r-- | drivers/acpi/ec.c | 121 | ||||
-rw-r--r-- | drivers/acpi/internal.h | 3 | ||||
-rw-r--r-- | drivers/acpi/nfit.c | 11 | ||||
-rw-r--r-- | drivers/acpi/nfit.h | 10 | ||||
-rw-r--r-- | drivers/acpi/numa.c | 226 | ||||
-rw-r--r-- | drivers/acpi/pci_link.c | 65 | ||||
-rw-r--r-- | drivers/acpi/pci_slot.c | 43 | ||||
-rw-r--r-- | drivers/acpi/processor_core.c | 26 | ||||
-rw-r--r-- | drivers/acpi/scan.c | 81 | ||||
-rw-r--r-- | drivers/acpi/sysfs.c | 6 | ||||
-rw-r--r-- | drivers/acpi/tables.c | 23 | ||||
-rw-r--r-- | drivers/acpi/video_detect.c | 8 |
19 files changed, 894 insertions, 255 deletions
diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index b7e2e776397d..5f6c76b79313 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig @@ -291,8 +291,8 @@ config ACPI_THERMAL config ACPI_NUMA bool "NUMA support" depends on NUMA - depends on (X86 || IA64) - default y if IA64_GENERIC || IA64_SGI_SN2 + depends on (X86 || IA64 || ARM64) + default y if IA64_GENERIC || IA64_SGI_SN2 || ARM64 config ACPI_CUSTOM_DSDT_FILE string "Custom DSDT Table file to include" @@ -311,9 +311,12 @@ config ACPI_CUSTOM_DSDT bool default ACPI_CUSTOM_DSDT_FILE != "" +config ARCH_HAS_ACPI_TABLE_UPGRADE + def_bool n + config ACPI_TABLE_UPGRADE bool "Allow upgrading ACPI tables via initrd" - depends on BLK_DEV_INITRD && X86 + depends on BLK_DEV_INITRD && ARCH_HAS_ACPI_TABLE_UPGRADE default y help This option provides functionality to upgrade arbitrary ACPI tables @@ -521,4 +524,12 @@ config XPOWER_PMIC_OPREGION endif +config ACPI_CONFIGFS + tristate "ACPI configfs support" + select CONFIGFS_FS + help + Select this option to enable support for ACPI configuration from + userspace. The configurable ACPI groups will be visible under + /config/acpi, assuming configfs is mounted under /config. + endif # ACPI diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile index 251ce85a66fb..f72a83df1cf3 100644 --- a/drivers/acpi/Makefile +++ b/drivers/acpi/Makefile @@ -99,5 +99,6 @@ obj-$(CONFIG_ACPI_EXTLOG) += acpi_extlog.o obj-$(CONFIG_PMIC_OPREGION) += pmic/intel_pmic.o obj-$(CONFIG_CRC_PMIC_OPREGION) += pmic/intel_pmic_crc.o obj-$(CONFIG_XPOWER_PMIC_OPREGION) += pmic/intel_pmic_xpower.o +obj-$(CONFIG_ACPI_CONFIGFS) += acpi_configfs.o video-objs += acpi_video.o video_detect.o diff --git a/drivers/acpi/acpi_configfs.c b/drivers/acpi/acpi_configfs.c new file mode 100644 index 000000000000..146a77fb762d --- /dev/null +++ b/drivers/acpi/acpi_configfs.c @@ -0,0 +1,267 @@ +/* + * ACPI configfs support + * + * Copyright (c) 2016 Intel Corporation + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + */ + +#define pr_fmt(fmt) "ACPI configfs: " fmt + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/configfs.h> +#include <linux/acpi.h> + +static struct config_group *acpi_table_group; + +struct acpi_table { + struct config_item cfg; + struct acpi_table_header *header; +}; + +static ssize_t acpi_table_aml_write(struct config_item *cfg, + const void *data, size_t size) +{ + const struct acpi_table_header *header = data; + struct acpi_table *table; + int ret; + + table = container_of(cfg, struct acpi_table, cfg); + + if (table->header) { + pr_err("table already loaded\n"); + return -EBUSY; + } + + if (header->length != size) { + pr_err("invalid table length\n"); + return -EINVAL; + } + + if (memcmp(header->signature, ACPI_SIG_SSDT, 4)) { + pr_err("invalid table signature\n"); + return -EINVAL; + } + + table = container_of(cfg, struct acpi_table, cfg); + + table->header = kmemdup(header, header->length, GFP_KERNEL); + if (!table->header) + return -ENOMEM; + + ret = acpi_load_table(table->header); + if (ret) { + kfree(table->header); + table->header = NULL; + } + + return ret; +} + +static inline struct acpi_table_header *get_header(struct config_item *cfg) +{ + struct acpi_table *table = container_of(cfg, struct acpi_table, cfg); + + if (!table->header) + pr_err("table not loaded\n"); + + return table->header; +} + +static ssize_t acpi_table_aml_read(struct config_item *cfg, + void *data, size_t size) +{ + struct acpi_table_header *h = get_header(cfg); + + if (!h) + return -EINVAL; + + if (data) + memcpy(data, h, h->length); + + return h->length; +} + +#define MAX_ACPI_TABLE_SIZE (128 * 1024) + +CONFIGFS_BIN_ATTR(acpi_table_, aml, NULL, MAX_ACPI_TABLE_SIZE); + +struct configfs_bin_attribute *acpi_table_bin_attrs[] = { + &acpi_table_attr_aml, + NULL, +}; + +ssize_t acpi_table_signature_show(struct config_item *cfg, char *str) +{ + struct acpi_table_header *h = get_header(cfg); + + if (!h) + return -EINVAL; + + return sprintf(str, "%.*s\n", ACPI_NAME_SIZE, h->signature); +} + +ssize_t acpi_table_length_show(struct config_item *cfg, char *str) +{ + struct acpi_table_header *h = get_header(cfg); + + if (!h) + return -EINVAL; + + return sprintf(str, "%d\n", h->length); +} + +ssize_t acpi_table_revision_show(struct config_item *cfg, char *str) +{ + struct acpi_table_header *h = get_header(cfg); + + if (!h) + return -EINVAL; + + return sprintf(str, "%d\n", h->revision); +} + +ssize_t acpi_table_oem_id_show(struct config_item *cfg, char *str) +{ + struct acpi_table_header *h = get_header(cfg); + + if (!h) + return -EINVAL; + + return sprintf(str, "%.*s\n", ACPI_OEM_ID_SIZE, h->oem_id); +} + +ssize_t acpi_table_oem_table_id_show(struct config_item *cfg, char *str) +{ + struct acpi_table_header *h = get_header(cfg); + + if (!h) + return -EINVAL; + + return sprintf(str, "%.*s\n", ACPI_OEM_TABLE_ID_SIZE, h->oem_table_id); +} + +ssize_t acpi_table_oem_revision_show(struct config_item *cfg, char *str) +{ + struct acpi_table_header *h = get_header(cfg); + + if (!h) + return -EINVAL; + + return sprintf(str, "%d\n", h->oem_revision); +} + +ssize_t acpi_table_asl_compiler_id_show(struct config_item *cfg, char *str) +{ + struct acpi_table_header *h = get_header(cfg); + + if (!h) + return -EINVAL; + + return sprintf(str, "%.*s\n", ACPI_NAME_SIZE, h->asl_compiler_id); +} + +ssize_t acpi_table_asl_compiler_revision_show(struct config_item *cfg, + char *str) +{ + struct acpi_table_header *h = get_header(cfg); + + if (!h) + return -EINVAL; + + return sprintf(str, "%d\n", h->asl_compiler_revision); +} + +CONFIGFS_ATTR_RO(acpi_table_, signature); +CONFIGFS_ATTR_RO(acpi_table_, length); +CONFIGFS_ATTR_RO(acpi_table_, revision); +CONFIGFS_ATTR_RO(acpi_table_, oem_id); +CONFIGFS_ATTR_RO(acpi_table_, oem_table_id); +CONFIGFS_ATTR_RO(acpi_table_, oem_revision); +CONFIGFS_ATTR_RO(acpi_table_, asl_compiler_id); +CONFIGFS_ATTR_RO(acpi_table_, asl_compiler_revision); + +struct configfs_attribute *acpi_table_attrs[] = { + &acpi_table_attr_signature, + &acpi_table_attr_length, + &acpi_table_attr_revision, + &acpi_table_attr_oem_id, + &acpi_table_attr_oem_table_id, + &acpi_table_attr_oem_revision, + &acpi_table_attr_asl_compiler_id, + &acpi_table_attr_asl_compiler_revision, + NULL, +}; + +static struct config_item_type acpi_table_type = { + .ct_owner = THIS_MODULE, + .ct_bin_attrs = acpi_table_bin_attrs, + .ct_attrs = acpi_table_attrs, +}; + +static struct config_item *acpi_table_make_item(struct config_group *group, + const char *name) +{ + struct acpi_table *table; + + table = kzalloc(sizeof(*table), GFP_KERNEL); + if (!table) + return ERR_PTR(-ENOMEM); + + config_item_init_type_name(&table->cfg, name, &acpi_table_type); + return &table->cfg; +} + +struct configfs_group_operations acpi_table_group_ops = { + .make_item = acpi_table_make_item, +}; + +static struct config_item_type acpi_tables_type = { + .ct_owner = THIS_MODULE, + .ct_group_ops = &acpi_table_group_ops, +}; + +static struct config_item_type acpi_root_group_type = { + .ct_owner = THIS_MODULE, +}; + +static struct configfs_subsystem acpi_configfs = { + .su_group = { + .cg_item = { + .ci_namebuf = "acpi", + .ci_type = &acpi_root_group_type, + }, + }, + .su_mutex = __MUTEX_INITIALIZER(acpi_configfs.su_mutex), +}; + +static int __init acpi_configfs_init(void) +{ + int ret; + struct config_group *root = &acpi_configfs.su_group; + + config_group_init(root); + + ret = configfs_register_subsystem(&acpi_configfs); + if (ret) + return ret; + + acpi_table_group = configfs_register_default_group(root, "table", + &acpi_tables_type); + return PTR_ERR_OR_ZERO(acpi_table_group); +} +module_init(acpi_configfs_init); + +static void __exit acpi_configfs_exit(void) +{ + configfs_unregister_default_group(acpi_table_group); + configfs_unregister_subsystem(&acpi_configfs); +} +module_exit(acpi_configfs_exit); + +MODULE_AUTHOR("Octavian Purdila <octavian.purdila@intel.com>"); +MODULE_DESCRIPTION("ACPI configfs support"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/acpi/acpi_dbg.c b/drivers/acpi/acpi_dbg.c index 1f4128487dd4..dee86925a9a1 100644 --- a/drivers/acpi/acpi_dbg.c +++ b/drivers/acpi/acpi_dbg.c @@ -602,7 +602,7 @@ static int acpi_aml_read_user(char __user *buf, int len) crc->tail = (crc->tail + n) & (ACPI_AML_BUF_SIZE - 1); ret = n; out: - acpi_aml_unlock_fifo(ACPI_AML_OUT_USER, !ret); + acpi_aml_unlock_fifo(ACPI_AML_OUT_USER, ret >= 0); return ret; } @@ -672,7 +672,7 @@ static int acpi_aml_write_user(const char __user *buf, int len) crc->head = (crc->head + n) & (ACPI_AML_BUF_SIZE - 1); ret = n; out: - acpi_aml_unlock_fifo(ACPI_AML_IN_USER, !ret); + acpi_aml_unlock_fifo(ACPI_AML_IN_USER, ret >= 0); return n; } diff --git a/drivers/acpi/acpi_video.c b/drivers/acpi/acpi_video.c index c1d138e128cb..c5557d070954 100644 --- a/drivers/acpi/acpi_video.c +++ b/drivers/acpi/acpi_video.c @@ -1246,6 +1246,9 @@ static int acpi_video_device_enumerate(struct acpi_video_bus *video) union acpi_object *dod = NULL; union acpi_object *obj; + if (!video->cap._DOD) + return AE_NOT_EXIST; + status = acpi_evaluate_object(video->device->handle, "_DOD", NULL, &buffer); if (!ACPI_SUCCESS(status)) { ACPI_EXCEPTION((AE_INFO, status, "Evaluating _DOD")); diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c index 262ca31b86d9..cb9558eb1e4f 100644 --- a/drivers/acpi/bus.c +++ b/drivers/acpi/bus.c @@ -30,6 +30,9 @@ #include <linux/acpi.h> #include <linux/slab.h> #include <linux/regulator/machine.h> +#include <linux/workqueue.h> +#include <linux/reboot.h> +#include <linux/delay.h> #ifdef CONFIG_X86 #include <asm/mpspec.h> #endif @@ -174,22 +177,17 @@ void acpi_bus_detach_private_data(acpi_handle handle) EXPORT_SYMBOL_GPL(acpi_bus_detach_private_data); static void acpi_print_osc_error(acpi_handle handle, - struct acpi_osc_context *context, char *error) + struct acpi_osc_context *context, char *error) { - struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER}; int i; - if (ACPI_FAILURE(acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer))) - printk(KERN_DEBUG "%s: %s\n", context->uuid_str, error); - else { - printk(KERN_DEBUG "%s (%s): %s\n", - (char *)buffer.pointer, context->uuid_str, error); - kfree(buffer.pointer); - } - printk(KERN_DEBUG "_OSC request data:"); + acpi_handle_debug(handle, "(%s): %s\n", context->uuid_str, error); + + pr_debug("_OSC request data:"); for (i = 0; i < context->cap.length; i += sizeof(u32)) - printk(" %x", *((u32 *)(context->cap.pointer + i))); - printk("\n"); + pr_debug(" %x", *((u32 *)(context->cap.pointer + i))); + + pr_debug("\n"); } acpi_status acpi_str_to_uuid(char *str, u8 *uuid) @@ -475,6 +473,56 @@ static void acpi_device_remove_notify_handler(struct acpi_device *device) acpi_device_notify); } +/* Handle events targeting \_SB device (at present only graceful shutdown) */ + +#define ACPI_SB_NOTIFY_SHUTDOWN_REQUEST 0x81 +#define ACPI_SB_INDICATE_INTERVAL 10000 + +static void sb_notify_work(struct work_struct *dummy) +{ + acpi_handle sb_handle; + + orderly_poweroff(true); + + /* + * After initiating graceful shutdown, the ACPI spec requires OSPM + * to evaluate _OST method once every 10seconds to indicate that + * the shutdown is in progress + */ + acpi_get_handle(NULL, "\\_SB", &sb_handle); + while (1) { + pr_info("Graceful shutdown in progress.\n"); + acpi_evaluate_ost(sb_handle, ACPI_OST_EC_OSPM_SHUTDOWN, + ACPI_OST_SC_OS_SHUTDOWN_IN_PROGRESS, NULL); + msleep(ACPI_SB_INDICATE_INTERVAL); + } +} + +static void acpi_sb_notify(acpi_handle handle, u32 event, void *data) +{ + static DECLARE_WORK(acpi_sb_work, sb_notify_work); + + if (event == ACPI_SB_NOTIFY_SHUTDOWN_REQUEST) { + if (!work_busy(&acpi_sb_work)) + schedule_work(&acpi_sb_work); + } else + pr_warn("event %x is not supported by \\_SB device\n", event); +} + +static int __init acpi_setup_sb_notify_handler(void) +{ + acpi_handle sb_handle; + + if (ACPI_FAILURE(acpi_get_handle(NULL, "\\_SB", &sb_handle))) + return -ENXIO; + + if (ACPI_FAILURE(acpi_install_notify_handler(sb_handle, ACPI_DEVICE_NOTIFY, + acpi_sb_notify, NULL))) + return -EINVAL; + + return 0; +} + /* -------------------------------------------------------------------------- Device Matching -------------------------------------------------------------------------- */ @@ -961,8 +1009,7 @@ void __init acpi_early_init(void) /** * acpi_subsystem_init - Finalize the early initialization of ACPI. * - * Switch over the platform to the ACPI mode (if possible), initialize the - * handling of ACPI events, install the interrupt and global lock handlers. + * Switch over the platform to the ACPI mode (if possible). * * Doing this too early is generally unsafe, but at the same time it needs to be * done before all things that really depend on ACPI. The right spot appears to @@ -990,6 +1037,13 @@ void __init acpi_subsystem_init(void) } } +static acpi_status acpi_bus_table_handler(u32 event, void *table, void *context) +{ + acpi_scan_table_handler(event, table, context); + + return acpi_sysfs_table_handler(event, table, context); +} + static int __init acpi_bus_init(void) { int result; @@ -1043,6 +1097,8 @@ static int __init acpi_bus_init(void) * _PDC control method may load dynamic SSDT tables, * and we need to install the table handler before that. */ + status = acpi_install_table_handler(acpi_bus_table_handler, NULL); + acpi_sysfs_init(); acpi_early_processor_set_pdc(); @@ -1124,6 +1180,7 @@ static int __init acpi_init(void) acpi_sleep_proc_init(); acpi_wakeup_device_init(); acpi_debugger_init(); + acpi_setup_sb_notify_handler(); return 0; } diff --git a/drivers/acpi/button.c b/drivers/acpi/button.c index 5c3b0918d5fd..148f4e5ca104 100644 --- a/drivers/acpi/button.c +++ b/drivers/acpi/button.c @@ -53,6 +53,10 @@ #define ACPI_BUTTON_DEVICE_NAME_LID "Lid Switch" #define ACPI_BUTTON_TYPE_LID 0x05 +#define ACPI_BUTTON_LID_INIT_IGNORE 0x00 +#define ACPI_BUTTON_LID_INIT_OPEN 0x01 +#define ACPI_BUTTON_LID_INIT_METHOD 0x02 + #define _COMPONENT ACPI_BUTTON_COMPONENT ACPI_MODULE_NAME("button"); @@ -105,6 +109,7 @@ struct acpi_button { static BLOCKING_NOTIFIER_HEAD(acpi_lid_notifier); static struct acpi_device *lid_device; +static u8 lid_init_state = ACPI_BUTTON_LID_INIT_METHOD; /* -------------------------------------------------------------------------- FS Interface (/proc) @@ -113,16 +118,52 @@ static struct acpi_device *lid_device; static struct proc_dir_entry *acpi_button_dir; static struct proc_dir_entry *acpi_lid_dir; +static int acpi_lid_evaluate_state(struct acpi_device *device) +{ + unsigned long long lid_state; + acpi_status status; + + status = acpi_evaluate_integer(device->handle, "_LID", NULL, &lid_state); + if (ACPI_FAILURE(status)) + return -ENODEV; + + return lid_state ? 1 : 0; +} + +static int acpi_lid_notify_state(struct acpi_device *device, int state) +{ + struct acpi_button *button = acpi_driver_data(device); + int ret; + + /* input layer checks if event is redundant */ + 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, + device); + if (ret == NOTIFY_DONE || ret == NOTIFY_OK) { + /* + * It is also regarded as success if the notifier_chain + * returns NOTIFY_OK or NOTIFY_DONE. + */ + ret = 0; + } + return ret; +} + static int acpi_button_state_seq_show(struct seq_file *seq, void *offset) { struct acpi_device *device = seq->private; - acpi_status status; - unsigned long long state; + int state; - status = acpi_evaluate_integer(device->handle, "_LID", NULL, &state); + state = acpi_lid_evaluate_state(device); seq_printf(seq, "state: %s\n", - ACPI_FAILURE(status) ? "unsupported" : - (state ? "open" : "closed")); + state < 0 ? "unsupported" : (state ? "open" : "closed")); return 0; } @@ -231,51 +272,37 @@ EXPORT_SYMBOL(acpi_lid_notifier_unregister); int acpi_lid_open(void) { - acpi_status status; - unsigned long long state; - if (!lid_device) return -ENODEV; - status = acpi_evaluate_integer(lid_device->handle, "_LID", NULL, - &state); - if (ACPI_FAILURE(status)) - return -ENODEV; - - return !!state; + return acpi_lid_evaluate_state(lid_device); } EXPORT_SYMBOL(acpi_lid_open); -static int acpi_lid_send_state(struct acpi_device *device) +static int acpi_lid_update_state(struct acpi_device *device) { - struct acpi_button *button = acpi_driver_data(device); - unsigned long long state; - acpi_status status; - int ret; - - status = acpi_evaluate_integer(device->handle, "_LID", NULL, &state); - if (ACPI_FAILURE(status)) - return -ENODEV; + int state; - /* input layer checks if event is redundant */ - input_report_switch(button->input, SW_LID, !state); - input_sync(button->input); + state = acpi_lid_evaluate_state(device); + if (state < 0) + return state; - if (state) - pm_wakeup_event(&device->dev, 0); + return acpi_lid_notify_state(device, state); +} - ret = blocking_notifier_call_chain(&acpi_lid_notifier, state, device); - if (ret == NOTIFY_DONE) - ret = blocking_notifier_call_chain(&acpi_lid_notifier, state, - device); - if (ret == NOTIFY_DONE || ret == NOTIFY_OK) { - /* - * It is also regarded as success if the notifier_chain - * returns NOTIFY_OK or NOTIFY_DONE. - */ - ret = 0; +static void acpi_lid_initialize_state(struct acpi_device *device) +{ + switch (lid_init_state) { + case ACPI_BUTTON_LID_INIT_OPEN: + (void)acpi_lid_notify_state(device, 1); + break; + case ACPI_BUTTON_LID_INIT_METHOD: + (void)acpi_lid_update_state(device); + break; + case ACPI_BUTTON_LID_INIT_IGNORE: + default: + break; } - return ret; } static void acpi_button_notify(struct acpi_device *device, u32 event) @@ -290,7 +317,7 @@ static void acpi_button_notify(struct acpi_device *device, u32 event) case ACPI_BUTTON_NOTIFY_STATUS: input = button->input; if (button->type == ACPI_BUTTON_TYPE_LID) { - acpi_lid_send_state(device); + acpi_lid_update_state(device); } else { int keycode; @@ -335,7 +362,7 @@ static int acpi_button_resume(struct device *dev) button->suspended = false; if (button->type == ACPI_BUTTON_TYPE_LID) - return acpi_lid_send_state(device); + acpi_lid_initialize_state(device); return 0; } #endif @@ -416,7 +443,7 @@ static int acpi_button_add(struct acpi_device *device) if (error) goto err_remove_fs; if (button->type == ACPI_BUTTON_TYPE_LID) { - acpi_lid_send_state(device); + acpi_lid_initialize_state(device); /* * This assumes there's only one lid device, or if there are * more we only care about the last one... @@ -446,4 +473,42 @@ static int acpi_button_remove(struct acpi_device *device) return 0; } +static int param_set_lid_init_state(const char *val, struct kernel_param *kp) +{ + int result = 0; + + if (!strncmp(val, "open", sizeof("open") - 1)) { + lid_init_state = ACPI_BUTTON_LID_INIT_OPEN; + pr_info("Notify initial lid state as open\n"); + } else if (!strncmp(val, "method", sizeof("method") - 1)) { + lid_init_state = ACPI_BUTTON_LID_INIT_METHOD; + pr_info("Notify initial lid state with _LID return value\n"); + } else if (!strncmp(val, "ignore", sizeof("ignore") - 1)) { + lid_init_state = ACPI_BUTTON_LID_INIT_IGNORE; + pr_info("Do not notify initial lid state\n"); + } else + result = -EINVAL; + return result; +} + +static int param_get_lid_init_state(char *buffer, struct kernel_param *kp) +{ + switch (lid_init_state) { + case ACPI_BUTTON_LID_INIT_OPEN: + return sprintf(buffer, "open"); + case ACPI_BUTTON_LID_INIT_METHOD: + return sprintf(buffer, "method"); + case ACPI_BUTTON_LID_INIT_IGNORE: + return sprintf(buffer, "ignore"); + default: + return sprintf(buffer, "invalid"); + } + return 0; +} + +module_param_call(lid_init_state, + param_set_lid_init_state, param_get_lid_init_state, + NULL, 0644); +MODULE_PARM_DESC(lid_init_state, "Behavior for reporting LID initial state"); + module_acpi_driver(acpi_button_driver); diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index 73c76d646064..999a10914678 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -1331,8 +1331,6 @@ static int ec_install_handlers(struct acpi_ec *ec) static void ec_remove_handlers(struct acpi_ec *ec) { - acpi_ec_stop(ec, false); - if (test_bit(EC_FLAGS_EC_HANDLER_INSTALLED, &ec->flags)) { if (ACPI_FAILURE(acpi_remove_address_space_handler(ec->handle, ACPI_ADR_SPACE_EC, &acpi_ec_space_handler))) @@ -1340,6 +1338,19 @@ static void ec_remove_handlers(struct acpi_ec *ec) clear_bit(EC_FLAGS_EC_HANDLER_INSTALLED, &ec->flags); } + /* + * Stops handling the EC transactions after removing the operation + * region handler. This is required because _REG(DISCONNECT) + * invoked during the removal can result in new EC transactions. + * + * Flushes the EC requests and thus disables the GPE before + * removing the GPE handler. This is required by the current ACPICA + * GPE core. ACPICA GPE core will automatically disable a GPE when + * it is indicated but there is no way to handle it. So the drivers + * must disable the GPEs prior to removing the GPE handlers. + */ + acpi_ec_stop(ec, false); + if (test_bit(EC_FLAGS_GPE_HANDLER_INSTALLED, &ec->flags)) { if (ACPI_FAILURE(acpi_remove_gpe_handler(NULL, ec->gpe, &acpi_ec_gpe_handler))) @@ -1348,13 +1359,9 @@ static void ec_remove_handlers(struct acpi_ec *ec) } } -static int acpi_ec_add(struct acpi_device *device) +static struct acpi_ec *acpi_ec_alloc(void) { - struct acpi_ec *ec = NULL; - int ret; - - strcpy(acpi_device_name(device), ACPI_EC_DEVICE_NAME); - strcpy(acpi_device_class(device), ACPI_EC_CLASS); + struct acpi_ec *ec; /* Check for boot EC */ if (boot_ec) { @@ -1365,9 +1372,21 @@ static int acpi_ec_add(struct acpi_device *device) first_ec = NULL; } else { ec = make_acpi_ec(); - if (!ec) - return -ENOMEM; } + return ec; +} + +static int acpi_ec_add(struct acpi_device *device) +{ + struct acpi_ec *ec = NULL; + int ret; + + strcpy(acpi_device_name(device), ACPI_EC_DEVICE_NAME); + strcpy(acpi_device_class(device), ACPI_EC_CLASS); + + ec = acpi_ec_alloc(); + if (!ec) + return -ENOMEM; if (ec_parse_device(device->handle, 0, ec, NULL) != AE_CTRL_TERMINATE) { kfree(ec); @@ -1454,27 +1473,31 @@ static const struct acpi_device_id ec_device_ids[] = { int __init acpi_ec_dsdt_probe(void) { acpi_status status; + struct acpi_ec *ec; + int ret; - if (boot_ec) - return 0; - + ec = acpi_ec_alloc(); + if (!ec) + return -ENOMEM; /* * Finding EC from DSDT if there is no ECDT EC available. When this * function is invoked, ACPI tables have been fully loaded, we can * walk namespace now. */ - boot_ec = make_acpi_ec(); - if (!boot_ec) - return -ENOMEM; status = acpi_get_devices(ec_device_ids[0].id, - ec_parse_device, boot_ec, NULL); - if (ACPI_FAILURE(status) || !boot_ec->handle) - return -ENODEV; - if (!ec_install_handlers(boot_ec)) { - first_ec = boot_ec; - return 0; + ec_parse_device, ec, NULL); + if (ACPI_FAILURE(status) || !ec->handle) { + ret = -ENODEV; + goto error; } - return -EFAULT; + ret = ec_install_handlers(ec); + +error: + if (ret) + kfree(ec); + else + first_ec = boot_ec = ec; + return ret; } #if 0 @@ -1518,6 +1541,11 @@ static int ec_clear_on_resume(const struct dmi_system_id *id) return 0; } +/* + * Some ECDTs contain wrong register addresses. + * MSI MS-171F + * https://bugzilla.kernel.org/show_bug.cgi?id=12461 + */ static int ec_correct_ecdt(const struct dmi_system_id *id) { pr_debug("Detected system needing ECDT address correction.\n"); @@ -1527,16 +1555,6 @@ static int ec_correct_ecdt(const struct dmi_system_id *id) static struct dmi_system_id ec_dmi_table[] __initdata = { { - ec_correct_ecdt, "Asus L4R", { - DMI_MATCH(DMI_BIOS_VERSION, "1008.006"), - DMI_MATCH(DMI_PRODUCT_NAME, "L4R"), - DMI_MATCH(DMI_BOARD_NAME, "L4R") }, NULL}, - { - ec_correct_ecdt, "Asus M6R", { - DMI_MATCH(DMI_BIOS_VERSION, "0207"), - DMI_MATCH(DMI_PRODUCT_NAME, "M6R"), - DMI_MATCH(DMI_BOARD_NAME, "M6R") }, NULL}, - { ec_correct_ecdt, "MSI MS-171F", { DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star"), DMI_MATCH(DMI_PRODUCT_NAME, "MS-171F"),}, NULL}, @@ -1548,12 +1566,13 @@ static struct dmi_system_id ec_dmi_table[] __initdata = { int __init acpi_ec_ecdt_probe(void) { - int ret = 0; + int ret; acpi_status status; struct acpi_table_ecdt *ecdt_ptr; + struct acpi_ec *ec; - boot_ec = make_acpi_ec(); - if (!boot_ec) + ec = acpi_ec_alloc(); + if (!ec) return -ENOMEM; /* * Generate a boot ec context @@ -1577,28 +1596,20 @@ int __init acpi_ec_ecdt_probe(void) pr_info("EC description table is found, configuring boot EC\n"); if (EC_FLAGS_CORRECT_ECDT) { - /* - * Asus L4R, Asus M6R - * https://bugzilla.kernel.org/show_bug.cgi?id=9399 - * MSI MS-171F - * https://bugzilla.kernel.org/show_bug.cgi?id=12461 - */ - boot_ec->command_addr = ecdt_ptr->data.address; - boot_ec->data_addr = ecdt_ptr->control.address; + ec->command_addr = ecdt_ptr->data.address; + ec->data_addr = ecdt_ptr->control.address; } else { - boot_ec->command_addr = ecdt_ptr->control.address; - boot_ec->data_addr = ecdt_ptr->data.address; + ec->command_addr = ecdt_ptr->control.address; + ec->data_addr = ecdt_ptr->data.address; } - boot_ec->gpe = ecdt_ptr->gpe; - boot_ec->handle = ACPI_ROOT_OBJECT; - ret = ec_install_handlers(boot_ec); - if (!ret) - first_ec = boot_ec; + ec->gpe = ecdt_ptr->gpe; + ec->handle = ACPI_ROOT_OBJECT; + ret = ec_install_handlers(ec); error: - if (ret) { - kfree(boot_ec); - boot_ec = NULL; - } + if (ret) + kfree(ec); + else + first_ec = boot_ec = ec; return ret; } diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h index 27cc7feabfe4..940218ff0193 100644 --- a/drivers/acpi/internal.h +++ b/drivers/acpi/internal.h @@ -87,6 +87,9 @@ bool acpi_queue_hotplug_work(struct work_struct *work); void acpi_device_hotplug(struct acpi_device *adev, u32 src); bool acpi_scan_is_offline(struct acpi_device *adev, bool uevent); +acpi_status acpi_sysfs_table_handler(u32 event, void *table, void *context); +void acpi_scan_table_handler(u32 event, void *table, void *context); + /* -------------------------------------------------------------------------- Device Node Initialization / Removal -------------------------------------------------------------------------- */ diff --git a/drivers/acpi/nfit.c b/drivers/acpi/nfit.c index 2215fc847fa9..1f0e06065ae6 100644 --- a/drivers/acpi/nfit.c +++ b/drivers/acpi/nfit.c @@ -928,7 +928,7 @@ static ssize_t format_show(struct device *dev, { struct acpi_nfit_control_region *dcr = to_nfit_dcr(dev); - return sprintf(buf, "0x%04x\n", be16_to_cpu(dcr->code)); + return sprintf(buf, "0x%04x\n", le16_to_cpu(dcr->code)); } static DEVICE_ATTR_RO(format); @@ -961,8 +961,8 @@ static ssize_t format1_show(struct device *dev, continue; if (nfit_dcr->dcr->code == dcr->code) continue; - rc = sprintf(buf, "%#x\n", - be16_to_cpu(nfit_dcr->dcr->code)); + rc = sprintf(buf, "0x%04x\n", + le16_to_cpu(nfit_dcr->dcr->code)); break; } if (rc != ENXIO) @@ -1151,9 +1151,10 @@ static int acpi_nfit_add_dimm(struct acpi_nfit_desc *acpi_desc, if (disable_vendor_specific) dsm_mask &= ~(1 << 8); } else { - dev_err(dev, "unknown dimm command family\n"); + dev_dbg(dev, "unknown dimm command family\n"); nfit_mem->family = -1; - return force_enable_dimms ? 0 : -ENODEV; + /* DSMs are optional, continue loading the driver... */ + return 0; } uuid = to_nfit_uuid(nfit_mem->family); diff --git a/drivers/acpi/nfit.h b/drivers/acpi/nfit.h index 11cb38348aef..02b9ea1e8d2e 100644 --- a/drivers/acpi/nfit.h +++ b/drivers/acpi/nfit.h @@ -53,12 +53,12 @@ enum nfit_uuids { }; /* - * Region format interface codes are stored as an array of bytes in the - * NFIT DIMM Control Region structure + * Region format interface codes are stored with the interface as the + * LSB and the function as the MSB. */ -#define NFIT_FIC_BYTE cpu_to_be16(0x101) /* byte-addressable energy backed */ -#define NFIT_FIC_BLK cpu_to_be16(0x201) /* block-addressable non-energy backed */ -#define NFIT_FIC_BYTEN cpu_to_be16(0x301) /* byte-addressable non-energy backed */ +#define NFIT_FIC_BYTE cpu_to_le16(0x101) /* byte-addressable energy backed */ +#define NFIT_FIC_BLK cpu_to_le16(0x201) /* block-addressable non-energy backed */ +#define NFIT_FIC_BYTEN cpu_to_le16(0x301) /* byte-addressable non-energy backed */ enum { NFIT_BLK_READ_FLUSH = 1, diff --git a/drivers/acpi/numa.c b/drivers/acpi/numa.c index d176e0ece470..ce3a7a16f03f 100644 --- a/drivers/acpi/numa.c +++ b/drivers/acpi/numa.c @@ -18,22 +18,21 @@ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * */ + +#define pr_fmt(fmt) "ACPI: " fmt + #include <linux/module.h> #include <linux/init.h> #include <linux/kernel.h> #include <linux/types.h> #include <linux/errno.h> #include <linux/acpi.h> +#include <linux/bootmem.h> +#include <linux/memblock.h> #include <linux/numa.h> #include <linux/nodemask.h> #include <linux/topology.h> -#define PREFIX "ACPI: " - -#define ACPI_NUMA 0x80000000 -#define _COMPONENT ACPI_NUMA -ACPI_MODULE_NAME("numa"); - static nodemask_t nodes_found_map = NODE_MASK_NONE; /* maps to convert between proximity domain and logical node ID */ @@ -43,6 +42,7 @@ static int node_to_pxm_map[MAX_NUMNODES] = { [0 ... MAX_NUMNODES - 1] = PXM_INVAL }; unsigned char acpi_srat_revision __initdata; +int acpi_numa __initdata; int pxm_to_node(int pxm) { @@ -128,68 +128,63 @@ EXPORT_SYMBOL(acpi_map_pxm_to_online_node); static void __init acpi_table_print_srat_entry(struct acpi_subtable_header *header) { - - ACPI_FUNCTION_NAME("acpi_table_print_srat_entry"); - - if (!header) - return; - switch (header->type) { - case ACPI_SRAT_TYPE_CPU_AFFINITY: -#ifdef ACPI_DEBUG_OUTPUT { struct acpi_srat_cpu_affinity *p = (struct acpi_srat_cpu_affinity *)header; - ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "SRAT Processor (id[0x%02x] eid[0x%02x]) in proximity domain %d %s\n", - p->apic_id, p->local_sapic_eid, - p->proximity_domain_lo, - (p->flags & ACPI_SRAT_CPU_ENABLED)? - "enabled" : "disabled")); + pr_debug("SRAT Processor (id[0x%02x] eid[0x%02x]) in proximity domain %d %s\n", + p->apic_id, p->local_sapic_eid, + p->proximity_domain_lo, + (p->flags & ACPI_SRAT_CPU_ENABLED) ? + "enabled" : "disabled"); } -#endif /* ACPI_DEBUG_OUTPUT */ break; case ACPI_SRAT_TYPE_MEMORY_AFFINITY: -#ifdef ACPI_DEBUG_OUTPUT { struct acpi_srat_mem_affinity *p = (struct acpi_srat_mem_affinity *)header; - ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "SRAT Memory (0x%lx length 0x%lx) in proximity domain %d %s%s%s\n", - (unsigned long)p->base_address, - (unsigned long)p->length, - p->proximity_domain, - (p->flags & ACPI_SRAT_MEM_ENABLED)? - "enabled" : "disabled", - (p->flags & ACPI_SRAT_MEM_HOT_PLUGGABLE)? - " hot-pluggable" : "", - (p->flags & ACPI_SRAT_MEM_NON_VOLATILE)? - " non-volatile" : "")); + pr_debug("SRAT Memory (0x%lx length 0x%lx) in proximity domain %d %s%s%s\n", + (unsigned long)p->base_address, + (unsigned long)p->length, + p->proximity_domain, + (p->flags & ACPI_SRAT_MEM_ENABLED) ? + "enabled" : "disabled", + (p->flags & ACPI_SRAT_MEM_HOT_PLUGGABLE) ? + " hot-pluggable" : "", + (p->flags & ACPI_SRAT_MEM_NON_VOLATILE) ? + " non-volatile" : ""); } -#endif /* ACPI_DEBUG_OUTPUT */ break; case ACPI_SRAT_TYPE_X2APIC_CPU_AFFINITY: -#ifdef ACPI_DEBUG_OUTPUT { struct acpi_srat_x2apic_cpu_affinity *p = (struct acpi_srat_x2apic_cpu_affinity *)header; - ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "SRAT Processor (x2apicid[0x%08x]) in" - " proximity domain %d %s\n", - p->apic_id, - p->proximity_domain, - (p->flags & ACPI_SRAT_CPU_ENABLED) ? - "enabled" : "disabled")); + pr_debug("SRAT Processor (x2apicid[0x%08x]) in proximity domain %d %s\n", + p->apic_id, + p->proximity_domain, + (p->flags & ACPI_SRAT_CPU_ENABLED) ? + "enabled" : "disabled"); } -#endif /* ACPI_DEBUG_OUTPUT */ break; + + case ACPI_SRAT_TYPE_GICC_AFFINITY: + { + struct acpi_srat_gicc_affinity *p = + (struct acpi_srat_gicc_affinity *)header; + pr_debug("SRAT Processor (acpi id[0x%04x]) in proximity domain %d %s\n", + p->acpi_processor_uid, + p->proximity_domain, + (p->flags & ACPI_SRAT_GICC_ENABLED) ? + "enabled" : "disabled"); + } + break; + default: - printk(KERN_WARNING PREFIX - "Found unsupported SRAT entry (type = 0x%x)\n", - header->type); + pr_warn("Found unsupported SRAT entry (type = 0x%x)\n", + header->type); break; } } @@ -217,12 +212,117 @@ static int __init slit_valid(struct acpi_table_slit *slit) return 1; } +void __init bad_srat(void) +{ + pr_err("SRAT: SRAT not used.\n"); + acpi_numa = -1; +} + +int __init srat_disabled(void) +{ + return acpi_numa < 0; +} + +#if defined(CONFIG_X86) || defined(CONFIG_ARM64) +/* + * Callback for SLIT parsing. pxm_to_node() returns NUMA_NO_NODE for + * I/O localities since SRAT does not list them. I/O localities are + * not supported at this point. + */ +void __init acpi_numa_slit_init(struct acpi_table_slit *slit) +{ + int i, j; + + for (i = 0; i < slit->locality_count; i++) { + const int from_node = pxm_to_node(i); + + if (from_node == NUMA_NO_NODE) + continue; + + for (j = 0; j < slit->locality_count; j++) { + const int to_node = pxm_to_node(j); + + if (to_node == NUMA_NO_NODE) + continue; + + numa_set_distance(from_node, to_node, + slit->entry[slit->locality_count * i + j]); + } + } +} + +/* + * Default callback for parsing of the Proximity Domain <-> Memory + * Area mappings + */ +int __init +acpi_numa_memory_affinity_init(struct acpi_srat_mem_affinity *ma) +{ + u64 start, end; + u32 hotpluggable; + int node, pxm; + + if (srat_disabled()) + goto out_err; + if (ma->header.length < sizeof(struct acpi_srat_mem_affinity)) { + pr_err("SRAT: Unexpected header length: %d\n", + ma->header.length); + goto out_err_bad_srat; + } + if ((ma->flags & ACPI_SRAT_MEM_ENABLED) == 0) + goto out_err; + hotpluggable = ma->flags & ACPI_SRAT_MEM_HOT_PLUGGABLE; + if (hotpluggable && !IS_ENABLED(CONFIG_MEMORY_HOTPLUG)) + goto out_err; + + start = ma->base_address; + end = start + ma->length; + pxm = ma->proximity_domain; + if (acpi_srat_revision <= 1) + pxm &= 0xff; + + node = acpi_map_pxm_to_node(pxm); + if (node == NUMA_NO_NODE || node >= MAX_NUMNODES) { + pr_err("SRAT: Too many proximity domains.\n"); + goto out_err_bad_srat; + } + + if (numa_add_memblk(node, start, end) < 0) { + pr_err("SRAT: Failed to add memblk to node %u [mem %#010Lx-%#010Lx]\n", + node, (unsigned long long) start, + (unsigned long long) end - 1); + goto out_err_bad_srat; + } + + node_set(node, numa_nodes_parsed); + + pr_info("SRAT: Node %u PXM %u [mem %#010Lx-%#010Lx]%s%s\n", + node, pxm, + (unsigned long long) start, (unsigned long long) end - 1, + hotpluggable ? " hotplug" : "", + ma->flags & ACPI_SRAT_MEM_NON_VOLATILE ? " non-volatile" : ""); + + /* Mark hotplug range in memblock. */ + if (hotpluggable && memblock_mark_hotplug(start, ma->length)) + pr_warn("SRAT: Failed to mark hotplug range [mem %#010Lx-%#010Lx] in memblock\n", + (unsigned long long)start, (unsigned long long)end - 1); + + max_possible_pfn = max(max_possible_pfn, PFN_UP(end - 1)); + + return 0; +out_err_bad_srat: + bad_srat(); +out_err: + return -EINVAL; +} +#endif /* defined(CONFIG_X86) || defined (CONFIG_ARM64) */ + static int __init acpi_parse_slit(struct acpi_table_header *table) { struct acpi_table_slit *slit = (struct acpi_table_slit *)table; if (!slit_valid(slit)) { - printk(KERN_INFO "ACPI: SLIT table looks invalid. Not used.\n"); + pr_info("SLIT table looks invalid. Not used.\n"); return -EINVAL; } acpi_numa_slit_init(slit); @@ -233,12 +333,9 @@ static int __init acpi_parse_slit(struct acpi_table_header *table) void __init __weak acpi_numa_x2apic_affinity_init(struct acpi_srat_x2apic_cpu_affinity *pa) { - printk(KERN_WARNING PREFIX - "Found unsupported x2apic [0x%08x] SRAT entry\n", pa->apic_id); - return; + pr_warn("Found unsupported x2apic [0x%08x] SRAT entry\n", pa->apic_id); } - static int __init acpi_parse_x2apic_affinity(struct acpi_subtable_header *header, const unsigned long end) @@ -275,6 +372,24 @@ acpi_parse_processor_affinity(struct acpi_subtable_header *header, return 0; } +static int __init +acpi_parse_gicc_affinity(struct acpi_subtable_header *header, + const unsigned long end) +{ + struct acpi_srat_gicc_affinity *processor_affinity; + + processor_affinity = (struct acpi_srat_gicc_affinity *)header; + if (!processor_affinity) + return -EINVAL; + + acpi_table_print_srat_entry(header); + + /* let architecture-dependent part to do it */ + acpi_numa_gicc_affinity_init(processor_affinity); + + return 0; +} + static int __initdata parsed_numa_memblks; static int __init @@ -319,6 +434,9 @@ int __init acpi_numa_init(void) { int cnt = 0; + if (acpi_disabled) + return -EINVAL; + /* * Should not limit number with cpu num that is from NR_CPUS or nr_cpus= * SRAT cpu entries could have different order with that in MADT. @@ -327,13 +445,15 @@ int __init acpi_numa_init(void) /* SRAT: Static Resource Affinity Table */ if (!acpi_table_parse(ACPI_SIG_SRAT, acpi_parse_srat)) { - struct acpi_subtable_proc srat_proc[2]; + struct acpi_subtable_proc srat_proc[3]; memset(srat_proc, 0, sizeof(srat_proc)); srat_proc[0].id = ACPI_SRAT_TYPE_CPU_AFFINITY; srat_proc[0].handler = acpi_parse_processor_affinity; srat_proc[1].id = ACPI_SRAT_TYPE_X2APIC_CPU_AFFINITY; srat_proc[1].handler = acpi_parse_x2apic_affinity; + srat_proc[2].id = ACPI_SRAT_TYPE_GICC_AFFINITY; + srat_proc[2].handler = acpi_parse_gicc_affinity; acpi_table_parse_entries_array(ACPI_SIG_SRAT, sizeof(struct acpi_table_srat), @@ -347,8 +467,6 @@ int __init acpi_numa_init(void) /* SLIT: System Locality Information Table */ acpi_table_parse(ACPI_SIG_SLIT, acpi_parse_slit); - acpi_numa_arch_fixup(); - if (cnt < 0) return cnt; else if (!parsed_numa_memblks) diff --git a/drivers/acpi/pci_link.c b/drivers/acpi/pci_link.c index 8fc7323ed3e8..c983bf733ad3 100644 --- a/drivers/acpi/pci_link.c +++ b/drivers/acpi/pci_link.c @@ -470,6 +470,7 @@ static int acpi_irq_pci_sharing_penalty(int irq) { struct acpi_pci_link *link; int penalty = 0; + int i; list_for_each_entry(link, &acpi_link_list, list) { /* @@ -478,18 +479,14 @@ static int acpi_irq_pci_sharing_penalty(int irq) */ if (link->irq.active && link->irq.active == irq) penalty += PIRQ_PENALTY_PCI_USING; - else { - int i; - - /* - * If a link is inactive, penalize the IRQs it - * might use, but not as severely. - */ - for (i = 0; i < link->irq.possible_count; i++) - if (link->irq.possible[i] == irq) - penalty += PIRQ_PENALTY_PCI_POSSIBLE / - link->irq.possible_count; - } + + /* + * penalize the IRQs PCI might use, but not as severely. + */ + for (i = 0; i < link->irq.possible_count; i++) + if (link->irq.possible[i] == irq) + penalty += PIRQ_PENALTY_PCI_POSSIBLE / + link->irq.possible_count; } return penalty; @@ -499,9 +496,6 @@ static int acpi_irq_get_penalty(int irq) { int penalty = 0; - if (irq < ACPI_MAX_ISA_IRQS) - penalty += acpi_isa_irq_penalty[irq]; - /* * Penalize IRQ used by ACPI SCI. If ACPI SCI pin attributes conflict * with PCI IRQ attributes, mark ACPI SCI as ISA_ALWAYS so it won't be @@ -516,10 +510,49 @@ static int acpi_irq_get_penalty(int irq) penalty += PIRQ_PENALTY_PCI_USING; } + if (irq < ACPI_MAX_ISA_IRQS) + return penalty + acpi_isa_irq_penalty[irq]; + penalty += acpi_irq_pci_sharing_penalty(irq); return penalty; } +int __init acpi_irq_penalty_init(void) +{ + struct acpi_pci_link *link; + int i; + + /* + * Update penalties to facilitate IRQ balancing. + */ + list_for_each_entry(link, &acpi_link_list, list) { + + /* + * reflect the possible and active irqs in the penalty table -- + * useful for breaking ties. + */ + if (link->irq.possible_count) { + int penalty = + PIRQ_PENALTY_PCI_POSSIBLE / + link->irq.possible_count; + + for (i = 0; i < link->irq.possible_count; i++) { + if (link->irq.possible[i] < ACPI_MAX_ISA_IRQS) + acpi_isa_irq_penalty[link->irq. + possible[i]] += + penalty; + } + + } else if (link->irq.active && + (link->irq.active < ACPI_MAX_ISA_IRQS)) { + acpi_isa_irq_penalty[link->irq.active] += + PIRQ_PENALTY_PCI_POSSIBLE; + } + } + + return 0; +} + static int acpi_irq_balance = -1; /* 0: static, 1: balance */ static int acpi_pci_link_allocate(struct acpi_pci_link *link) @@ -839,7 +872,7 @@ void acpi_penalize_isa_irq(int irq, int active) { if ((irq >= 0) && (irq < ARRAY_SIZE(acpi_isa_irq_penalty))) acpi_isa_irq_penalty[irq] = acpi_irq_get_penalty(irq) + - active ? PIRQ_PENALTY_ISA_USED : PIRQ_PENALTY_PCI_USING; + (active ? PIRQ_PENALTY_ISA_USED : PIRQ_PENALTY_PCI_USING); } bool acpi_isa_irq_available(int irq) diff --git a/drivers/acpi/pci_slot.c b/drivers/acpi/pci_slot.c index 7188e53b6b7c..f62c68e24317 100644 --- a/drivers/acpi/pci_slot.c +++ b/drivers/acpi/pci_slot.c @@ -22,8 +22,9 @@ * General Public License for more details. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/kernel.h> -#include <linux/module.h> #include <linux/init.h> #include <linux/slab.h> #include <linux/types.h> @@ -33,30 +34,11 @@ #include <linux/dmi.h> #include <linux/pci-acpi.h> -static bool debug; static int check_sta_before_sun; -#define DRIVER_VERSION "0.1" -#define DRIVER_AUTHOR "Alex Chiang <achiang@hp.com>" -#define DRIVER_DESC "ACPI PCI Slot Detection Driver" -MODULE_AUTHOR(DRIVER_AUTHOR); -MODULE_DESCRIPTION(DRIVER_DESC); -MODULE_LICENSE("GPL"); -MODULE_PARM_DESC(debug, "Debugging mode enabled or not"); -module_param(debug, bool, 0644); - #define _COMPONENT ACPI_PCI_COMPONENT ACPI_MODULE_NAME("pci_slot"); -#define MY_NAME "pci_slot" -#define err(format, arg...) pr_err("%s: " format , MY_NAME , ## arg) -#define info(format, arg...) pr_info("%s: " format , MY_NAME , ## arg) -#define dbg(format, arg...) \ - do { \ - if (debug) \ - pr_debug("%s: " format, MY_NAME , ## arg); \ - } while (0) - #define SLOT_NAME_SIZE 21 /* Inspired by #define in acpiphp.h */ struct acpi_pci_slot { @@ -76,7 +58,7 @@ check_slot(acpi_handle handle, unsigned long long *sun) struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer); - dbg("Checking slot on path: %s\n", (char *)buffer.pointer); + pr_debug("Checking slot on path: %s\n", (char *)buffer.pointer); if (check_sta_before_sun) { /* If SxFy doesn't have _STA, we just assume it's there */ @@ -87,14 +69,16 @@ check_slot(acpi_handle handle, unsigned long long *sun) status = acpi_evaluate_integer(handle, "_ADR", NULL, &adr); if (ACPI_FAILURE(status)) { - dbg("_ADR returned %d on %s\n", status, (char *)buffer.pointer); + pr_debug("_ADR returned %d on %s\n", + status, (char *)buffer.pointer); goto out; } /* No _SUN == not a slot == bail */ status = acpi_evaluate_integer(handle, "_SUN", NULL, sun); if (ACPI_FAILURE(status)) { - dbg("_SUN returned %d on %s\n", status, (char *)buffer.pointer); + pr_debug("_SUN returned %d on %s\n", + status, (char *)buffer.pointer); goto out; } @@ -132,15 +116,13 @@ register_slot(acpi_handle handle, u32 lvl, void *context, void **rv) } slot = kmalloc(sizeof(*slot), GFP_KERNEL); - if (!slot) { - err("%s: cannot allocate memory\n", __func__); + if (!slot) return AE_OK; - } snprintf(name, sizeof(name), "%llu", sun); pci_slot = pci_create_slot(pci_bus, device, name, NULL); if (IS_ERR(pci_slot)) { - err("pci_create_slot returned %ld\n", PTR_ERR(pci_slot)); + pr_err("pci_create_slot returned %ld\n", PTR_ERR(pci_slot)); kfree(slot); return AE_OK; } @@ -150,8 +132,8 @@ register_slot(acpi_handle handle, u32 lvl, void *context, void **rv) get_device(&pci_bus->dev); - dbg("pci_slot: %p, pci_bus: %x, device: %d, name: %s\n", - pci_slot, pci_bus->number, device, name); + pr_debug("%p, pci_bus: %x, device: %d, name: %s\n", + pci_slot, pci_bus->number, device, name); return AE_OK; } @@ -186,7 +168,8 @@ void acpi_pci_slot_remove(struct pci_bus *bus) static int do_sta_before_sun(const struct dmi_system_id *d) { - info("%s detected: will evaluate _STA before calling _SUN\n", d->ident); + pr_info("%s detected: will evaluate _STA before calling _SUN\n", + d->ident); check_sta_before_sun = 1; return 0; } diff --git a/drivers/acpi/processor_core.c b/drivers/acpi/processor_core.c index 33a38d604630..9125d7d96372 100644 --- a/drivers/acpi/processor_core.c +++ b/drivers/acpi/processor_core.c @@ -108,13 +108,12 @@ static int map_gicc_mpidr(struct acpi_subtable_header *entry, return -EINVAL; } -static phys_cpuid_t map_madt_entry(int type, u32 acpi_id) +static phys_cpuid_t map_madt_entry(struct acpi_table_madt *madt, + int type, u32 acpi_id) { unsigned long madt_end, entry; phys_cpuid_t phys_id = PHYS_CPUID_INVALID; /* CPU hardware ID */ - struct acpi_table_madt *madt; - madt = get_madt_table(); if (!madt) return phys_id; @@ -145,6 +144,25 @@ static phys_cpuid_t map_madt_entry(int type, u32 acpi_id) return phys_id; } +phys_cpuid_t __init acpi_map_madt_entry(u32 acpi_id) +{ + struct acpi_table_madt *madt = NULL; + acpi_size tbl_size; + phys_cpuid_t rv; + + acpi_get_table_with_size(ACPI_SIG_MADT, 0, + (struct acpi_table_header **)&madt, + &tbl_size); + if (!madt) + return PHYS_CPUID_INVALID; + + rv = map_madt_entry(madt, 1, acpi_id); + + early_acpi_os_unmap_memory(madt, tbl_size); + + return rv; +} + static phys_cpuid_t map_mat_entry(acpi_handle handle, int type, u32 acpi_id) { struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; @@ -185,7 +203,7 @@ phys_cpuid_t acpi_get_phys_id(acpi_handle handle, int type, u32 acpi_id) phys_id = map_mat_entry(handle, type, acpi_id); if (invalid_phys_cpuid(phys_id)) - phys_id = map_madt_entry(type, acpi_id); + phys_id = map_madt_entry(get_madt_table(), type, acpi_id); return phys_id; } diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 5f28cf778349..405056b95b05 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -494,6 +494,8 @@ static void acpi_device_del(struct acpi_device *device) device_del(&device->dev); } +static BLOCKING_NOTIFIER_HEAD(acpi_reconfig_chain); + static LIST_HEAD(acpi_device_del_list); static DEFINE_MUTEX(acpi_device_del_lock); @@ -514,6 +516,9 @@ static void acpi_device_del_work_fn(struct work_struct *work_not_used) mutex_unlock(&acpi_device_del_lock); + blocking_notifier_call_chain(&acpi_reconfig_chain, + ACPI_RECONFIG_DEVICE_REMOVE, adev); + acpi_device_del(adev); /* * Drop references to all power resources that might have been @@ -1406,7 +1411,7 @@ void acpi_init_device_object(struct acpi_device *device, acpi_handle handle, acpi_bus_get_flags(device); device->flags.match_driver = false; device->flags.initialized = true; - device->flags.visited = false; + acpi_device_clear_enumerated(device); device_initialize(&device->dev); dev_set_uevent_suppress(&device->dev, true); acpi_init_coherency(device); @@ -1676,15 +1681,20 @@ static void acpi_default_enumeration(struct acpi_device *device) bool is_spi_i2c_slave = false; /* - * Do not enemerate SPI/I2C slaves as they will be enuerated by their + * Do not enumerate SPI/I2C slaves as they will be enumerated by their * respective parents. */ INIT_LIST_HEAD(&resource_list); acpi_dev_get_resources(device, &resource_list, acpi_check_spi_i2c_slave, &is_spi_i2c_slave); acpi_dev_free_resource_list(&resource_list); - if (!is_spi_i2c_slave) + if (!is_spi_i2c_slave) { acpi_create_platform_device(device); + acpi_device_set_enumerated(device); + } else { + blocking_notifier_call_chain(&acpi_reconfig_chain, + ACPI_RECONFIG_DEVICE_ADD, device); + } } static const struct acpi_device_id generic_device_ids[] = { @@ -1751,7 +1761,7 @@ static void acpi_bus_attach(struct acpi_device *device) acpi_bus_get_status(device); /* Skip devices that are not present. */ if (!acpi_device_is_present(device)) { - device->flags.visited = false; + acpi_device_clear_enumerated(device); device->flags.power_manageable = 0; return; } @@ -1766,7 +1776,7 @@ static void acpi_bus_attach(struct acpi_device *device) device->flags.initialized = true; } - device->flags.visited = false; + ret = acpi_scan_attach_handler(device); if (ret < 0) return; @@ -1780,7 +1790,6 @@ static void acpi_bus_attach(struct acpi_device *device) if (!ret && device->pnp.type.platform_id) acpi_default_enumeration(device); } - device->flags.visited = true; ok: list_for_each_entry(child, &device->children, node) @@ -1872,7 +1881,7 @@ void acpi_bus_trim(struct acpi_device *adev) */ acpi_device_set_power(adev, ACPI_STATE_D3_COLD); adev->flags.initialized = false; - adev->flags.visited = false; + acpi_device_clear_enumerated(adev); } EXPORT_SYMBOL_GPL(acpi_bus_trim); @@ -1916,6 +1925,8 @@ static int acpi_bus_scan_fixed(void) return result < 0 ? result : 0; } +static bool acpi_scan_initialized; + int __init acpi_scan_init(void) { int result; @@ -1960,6 +1971,8 @@ int __init acpi_scan_init(void) acpi_update_all_gpes(); + acpi_scan_initialized = true; + out: mutex_unlock(&acpi_scan_lock); return result; @@ -2003,3 +2016,57 @@ int __init __acpi_probe_device_table(struct acpi_probe_entry *ap_head, int nr) return count; } + +struct acpi_table_events_work { + struct work_struct work; + void *table; + u32 event; +}; + +static void acpi_table_events_fn(struct work_struct *work) +{ + struct acpi_table_events_work *tew; + + tew = container_of(work, struct acpi_table_events_work, work); + + if (tew->event == ACPI_TABLE_EVENT_LOAD) { + acpi_scan_lock_acquire(); + acpi_bus_scan(ACPI_ROOT_OBJECT); + acpi_scan_lock_release(); + } + + kfree(tew); +} + +void acpi_scan_table_handler(u32 event, void *table, void *context) +{ + struct acpi_table_events_work *tew; + + if (!acpi_scan_initialized) + return; + + if (event != ACPI_TABLE_EVENT_LOAD) + return; + + tew = kmalloc(sizeof(*tew), GFP_KERNEL); + if (!tew) + return; + + INIT_WORK(&tew->work, acpi_table_events_fn); + tew->table = table; + tew->event = event; + + schedule_work(&tew->work); +} + +int acpi_reconfig_notifier_register(struct notifier_block *nb) +{ + return blocking_notifier_chain_register(&acpi_reconfig_chain, nb); +} +EXPORT_SYMBOL(acpi_reconfig_notifier_register); + +int acpi_reconfig_notifier_unregister(struct notifier_block *nb) +{ + return blocking_notifier_chain_unregister(&acpi_reconfig_chain, nb); +} +EXPORT_SYMBOL(acpi_reconfig_notifier_unregister); diff --git a/drivers/acpi/sysfs.c b/drivers/acpi/sysfs.c index 4b3a9e27f1b6..358165e9f5b8 100644 --- a/drivers/acpi/sysfs.c +++ b/drivers/acpi/sysfs.c @@ -378,8 +378,7 @@ static void acpi_table_attr_init(struct acpi_table_attr *table_attr, return; } -static acpi_status -acpi_sysfs_table_handler(u32 event, void *table, void *context) +acpi_status acpi_sysfs_table_handler(u32 event, void *table, void *context) { struct acpi_table_attr *table_attr; @@ -452,9 +451,8 @@ static int acpi_tables_sysfs_init(void) kobject_uevent(tables_kobj, KOBJ_ADD); kobject_uevent(dynamic_tables_kobj, KOBJ_ADD); - status = acpi_install_table_handler(acpi_sysfs_table_handler, NULL); - return ACPI_FAILURE(status) ? -EINVAL : 0; + return 0; err_dynamic_tables: kobject_put(tables_kobj); err: diff --git a/drivers/acpi/tables.c b/drivers/acpi/tables.c index a372f9eaa15d..9f0ad6ebb368 100644 --- a/drivers/acpi/tables.c +++ b/drivers/acpi/tables.c @@ -34,6 +34,8 @@ #include <linux/bootmem.h> #include <linux/earlycpio.h> #include <linux/memblock.h> +#include <linux/initrd.h> +#include <linux/acpi.h> #include "internal.h" #ifdef CONFIG_ACPI_CUSTOM_DSDT @@ -481,8 +483,10 @@ static DECLARE_BITMAP(acpi_initrd_installed, NR_ACPI_INITRD_TABLES); #define MAP_CHUNK_SIZE (NR_FIX_BTMAPS << PAGE_SHIFT) -static void __init acpi_table_initrd_init(void *data, size_t size) +void __init acpi_table_upgrade(void) { + void *data = (void *)initrd_start; + size_t size = initrd_end - initrd_start; int sig, no, table_nr = 0, total_offset = 0; long offset = 0; struct acpi_table_header *table; @@ -540,7 +544,7 @@ static void __init acpi_table_initrd_init(void *data, size_t size) return; acpi_tables_addr = - memblock_find_in_range(0, max_low_pfn_mapped << PAGE_SHIFT, + memblock_find_in_range(0, ACPI_TABLE_UPGRADE_MAX_PHYS, all_tables_size, PAGE_SIZE); if (!acpi_tables_addr) { WARN_ON(1); @@ -578,10 +582,10 @@ static void __init acpi_table_initrd_init(void *data, size_t size) clen = size; if (clen > MAP_CHUNK_SIZE - slop) clen = MAP_CHUNK_SIZE - slop; - dest_p = early_ioremap(dest_addr & PAGE_MASK, - clen + slop); + dest_p = early_memremap(dest_addr & PAGE_MASK, + clen + slop); memcpy(dest_p + slop, src_p, clen); - early_iounmap(dest_p, clen + slop); + early_memunmap(dest_p, clen + slop); src_p += clen; dest_addr += clen; size -= clen; @@ -696,10 +700,6 @@ next_table: } } #else -static void __init acpi_table_initrd_init(void *data, size_t size) -{ -} - static acpi_status acpi_table_initrd_override(struct acpi_table_header *existing_table, acpi_physical_address *address, @@ -742,11 +742,6 @@ acpi_os_table_override(struct acpi_table_header *existing_table, return AE_OK; } -void __init early_acpi_table_init(void *data, size_t size) -{ - acpi_table_initrd_init(data, size); -} - /* * acpi_table_init() * diff --git a/drivers/acpi/video_detect.c b/drivers/acpi/video_detect.c index 3d1327615f72..a6b36fc53aec 100644 --- a/drivers/acpi/video_detect.c +++ b/drivers/acpi/video_detect.c @@ -167,6 +167,14 @@ static const struct dmi_system_id video_detect_dmi_table[] = { DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad X201s"), }, }, + { + .callback = video_detect_force_video, + .ident = "ThinkPad X201T", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad X201T"), + }, + }, /* The native backlight controls do not work on some older machines */ { |