diff options
Diffstat (limited to 'drivers/acpi')
-rw-r--r-- | drivers/acpi/Kconfig | 12 | ||||
-rw-r--r-- | drivers/acpi/Makefile | 2 | ||||
-rw-r--r-- | drivers/acpi/ac.c | 1 | ||||
-rw-r--r-- | drivers/acpi/acpi_pad.c | 514 | ||||
-rw-r--r-- | drivers/acpi/acpica/acconfig.h | 4 | ||||
-rw-r--r-- | drivers/acpi/acpica/acpredef.h | 5 | ||||
-rw-r--r-- | drivers/acpi/acpica/exregion.c | 35 | ||||
-rw-r--r-- | drivers/acpi/blacklist.c | 17 | ||||
-rw-r--r-- | drivers/acpi/button.c | 3 | ||||
-rw-r--r-- | drivers/acpi/dock.c | 16 | ||||
-rw-r--r-- | drivers/acpi/ec.c | 56 | ||||
-rw-r--r-- | drivers/acpi/pci_root.c | 11 | ||||
-rw-r--r-- | drivers/acpi/power_meter.c | 6 | ||||
-rw-r--r-- | drivers/acpi/proc.c | 4 | ||||
-rw-r--r-- | drivers/acpi/processor_core.c | 9 | ||||
-rw-r--r-- | drivers/acpi/processor_throttling.c | 6 | ||||
-rw-r--r-- | drivers/acpi/scan.c | 7 | ||||
-rw-r--r-- | drivers/acpi/sleep.c | 32 | ||||
-rw-r--r-- | drivers/acpi/video.c | 17 | ||||
-rw-r--r-- | drivers/acpi/video_detect.c | 2 |
20 files changed, 703 insertions, 56 deletions
diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index dd8729d674e5..93d2c7971df6 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig @@ -211,6 +211,18 @@ config ACPI_HOTPLUG_CPU select ACPI_CONTAINER default y +config ACPI_PROCESSOR_AGGREGATOR + tristate "Processor Aggregator" + depends on ACPI_PROCESSOR + depends on EXPERIMENTAL + depends on X86 + help + ACPI 4.0 defines processor Aggregator, which enables OS to perform + specific processor configuration and control that applies to all + processors in the platform. Currently only logical processor idling + is defined, which is to reduce power consumption. This driver + supports the new device. + config ACPI_THERMAL tristate "Thermal Zone" depends on ACPI_PROCESSOR diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile index 82cd49dc603b..7702118509a0 100644 --- a/drivers/acpi/Makefile +++ b/drivers/acpi/Makefile @@ -62,3 +62,5 @@ obj-$(CONFIG_ACPI_POWER_METER) += power_meter.o processor-y := processor_core.o processor_throttling.o processor-y += processor_idle.o processor_thermal.o processor-$(CONFIG_CPU_FREQ) += processor_perflib.o + +obj-$(CONFIG_ACPI_PROCESSOR_AGGREGATOR) += acpi_pad.o diff --git a/drivers/acpi/ac.c b/drivers/acpi/ac.c index 98b9690b0159..b6ed60b57b0d 100644 --- a/drivers/acpi/ac.c +++ b/drivers/acpi/ac.c @@ -245,6 +245,7 @@ static void acpi_ac_notify(struct acpi_device *device, u32 event) acpi_bus_generate_netlink_event(device->pnp.device_class, dev_name(&device->dev), event, (u32) ac->state); + acpi_notifier_call_chain(device, event, (u32) ac->state); #ifdef CONFIG_ACPI_SYSFS_POWER kobject_uevent(&ac->charger.dev->kobj, KOBJ_CHANGE); #endif diff --git a/drivers/acpi/acpi_pad.c b/drivers/acpi/acpi_pad.c new file mode 100644 index 000000000000..0d2cdb86158b --- /dev/null +++ b/drivers/acpi/acpi_pad.c @@ -0,0 +1,514 @@ +/* + * acpi_pad.c ACPI Processor Aggregator Driver + * + * Copyright (c) 2009, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <linux/kernel.h> +#include <linux/cpumask.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/types.h> +#include <linux/kthread.h> +#include <linux/freezer.h> +#include <linux/cpu.h> +#include <linux/clockchips.h> +#include <acpi/acpi_bus.h> +#include <acpi/acpi_drivers.h> + +#define ACPI_PROCESSOR_AGGREGATOR_CLASS "processor_aggregator" +#define ACPI_PROCESSOR_AGGREGATOR_DEVICE_NAME "Processor Aggregator" +#define ACPI_PROCESSOR_AGGREGATOR_NOTIFY 0x80 +static DEFINE_MUTEX(isolated_cpus_lock); + +#define MWAIT_SUBSTATE_MASK (0xf) +#define MWAIT_CSTATE_MASK (0xf) +#define MWAIT_SUBSTATE_SIZE (4) +#define CPUID_MWAIT_LEAF (5) +#define CPUID5_ECX_EXTENSIONS_SUPPORTED (0x1) +#define CPUID5_ECX_INTERRUPT_BREAK (0x2) +static unsigned long power_saving_mwait_eax; +static void power_saving_mwait_init(void) +{ + unsigned int eax, ebx, ecx, edx; + unsigned int highest_cstate = 0; + unsigned int highest_subcstate = 0; + int i; + + if (!boot_cpu_has(X86_FEATURE_MWAIT)) + return; + if (boot_cpu_data.cpuid_level < CPUID_MWAIT_LEAF) + return; + + cpuid(CPUID_MWAIT_LEAF, &eax, &ebx, &ecx, &edx); + + if (!(ecx & CPUID5_ECX_EXTENSIONS_SUPPORTED) || + !(ecx & CPUID5_ECX_INTERRUPT_BREAK)) + return; + + edx >>= MWAIT_SUBSTATE_SIZE; + for (i = 0; i < 7 && edx; i++, edx >>= MWAIT_SUBSTATE_SIZE) { + if (edx & MWAIT_SUBSTATE_MASK) { + highest_cstate = i; + highest_subcstate = edx & MWAIT_SUBSTATE_MASK; + } + } + power_saving_mwait_eax = (highest_cstate << MWAIT_SUBSTATE_SIZE) | + (highest_subcstate - 1); + + for_each_online_cpu(i) + clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_ON, &i); + +#if defined(CONFIG_GENERIC_TIME) && defined(CONFIG_X86) + switch (boot_cpu_data.x86_vendor) { + case X86_VENDOR_AMD: + case X86_VENDOR_INTEL: + /* + * AMD Fam10h TSC will tick in all + * C/P/S0/S1 states when this bit is set. + */ + if (boot_cpu_has(X86_FEATURE_NONSTOP_TSC)) + return; + + /*FALL THROUGH*/ + default: + /* TSC could halt in idle, so notify users */ + mark_tsc_unstable("TSC halts in idle"); + } +#endif +} + +static unsigned long cpu_weight[NR_CPUS]; +static int tsk_in_cpu[NR_CPUS] = {[0 ... NR_CPUS-1] = -1}; +static DECLARE_BITMAP(pad_busy_cpus_bits, NR_CPUS); +static void round_robin_cpu(unsigned int tsk_index) +{ + struct cpumask *pad_busy_cpus = to_cpumask(pad_busy_cpus_bits); + cpumask_var_t tmp; + int cpu; + unsigned long min_weight = -1, preferred_cpu; + + if (!alloc_cpumask_var(&tmp, GFP_KERNEL)) + return; + + mutex_lock(&isolated_cpus_lock); + cpumask_clear(tmp); + for_each_cpu(cpu, pad_busy_cpus) + cpumask_or(tmp, tmp, topology_thread_cpumask(cpu)); + cpumask_andnot(tmp, cpu_online_mask, tmp); + /* avoid HT sibilings if possible */ + if (cpumask_empty(tmp)) + cpumask_andnot(tmp, cpu_online_mask, pad_busy_cpus); + if (cpumask_empty(tmp)) { + mutex_unlock(&isolated_cpus_lock); + return; + } + for_each_cpu(cpu, tmp) { + if (cpu_weight[cpu] < min_weight) { + min_weight = cpu_weight[cpu]; + preferred_cpu = cpu; + } + } + + if (tsk_in_cpu[tsk_index] != -1) + cpumask_clear_cpu(tsk_in_cpu[tsk_index], pad_busy_cpus); + tsk_in_cpu[tsk_index] = preferred_cpu; + cpumask_set_cpu(preferred_cpu, pad_busy_cpus); + cpu_weight[preferred_cpu]++; + mutex_unlock(&isolated_cpus_lock); + + set_cpus_allowed_ptr(current, cpumask_of(preferred_cpu)); +} + +static void exit_round_robin(unsigned int tsk_index) +{ + struct cpumask *pad_busy_cpus = to_cpumask(pad_busy_cpus_bits); + cpumask_clear_cpu(tsk_in_cpu[tsk_index], pad_busy_cpus); + tsk_in_cpu[tsk_index] = -1; +} + +static unsigned int idle_pct = 5; /* percentage */ +static unsigned int round_robin_time = 10; /* second */ +static int power_saving_thread(void *data) +{ + struct sched_param param = {.sched_priority = 1}; + int do_sleep; + unsigned int tsk_index = (unsigned long)data; + u64 last_jiffies = 0; + + sched_setscheduler(current, SCHED_RR, ¶m); + + while (!kthread_should_stop()) { + int cpu; + u64 expire_time; + + try_to_freeze(); + + /* round robin to cpus */ + if (last_jiffies + round_robin_time * HZ < jiffies) { + last_jiffies = jiffies; + round_robin_cpu(tsk_index); + } + + do_sleep = 0; + + current_thread_info()->status &= ~TS_POLLING; + /* + * TS_POLLING-cleared state must be visible before we test + * NEED_RESCHED: + */ + smp_mb(); + + expire_time = jiffies + HZ * (100 - idle_pct) / 100; + + while (!need_resched()) { + local_irq_disable(); + cpu = smp_processor_id(); + clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_ENTER, + &cpu); + stop_critical_timings(); + + __monitor((void *)¤t_thread_info()->flags, 0, 0); + smp_mb(); + if (!need_resched()) + __mwait(power_saving_mwait_eax, 1); + + start_critical_timings(); + clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_EXIT, + &cpu); + local_irq_enable(); + + if (jiffies > expire_time) { + do_sleep = 1; + break; + } + } + + current_thread_info()->status |= TS_POLLING; + + /* + * current sched_rt has threshold for rt task running time. + * When a rt task uses 95% CPU time, the rt thread will be + * scheduled out for 5% CPU time to not starve other tasks. But + * the mechanism only works when all CPUs have RT task running, + * as if one CPU hasn't RT task, RT task from other CPUs will + * borrow CPU time from this CPU and cause RT task use > 95% + * CPU time. To make 'avoid staration' work, takes a nap here. + */ + if (do_sleep) + schedule_timeout_killable(HZ * idle_pct / 100); + } + + exit_round_robin(tsk_index); + return 0; +} + +static struct task_struct *ps_tsks[NR_CPUS]; +static unsigned int ps_tsk_num; +static int create_power_saving_task(void) +{ + ps_tsks[ps_tsk_num] = kthread_run(power_saving_thread, + (void *)(unsigned long)ps_tsk_num, + "power_saving/%d", ps_tsk_num); + if (ps_tsks[ps_tsk_num]) { + ps_tsk_num++; + return 0; + } + return -EINVAL; +} + +static void destroy_power_saving_task(void) +{ + if (ps_tsk_num > 0) { + ps_tsk_num--; + kthread_stop(ps_tsks[ps_tsk_num]); + } +} + +static void set_power_saving_task_num(unsigned int num) +{ + if (num > ps_tsk_num) { + while (ps_tsk_num < num) { + if (create_power_saving_task()) + return; + } + } else if (num < ps_tsk_num) { + while (ps_tsk_num > num) + destroy_power_saving_task(); + } +} + +static int acpi_pad_idle_cpus(unsigned int num_cpus) +{ + get_online_cpus(); + + num_cpus = min_t(unsigned int, num_cpus, num_online_cpus()); + set_power_saving_task_num(num_cpus); + + put_online_cpus(); + return 0; +} + +static uint32_t acpi_pad_idle_cpus_num(void) +{ + return ps_tsk_num; +} + +static ssize_t acpi_pad_rrtime_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + unsigned long num; + if (strict_strtoul(buf, 0, &num)) + return -EINVAL; + if (num < 1 || num >= 100) + return -EINVAL; + mutex_lock(&isolated_cpus_lock); + round_robin_time = num; + mutex_unlock(&isolated_cpus_lock); + return count; +} + +static ssize_t acpi_pad_rrtime_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return scnprintf(buf, PAGE_SIZE, "%d", round_robin_time); +} +static DEVICE_ATTR(rrtime, S_IRUGO|S_IWUSR, + acpi_pad_rrtime_show, + acpi_pad_rrtime_store); + +static ssize_t acpi_pad_idlepct_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + unsigned long num; + if (strict_strtoul(buf, 0, &num)) + return -EINVAL; + if (num < 1 || num >= 100) + return -EINVAL; + mutex_lock(&isolated_cpus_lock); + idle_pct = num; + mutex_unlock(&isolated_cpus_lock); + return count; +} + +static ssize_t acpi_pad_idlepct_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return scnprintf(buf, PAGE_SIZE, "%d", idle_pct); +} +static DEVICE_ATTR(idlepct, S_IRUGO|S_IWUSR, + acpi_pad_idlepct_show, + acpi_pad_idlepct_store); + +static ssize_t acpi_pad_idlecpus_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + unsigned long num; + if (strict_strtoul(buf, 0, &num)) + return -EINVAL; + mutex_lock(&isolated_cpus_lock); + acpi_pad_idle_cpus(num); + mutex_unlock(&isolated_cpus_lock); + return count; +} + +static ssize_t acpi_pad_idlecpus_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return cpumask_scnprintf(buf, PAGE_SIZE, + to_cpumask(pad_busy_cpus_bits)); +} +static DEVICE_ATTR(idlecpus, S_IRUGO|S_IWUSR, + acpi_pad_idlecpus_show, + acpi_pad_idlecpus_store); + +static int acpi_pad_add_sysfs(struct acpi_device *device) +{ + int result; + + result = device_create_file(&device->dev, &dev_attr_idlecpus); + if (result) + return -ENODEV; + result = device_create_file(&device->dev, &dev_attr_idlepct); + if (result) { + device_remove_file(&device->dev, &dev_attr_idlecpus); + return -ENODEV; + } + result = device_create_file(&device->dev, &dev_attr_rrtime); + if (result) { + device_remove_file(&device->dev, &dev_attr_idlecpus); + device_remove_file(&device->dev, &dev_attr_idlepct); + return -ENODEV; + } + return 0; +} + +static void acpi_pad_remove_sysfs(struct acpi_device *device) +{ + device_remove_file(&device->dev, &dev_attr_idlecpus); + device_remove_file(&device->dev, &dev_attr_idlepct); + device_remove_file(&device->dev, &dev_attr_rrtime); +} + +/* Query firmware how many CPUs should be idle */ +static int acpi_pad_pur(acpi_handle handle, int *num_cpus) +{ + struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL}; + acpi_status status; + union acpi_object *package; + int rev, num, ret = -EINVAL; + + status = acpi_evaluate_object(handle, "_PUR", NULL, &buffer); + if (ACPI_FAILURE(status)) + return -EINVAL; + package = buffer.pointer; + if (package->type != ACPI_TYPE_PACKAGE || package->package.count != 2) + goto out; + rev = package->package.elements[0].integer.value; + num = package->package.elements[1].integer.value; + if (rev != 1) + goto out; + *num_cpus = num; + ret = 0; +out: + kfree(buffer.pointer); + return ret; +} + +/* Notify firmware how many CPUs are idle */ +static void acpi_pad_ost(acpi_handle handle, int stat, + uint32_t idle_cpus) +{ + union acpi_object params[3] = { + {.type = ACPI_TYPE_INTEGER,}, + {.type = ACPI_TYPE_INTEGER,}, + {.type = ACPI_TYPE_BUFFER,}, + }; + struct acpi_object_list arg_list = {3, params}; + + params[0].integer.value = ACPI_PROCESSOR_AGGREGATOR_NOTIFY; + params[1].integer.value = stat; + params[2].buffer.length = 4; + params[2].buffer.pointer = (void *)&idle_cpus; + acpi_evaluate_object(handle, "_OST", &arg_list, NULL); +} + +static void acpi_pad_handle_notify(acpi_handle handle) +{ + int num_cpus, ret; + uint32_t idle_cpus; + + mutex_lock(&isolated_cpus_lock); + if (acpi_pad_pur(handle, &num_cpus)) { + mutex_unlock(&isolated_cpus_lock); + return; + } + ret = acpi_pad_idle_cpus(num_cpus); + idle_cpus = acpi_pad_idle_cpus_num(); + if (!ret) + acpi_pad_ost(handle, 0, idle_cpus); + else + acpi_pad_ost(handle, 1, 0); + mutex_unlock(&isolated_cpus_lock); +} + +static void acpi_pad_notify(acpi_handle handle, u32 event, + void *data) +{ + struct acpi_device *device = data; + + switch (event) { + case ACPI_PROCESSOR_AGGREGATOR_NOTIFY: + acpi_pad_handle_notify(handle); + acpi_bus_generate_proc_event(device, event, 0); + acpi_bus_generate_netlink_event(device->pnp.device_class, + dev_name(&device->dev), event, 0); + break; + default: + printk(KERN_WARNING"Unsupported event [0x%x]\n", event); + break; + } +} + +static int acpi_pad_add(struct acpi_device *device) +{ + acpi_status status; + + strcpy(acpi_device_name(device), ACPI_PROCESSOR_AGGREGATOR_DEVICE_NAME); + strcpy(acpi_device_class(device), ACPI_PROCESSOR_AGGREGATOR_CLASS); + + if (acpi_pad_add_sysfs(device)) + return -ENODEV; + + status = acpi_install_notify_handler(device->handle, + ACPI_DEVICE_NOTIFY, acpi_pad_notify, device); + if (ACPI_FAILURE(status)) { + acpi_pad_remove_sysfs(device); + return -ENODEV; + } + + return 0; +} + +static int acpi_pad_remove(struct acpi_device *device, + int type) +{ + mutex_lock(&isolated_cpus_lock); + acpi_pad_idle_cpus(0); + mutex_unlock(&isolated_cpus_lock); + + acpi_remove_notify_handler(device->handle, + ACPI_DEVICE_NOTIFY, acpi_pad_notify); + acpi_pad_remove_sysfs(device); + return 0; +} + +static const struct acpi_device_id pad_device_ids[] = { + {"ACPI000C", 0}, + {"", 0}, +}; +MODULE_DEVICE_TABLE(acpi, pad_device_ids); + +static struct acpi_driver acpi_pad_driver = { + .name = "processor_aggregator", + .class = ACPI_PROCESSOR_AGGREGATOR_CLASS, + .ids = pad_device_ids, + .ops = { + .add = acpi_pad_add, + .remove = acpi_pad_remove, + }, +}; + +static int __init acpi_pad_init(void) +{ + power_saving_mwait_init(); + if (power_saving_mwait_eax == 0) + return -EINVAL; + + return acpi_bus_register_driver(&acpi_pad_driver); +} + +static void __exit acpi_pad_exit(void) +{ + acpi_bus_unregister_driver(&acpi_pad_driver); +} + +module_init(acpi_pad_init); +module_exit(acpi_pad_exit); +MODULE_AUTHOR("Shaohua Li<shaohua.li@intel.com>"); +MODULE_DESCRIPTION("ACPI Processor Aggregator Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/acpi/acpica/acconfig.h b/drivers/acpi/acpica/acconfig.h index 8e679ef5b231..a4471e3d3853 100644 --- a/drivers/acpi/acpica/acconfig.h +++ b/drivers/acpi/acpica/acconfig.h @@ -103,9 +103,9 @@ #define ACPI_MAX_REFERENCE_COUNT 0x1000 -/* Size of cached memory mapping for system memory operation region */ +/* Default page size for use in mapping memory for operation regions */ -#define ACPI_SYSMEM_REGION_WINDOW_SIZE 4096 +#define ACPI_DEFAULT_PAGE_SIZE 4096 /* Must be power of 2 */ /* owner_id tracking. 8 entries allows for 255 owner_ids */ diff --git a/drivers/acpi/acpica/acpredef.h b/drivers/acpi/acpica/acpredef.h index cd80d1dd1950..57bdaf6ffab1 100644 --- a/drivers/acpi/acpica/acpredef.h +++ b/drivers/acpi/acpica/acpredef.h @@ -203,8 +203,9 @@ static const union acpi_predefined_info predefined_names[] = {{"_BCT", 1, ACPI_RTYPE_INTEGER}}, {{"_BDN", 0, ACPI_RTYPE_INTEGER}}, {{"_BFS", 1, 0}}, - {{"_BIF", 0, ACPI_RTYPE_PACKAGE}}, /* Fixed-length (9 Int),(4 Str) */ - {{{ACPI_PTYPE1_FIXED, ACPI_RTYPE_INTEGER, 9, ACPI_RTYPE_STRING}, 4,0}}, + {{"_BIF", 0, ACPI_RTYPE_PACKAGE} }, /* Fixed-length (9 Int),(4 Str/Buf) */ + {{{ACPI_PTYPE1_FIXED, ACPI_RTYPE_INTEGER, 9, + ACPI_RTYPE_STRING | ACPI_RTYPE_BUFFER}, 4, 0} }, {{"_BIX", 0, ACPI_RTYPE_PACKAGE}}, /* Fixed-length (16 Int),(4 Str) */ {{{ACPI_PTYPE1_FIXED, ACPI_RTYPE_INTEGER, 16, ACPI_RTYPE_STRING}, 4, diff --git a/drivers/acpi/acpica/exregion.c b/drivers/acpi/acpica/exregion.c index 3a54b737d2da..2bd83ac57c3a 100644 --- a/drivers/acpi/acpica/exregion.c +++ b/drivers/acpi/acpica/exregion.c @@ -77,7 +77,8 @@ acpi_ex_system_memory_space_handler(u32 function, void *logical_addr_ptr = NULL; struct acpi_mem_space_context *mem_info = region_context; u32 length; - acpi_size window_size; + acpi_size map_length; + acpi_size page_boundary_map_length; #ifdef ACPI_MISALIGNMENT_NOT_SUPPORTED u32 remainder; #endif @@ -144,25 +145,39 @@ acpi_ex_system_memory_space_handler(u32 function, } /* - * Don't attempt to map memory beyond the end of the region, and - * constrain the maximum mapping size to something reasonable. + * Attempt to map from the requested address to the end of the region. + * However, we will never map more than one page, nor will we cross + * a page boundary. */ - window_size = (acpi_size) + map_length = (acpi_size) ((mem_info->address + mem_info->length) - address); - if (window_size > ACPI_SYSMEM_REGION_WINDOW_SIZE) { - window_size = ACPI_SYSMEM_REGION_WINDOW_SIZE; + /* + * If mapping the entire remaining portion of the region will cross + * a page boundary, just map up to the page boundary, do not cross. + * On some systems, crossing a page boundary while mapping regions + * can cause warnings if the pages have different attributes + * due to resource management + */ + page_boundary_map_length = + ACPI_ROUND_UP(address, ACPI_DEFAULT_PAGE_SIZE) - address; + + if (!page_boundary_map_length) { + page_boundary_map_length = ACPI_DEFAULT_PAGE_SIZE; + } + + if (map_length > page_boundary_map_length) { + map_length = page_boundary_map_length; } /* Create a new mapping starting at the address given */ - mem_info->mapped_logical_address = - acpi_os_map_memory((acpi_physical_address) address, window_size); + mem_info->mapped_logical_address = acpi_os_map_memory((acpi_physical_address) address, map_length); if (!mem_info->mapped_logical_address) { ACPI_ERROR((AE_INFO, "Could not map memory at %8.8X%8.8X, size %X", ACPI_FORMAT_NATIVE_UINT(address), - (u32) window_size)); + (u32) map_length)); mem_info->mapped_length = 0; return_ACPI_STATUS(AE_NO_MEMORY); } @@ -170,7 +185,7 @@ acpi_ex_system_memory_space_handler(u32 function, /* Save the physical address and mapping size */ mem_info->mapped_physical_address = address; - mem_info->mapped_length = window_size; + mem_info->mapped_length = map_length; } /* diff --git a/drivers/acpi/blacklist.c b/drivers/acpi/blacklist.c index e56b2a7b53db..23e5a0519af5 100644 --- a/drivers/acpi/blacklist.c +++ b/drivers/acpi/blacklist.c @@ -224,6 +224,7 @@ static struct dmi_system_id acpi_osi_dmi_table[] __initdata = { * _OSI(Linux) helps sound * DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad R61"), * DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad T61"), + * T400, T500 * _OSI(Linux) has Linux specific hooks * DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad X61"), * _OSI(Linux) is a NOP: @@ -254,6 +255,22 @@ static struct dmi_system_id acpi_osi_dmi_table[] __initdata = { DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad X61"), }, }, + { + .callback = dmi_enable_osi_linux, + .ident = "Lenovo ThinkPad T400", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad T400"), + }, + }, + { + .callback = dmi_enable_osi_linux, + .ident = "Lenovo ThinkPad T500", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad T500"), + }, + }, {} }; diff --git a/drivers/acpi/button.c b/drivers/acpi/button.c index 9335b87c5174..0c9c6a9a002c 100644 --- a/drivers/acpi/button.c +++ b/drivers/acpi/button.c @@ -251,6 +251,9 @@ 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)) diff --git a/drivers/acpi/dock.c b/drivers/acpi/dock.c index 3a2cfefc71ab..7338b6a3e049 100644 --- a/drivers/acpi/dock.c +++ b/drivers/acpi/dock.c @@ -67,7 +67,7 @@ struct dock_station { struct list_head dependent_devices; struct list_head hotplug_devices; - struct list_head sibiling; + struct list_head sibling; struct platform_device *dock_device; }; static LIST_HEAD(dock_stations); @@ -275,7 +275,7 @@ int is_dock_device(acpi_handle handle) if (is_dock(handle)) return 1; - list_for_each_entry(dock_station, &dock_stations, sibiling) { + list_for_each_entry(dock_station, &dock_stations, sibling) { if (find_dock_dependent_device(dock_station, handle)) return 1; } @@ -619,7 +619,7 @@ register_hotplug_dock_device(acpi_handle handle, struct acpi_dock_ops *ops, * make sure this handle is for a device dependent on the dock, * this would include the dock station itself */ - list_for_each_entry(dock_station, &dock_stations, sibiling) { + list_for_each_entry(dock_station, &dock_stations, sibling) { /* * An ATA bay can be in a dock and itself can be ejected * seperately, so there are two 'dock stations' which need the @@ -651,7 +651,7 @@ void unregister_hotplug_dock_device(acpi_handle handle) if (!dock_station_count) return; - list_for_each_entry(dock_station, &dock_stations, sibiling) { + list_for_each_entry(dock_station, &dock_stations, sibling) { dd = find_dock_dependent_device(dock_station, handle); if (dd) dock_del_hotplug_device(dock_station, dd); @@ -787,7 +787,7 @@ static int acpi_dock_notifier_call(struct notifier_block *this, if (event != ACPI_NOTIFY_BUS_CHECK && event != ACPI_NOTIFY_DEVICE_CHECK && event != ACPI_NOTIFY_EJECT_REQUEST) return 0; - list_for_each_entry(dock_station, &dock_stations, sibiling) { + list_for_each_entry(dock_station, &dock_stations, sibling) { if (dock_station->handle == handle) { struct dock_data *dock_data; @@ -958,7 +958,7 @@ static int dock_add(acpi_handle handle) dock_station->last_dock_time = jiffies - HZ; INIT_LIST_HEAD(&dock_station->dependent_devices); INIT_LIST_HEAD(&dock_station->hotplug_devices); - INIT_LIST_HEAD(&dock_station->sibiling); + INIT_LIST_HEAD(&dock_station->sibling); spin_lock_init(&dock_station->dd_lock); mutex_init(&dock_station->hp_lock); ATOMIC_INIT_NOTIFIER_HEAD(&dock_notifier_list); @@ -1044,7 +1044,7 @@ static int dock_add(acpi_handle handle) add_dock_dependent_device(dock_station, dd); dock_station_count++; - list_add(&dock_station->sibiling, &dock_stations); + list_add(&dock_station->sibling, &dock_stations); return 0; dock_add_err_unregister: @@ -1149,7 +1149,7 @@ static void __exit dock_exit(void) struct dock_station *tmp; unregister_acpi_bus_notifier(&dock_acpi_notifier); - list_for_each_entry_safe(dock_station, tmp, &dock_stations, sibiling) + list_for_each_entry_safe(dock_station, tmp, &dock_stations, sibling) dock_remove(dock_station); } diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index f70796081c4c..baef28c1e630 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -119,6 +119,8 @@ static struct acpi_ec { } *boot_ec, *first_ec; static int EC_FLAGS_MSI; /* Out-of-spec MSI controller */ +static int EC_FLAGS_VALIDATE_ECDT; /* ASUStec ECDTs need to be validated */ +static int EC_FLAGS_SKIP_DSDT_SCAN; /* Not all BIOS survive early DSDT scan */ /* -------------------------------------------------------------------------- Transaction Management @@ -232,10 +234,8 @@ static int ec_poll(struct acpi_ec *ec) } advance_transaction(ec, acpi_ec_read_status(ec)); } while (time_before(jiffies, delay)); - if (!ec->curr->irq_count || - (acpi_ec_read_status(ec) & ACPI_EC_FLAG_IBF)) + if (acpi_ec_read_status(ec) & ACPI_EC_FLAG_IBF) break; - /* try restart command if we get any false interrupts */ pr_debug(PREFIX "controller reset, restart transaction\n"); spin_lock_irqsave(&ec->curr_lock, flags); start_transaction(ec); @@ -899,6 +899,44 @@ static const struct acpi_device_id ec_device_ids[] = { {"", 0}, }; +/* Some BIOS do not survive early DSDT scan, skip it */ +static int ec_skip_dsdt_scan(const struct dmi_system_id *id) +{ + EC_FLAGS_SKIP_DSDT_SCAN = 1; + return 0; +} + +/* ASUStek often supplies us with broken ECDT, validate it */ +static int ec_validate_ecdt(const struct dmi_system_id *id) +{ + EC_FLAGS_VALIDATE_ECDT = 1; + return 0; +} + +/* MSI EC needs special treatment, enable it */ +static int ec_flag_msi(const struct dmi_system_id *id) +{ + EC_FLAGS_MSI = 1; + EC_FLAGS_VALIDATE_ECDT = 1; + return 0; +} + +static struct dmi_system_id __initdata ec_dmi_table[] = { + { + ec_skip_dsdt_scan, "Compal JFL92", { + DMI_MATCH(DMI_BIOS_VENDOR, "COMPAL"), + DMI_MATCH(DMI_BOARD_NAME, "JFL92") }, NULL}, + { + ec_flag_msi, "MSI hardware", { + DMI_MATCH(DMI_BIOS_VENDOR, "Micro-Star"), + DMI_MATCH(DMI_CHASSIS_VENDOR, "MICRO-Star") }, NULL}, + { + ec_validate_ecdt, "ASUS hardware", { + DMI_MATCH(DMI_BIOS_VENDOR, "ASUS") }, NULL}, + {}, +}; + + int __init acpi_ec_ecdt_probe(void) { acpi_status status; @@ -911,11 +949,7 @@ int __init acpi_ec_ecdt_probe(void) /* * Generate a boot ec context */ - if (dmi_name_in_vendors("Micro-Star") || - dmi_name_in_vendors("Notebook")) { - pr_info(PREFIX "Enabling special treatment for EC from MSI.\n"); - EC_FLAGS_MSI = 1; - } + dmi_check_system(ec_dmi_table); status = acpi_get_table(ACPI_SIG_ECDT, 1, (struct acpi_table_header **)&ecdt_ptr); if (ACPI_SUCCESS(status)) { @@ -926,7 +960,7 @@ int __init acpi_ec_ecdt_probe(void) boot_ec->handle = ACPI_ROOT_OBJECT; acpi_get_handle(ACPI_ROOT_OBJECT, ecdt_ptr->id, &boot_ec->handle); /* Don't trust ECDT, which comes from ASUSTek */ - if (!dmi_name_in_vendors("ASUS") && EC_FLAGS_MSI == 0) + if (!EC_FLAGS_VALIDATE_ECDT) goto install; saved_ec = kmalloc(sizeof(struct acpi_ec), GFP_KERNEL); if (!saved_ec) @@ -934,6 +968,10 @@ int __init acpi_ec_ecdt_probe(void) memcpy(saved_ec, boot_ec, sizeof(struct acpi_ec)); /* fall through */ } + + if (EC_FLAGS_SKIP_DSDT_SCAN) + return -ENODEV; + /* This workaround is needed only on some broken machines, * which require early EC, but fail to provide ECDT */ printk(KERN_DEBUG PREFIX "Look up EC in DSDT\n"); diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c index 31122214e0ec..1af808171d46 100644 --- a/drivers/acpi/pci_root.c +++ b/drivers/acpi/pci_root.c @@ -389,6 +389,17 @@ struct pci_dev *acpi_get_pci_dev(acpi_handle handle) pbus = pdev->subordinate; pci_dev_put(pdev); + + /* + * This function may be called for a non-PCI device that has a + * PCI parent (eg. a disk under a PCI SATA controller). In that + * case pdev->subordinate will be NULL for the parent. + */ + if (!pbus) { + dev_dbg(&pdev->dev, "Not a PCI-to-PCI bridge\n"); + pdev = NULL; + break; + } } out: list_for_each_entry_safe(node, tmp, &device_list, node) diff --git a/drivers/acpi/power_meter.c b/drivers/acpi/power_meter.c index e6bfd77986b8..2ef7030a0c28 100644 --- a/drivers/acpi/power_meter.c +++ b/drivers/acpi/power_meter.c @@ -294,7 +294,11 @@ static int set_acpi_trip(struct acpi_power_meter_resource *resource) return -EINVAL; } - return data; + /* _PTP returns 0 on success, nonzero otherwise */ + if (data) + return -EINVAL; + + return 0; } static ssize_t set_trip(struct device *dev, struct device_attribute *devattr, diff --git a/drivers/acpi/proc.c b/drivers/acpi/proc.c index d0d550d22a6d..d0d25e2e1ced 100644 --- a/drivers/acpi/proc.c +++ b/drivers/acpi/proc.c @@ -393,11 +393,13 @@ acpi_system_write_wakeup_device(struct file *file, struct list_head *node, *next; char strbuf[5]; char str[5] = ""; - int len = count; + unsigned int len = count; struct acpi_device *found_dev = NULL; if (len > 4) len = 4; + if (len < 0) + return -EFAULT; if (copy_from_user(strbuf, buffer, len)) return -EFAULT; diff --git a/drivers/acpi/processor_core.c b/drivers/acpi/processor_core.c index c2d4d6e09364..ec742a4e5635 100644 --- a/drivers/acpi/processor_core.c +++ b/drivers/acpi/processor_core.c @@ -770,7 +770,7 @@ static struct notifier_block acpi_cpu_notifier = .notifier_call = acpi_cpu_soft_notify, }; -static int acpi_processor_add(struct acpi_device *device) +static int __cpuinit acpi_processor_add(struct acpi_device *device) { struct acpi_processor *pr = NULL; int result = 0; @@ -863,13 +863,6 @@ static int acpi_processor_add(struct acpi_device *device) goto err_remove_sysfs; } - if (pr->flags.throttling) { - printk(KERN_INFO PREFIX "%s [%s] (supports", - acpi_device_name(device), acpi_device_bid(device)); - printk(" %d throttling states", pr->throttling.state_count); - printk(")\n"); - } - return 0; err_remove_sysfs: diff --git a/drivers/acpi/processor_throttling.c b/drivers/acpi/processor_throttling.c index 4c6c14c1e307..1c5d7a8b2fdf 100644 --- a/drivers/acpi/processor_throttling.c +++ b/drivers/acpi/processor_throttling.c @@ -1133,15 +1133,15 @@ int acpi_processor_get_throttling_info(struct acpi_processor *pr) int result = 0; struct acpi_processor_throttling *pthrottling; + if (!pr) + return -EINVAL; + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "pblk_address[0x%08x] duty_offset[%d] duty_width[%d]\n", pr->throttling.address, pr->throttling.duty_offset, pr->throttling.duty_width)); - if (!pr) - return -EINVAL; - /* * Evaluate _PTC, _TSS and _TPC * They must all be present or none of them can be used. diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 468921bed22f..14a7481c97d7 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -1052,6 +1052,8 @@ static void acpi_device_set_id(struct acpi_device *device) device->flags.bus_address = 1; } + kfree(info); + /* * Some devices don't reliably have _HIDs & _CIDs, so add * synthetic HIDs to make sure drivers can find them. @@ -1325,13 +1327,8 @@ static int acpi_bus_scan(acpi_handle handle, struct acpi_bus_ops *ops, struct acpi_device **child) { acpi_status status; - struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; void *device = NULL; - acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer); - printk(KERN_INFO PREFIX "Enumerating devices from [%s]\n", - (char *) buffer.pointer); - status = acpi_bus_check_add(handle, 0, ops, &device); if (ACPI_SUCCESS(status)) acpi_walk_namespace(ACPI_TYPE_ANY, handle, ACPI_UINT32_MAX, diff --git a/drivers/acpi/sleep.c b/drivers/acpi/sleep.c index a90afcc723ab..5f2c379ab7bf 100644 --- a/drivers/acpi/sleep.c +++ b/drivers/acpi/sleep.c @@ -413,6 +413,38 @@ static struct dmi_system_id __initdata acpisleep_dmi_table[] = { }, }, { + .callback = init_set_sci_en_on_resume, + .ident = "Hewlett-Packard Pavilion dv4", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), + DMI_MATCH(DMI_PRODUCT_NAME, "HP Pavilion dv4"), + }, + }, + { + .callback = init_set_sci_en_on_resume, + .ident = "Hewlett-Packard Pavilion dv7", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), + DMI_MATCH(DMI_PRODUCT_NAME, "HP Pavilion dv7"), + }, + }, + { + .callback = init_set_sci_en_on_resume, + .ident = "Hewlett-Packard Compaq Presario C700 Notebook PC", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), + DMI_MATCH(DMI_PRODUCT_NAME, "Compaq Presario C700 Notebook PC"), + }, + }, + { + .callback = init_set_sci_en_on_resume, + .ident = "Hewlett-Packard Compaq Presario CQ40 Notebook PC", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), + DMI_MATCH(DMI_PRODUCT_NAME, "Compaq Presario CQ40 Notebook PC"), + }, + }, + { .callback = init_old_suspend_ordering, .ident = "Panasonic CF51-2L", .matches = { diff --git a/drivers/acpi/video.c b/drivers/acpi/video.c index a4fddb24476f..05dff631591c 100644 --- a/drivers/acpi/video.c +++ b/drivers/acpi/video.c @@ -285,7 +285,7 @@ static int acpi_video_device_brightness_open_fs(struct inode *inode, struct file *file); static ssize_t acpi_video_device_write_brightness(struct file *file, const char __user *buffer, size_t count, loff_t *data); -static struct file_operations acpi_video_device_brightness_fops = { +static const struct file_operations acpi_video_device_brightness_fops = { .owner = THIS_MODULE, .open = acpi_video_device_brightness_open_fs, .read = seq_read, @@ -1109,7 +1109,12 @@ static int acpi_video_bus_check(struct acpi_video_bus *video) */ /* Does this device support video switching? */ - if (video->cap._DOS) { + if (video->cap._DOS || video->cap._DOD) { + if (!video->cap._DOS) { + printk(KERN_WARNING FW_BUG + "ACPI(%s) defines _DOD but not _DOS\n", + acpi_device_bid(video->device)); + } video->flags.multihead = 1; status = 0; } @@ -1218,7 +1223,7 @@ acpi_video_device_write_state(struct file *file, u32 state = 0; - if (!dev || count + 1 > sizeof str) + if (!dev || count >= sizeof(str)) return -EINVAL; if (copy_from_user(str, buffer, count)) @@ -1275,7 +1280,7 @@ acpi_video_device_write_brightness(struct file *file, int i; - if (!dev || !dev->brightness || count + 1 > sizeof str) + if (!dev || !dev->brightness || count >= sizeof(str)) return -EINVAL; if (copy_from_user(str, buffer, count)) @@ -1557,7 +1562,7 @@ acpi_video_bus_write_POST(struct file *file, unsigned long long opt, options; - if (!video || count + 1 > sizeof str) + if (!video || count >= sizeof(str)) return -EINVAL; status = acpi_video_bus_POST_options(video, &options); @@ -1597,7 +1602,7 @@ acpi_video_bus_write_DOS(struct file *file, unsigned long opt; - if (!video || count + 1 > sizeof str) + if (!video || count >= sizeof(str)) return -EINVAL; if (copy_from_user(str, buffer, count)) diff --git a/drivers/acpi/video_detect.c b/drivers/acpi/video_detect.c index 7032f25da9b5..575593a8b4e6 100644 --- a/drivers/acpi/video_detect.c +++ b/drivers/acpi/video_detect.c @@ -84,7 +84,7 @@ long acpi_is_video_device(struct acpi_device *device) return 0; /* Does this device able to support video switching ? */ - if (ACPI_SUCCESS(acpi_get_handle(device->handle, "_DOD", &h_dummy)) && + 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; |