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/acpi_pad.c | 514 | ||||
-rw-r--r-- | drivers/acpi/bus.c | 49 | ||||
-rw-r--r-- | drivers/acpi/dock.c | 16 | ||||
-rw-r--r-- | drivers/acpi/ec.c | 56 | ||||
-rw-r--r-- | drivers/acpi/osl.c | 8 | ||||
-rw-r--r-- | drivers/acpi/proc.c | 2 | ||||
-rw-r--r-- | drivers/acpi/processor_core.c | 7 | ||||
-rw-r--r-- | drivers/acpi/processor_idle.c | 8 | ||||
-rw-r--r-- | drivers/acpi/scan.c | 704 | ||||
-rw-r--r-- | drivers/acpi/video.c | 6 |
12 files changed, 889 insertions, 495 deletions
diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index dd8729d674e5..0ed42d8870c7 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 + specfic 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 + support 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/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/bus.c b/drivers/acpi/bus.c index 135fbfe1825c..741191524353 100644 --- a/drivers/acpi/bus.c +++ b/drivers/acpi/bus.c @@ -94,36 +94,33 @@ int acpi_bus_get_device(acpi_handle handle, struct acpi_device **device) EXPORT_SYMBOL(acpi_bus_get_device); -int acpi_bus_get_status(struct acpi_device *device) +acpi_status acpi_bus_get_status_handle(acpi_handle handle, + unsigned long long *sta) { - acpi_status status = AE_OK; - unsigned long long sta = 0; - + acpi_status status; - if (!device) - return -EINVAL; + status = acpi_evaluate_integer(handle, "_STA", NULL, sta); + if (ACPI_SUCCESS(status)) + return AE_OK; - /* - * Evaluate _STA if present. - */ - if (device->flags.dynamic_status) { - status = - acpi_evaluate_integer(device->handle, "_STA", NULL, &sta); - if (ACPI_FAILURE(status)) - return -ENODEV; - STRUCT_TO_INT(device->status) = (int)sta; + if (status == AE_NOT_FOUND) { + *sta = ACPI_STA_DEVICE_PRESENT | ACPI_STA_DEVICE_ENABLED | + ACPI_STA_DEVICE_UI | ACPI_STA_DEVICE_FUNCTIONING; + return AE_OK; } + return status; +} - /* - * According to ACPI spec some device can be present and functional - * even if the parent is not present but functional. - * In such conditions the child device should not inherit the status - * from the parent. - */ - else - STRUCT_TO_INT(device->status) = - ACPI_STA_DEVICE_PRESENT | ACPI_STA_DEVICE_ENABLED | - ACPI_STA_DEVICE_UI | ACPI_STA_DEVICE_FUNCTIONING; +int acpi_bus_get_status(struct acpi_device *device) +{ + acpi_status status; + unsigned long long sta; + + status = acpi_bus_get_status_handle(device->handle, &sta); + if (ACPI_FAILURE(status)) + return -ENODEV; + + STRUCT_TO_INT(device->status) = (int) sta; if (device->status.functional && !device->status.present) { ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device [%s] status [%08x]: " @@ -135,10 +132,8 @@ int acpi_bus_get_status(struct acpi_device *device) ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device [%s] status [%08x]\n", device->pnp.bus_id, (u32) STRUCT_TO_INT(device->status))); - return 0; } - EXPORT_SYMBOL(acpi_bus_get_status); void acpi_bus_private_data_handler(acpi_handle handle, 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/osl.c b/drivers/acpi/osl.c index 5633b86e3ed1..7c1c59ea9ec6 100644 --- a/drivers/acpi/osl.c +++ b/drivers/acpi/osl.c @@ -1161,7 +1161,13 @@ int acpi_check_resource_conflict(struct resource *res) res_list_elem->name, (long long) res_list_elem->start, (long long) res_list_elem->end); - printk(KERN_INFO "ACPI: Device needs an ACPI driver\n"); + if (acpi_enforce_resources == ENFORCE_RESOURCES_LAX) + printk(KERN_NOTICE "ACPI: This conflict may" + " cause random problems and system" + " instability\n"); + printk(KERN_INFO "ACPI: If an ACPI driver is available" + " for this device, you should use it instead of" + " the native driver\n"); } if (acpi_enforce_resources == ENFORCE_RESOURCES_STRICT) return -EBUSY; diff --git a/drivers/acpi/proc.c b/drivers/acpi/proc.c index d0d550d22a6d..f8b6f555ba52 100644 --- a/drivers/acpi/proc.c +++ b/drivers/acpi/proc.c @@ -398,6 +398,8 @@ acpi_system_write_wakeup_device(struct file *file, 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..c567b46dfa0f 100644 --- a/drivers/acpi/processor_core.c +++ b/drivers/acpi/processor_core.c @@ -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_idle.c b/drivers/acpi/processor_idle.c index cc61a6220102..bbd066e7f854 100644 --- a/drivers/acpi/processor_idle.c +++ b/drivers/acpi/processor_idle.c @@ -1166,7 +1166,6 @@ int __cpuinit acpi_processor_power_init(struct acpi_processor *pr, #ifdef CONFIG_ACPI_PROCFS struct proc_dir_entry *entry = NULL; #endif - unsigned int i; if (boot_option_idle_override) return 0; @@ -1214,13 +1213,6 @@ int __cpuinit acpi_processor_power_init(struct acpi_processor *pr, acpi_processor_setup_cpuidle(pr); if (cpuidle_register_device(&pr->power.dev)) return -EIO; - - printk(KERN_INFO PREFIX "CPU%d (power states:", pr->id); - for (i = 1; i <= pr->power.count; i++) - if (pr->power.states[i].valid) - printk(" C%d[C%d]", i, - pr->power.states[i].type); - printk(")\n"); } #ifdef CONFIG_ACPI_PROCFS /* 'power' [R] */ diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 408ebde18986..14a7481c97d7 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -22,6 +22,8 @@ extern struct acpi_device *acpi_root; #define ACPI_BUS_HID "LNXSYBUS" #define ACPI_BUS_DEVICE_NAME "System Bus" +#define ACPI_IS_ROOT_DEVICE(device) (!(device)->parent) + static LIST_HEAD(acpi_device_list); static LIST_HEAD(acpi_bus_id_list); DEFINE_MUTEX(acpi_device_lock); @@ -43,40 +45,19 @@ static int create_modalias(struct acpi_device *acpi_dev, char *modalias, { int len; int count; - - if (!acpi_dev->flags.hardware_id && !acpi_dev->flags.compatible_ids) - return -ENODEV; + struct acpi_hardware_id *id; len = snprintf(modalias, size, "acpi:"); size -= len; - if (acpi_dev->flags.hardware_id) { - count = snprintf(&modalias[len], size, "%s:", - acpi_dev->pnp.hardware_id); + list_for_each_entry(id, &acpi_dev->pnp.ids, list) { + count = snprintf(&modalias[len], size, "%s:", id->id); if (count < 0 || count >= size) return -EINVAL; len += count; size -= count; } - if (acpi_dev->flags.compatible_ids) { - struct acpica_device_id_list *cid_list; - int i; - - cid_list = acpi_dev->pnp.cid_list; - for (i = 0; i < cid_list->count; i++) { - count = snprintf(&modalias[len], size, "%s:", - cid_list->ids[i].string); - if (count < 0 || count >= size) { - printk(KERN_ERR PREFIX "%s cid[%i] exceeds event buffer size", - acpi_dev->pnp.device_name, i); - break; - } - len += count; - size -= count; - } - } - modalias[len] = '\0'; return len; } @@ -183,7 +164,7 @@ static ssize_t acpi_device_hid_show(struct device *dev, struct device_attribute *attr, char *buf) { struct acpi_device *acpi_dev = to_acpi_device(dev); - return sprintf(buf, "%s\n", acpi_dev->pnp.hardware_id); + return sprintf(buf, "%s\n", acpi_device_hid(acpi_dev)); } static DEVICE_ATTR(hid, 0444, acpi_device_hid_show, NULL); @@ -219,17 +200,13 @@ static int acpi_device_setup_files(struct acpi_device *dev) goto end; } - if (dev->flags.hardware_id) { - result = device_create_file(&dev->dev, &dev_attr_hid); - if (result) - goto end; - } + result = device_create_file(&dev->dev, &dev_attr_hid); + if (result) + goto end; - if (dev->flags.hardware_id || dev->flags.compatible_ids) { - result = device_create_file(&dev->dev, &dev_attr_modalias); - if (result) - goto end; - } + result = device_create_file(&dev->dev, &dev_attr_modalias); + if (result) + goto end; /* * If device has _EJ0, 'eject' file is created that is used to trigger @@ -255,11 +232,8 @@ static void acpi_device_remove_files(struct acpi_device *dev) if (ACPI_SUCCESS(status)) device_remove_file(&dev->dev, &dev_attr_eject); - if (dev->flags.hardware_id || dev->flags.compatible_ids) - device_remove_file(&dev->dev, &dev_attr_modalias); - - if (dev->flags.hardware_id) - device_remove_file(&dev->dev, &dev_attr_hid); + device_remove_file(&dev->dev, &dev_attr_modalias); + device_remove_file(&dev->dev, &dev_attr_hid); if (dev->handle) device_remove_file(&dev->dev, &dev_attr_path); } @@ -271,6 +245,7 @@ int acpi_match_device_ids(struct acpi_device *device, const struct acpi_device_id *ids) { const struct acpi_device_id *id; + struct acpi_hardware_id *hwid; /* * If the device is not present, it is unnecessary to load device @@ -279,40 +254,30 @@ int acpi_match_device_ids(struct acpi_device *device, if (!device->status.present) return -ENODEV; - if (device->flags.hardware_id) { - for (id = ids; id->id[0]; id++) { - if (!strcmp((char*)id->id, device->pnp.hardware_id)) + for (id = ids; id->id[0]; id++) + list_for_each_entry(hwid, &device->pnp.ids, list) + if (!strcmp((char *) id->id, hwid->id)) return 0; - } - } - - if (device->flags.compatible_ids) { - struct acpica_device_id_list *cid_list = device->pnp.cid_list; - int i; - - for (id = ids; id->id[0]; id++) { - /* compare multiple _CID entries against driver ids */ - for (i = 0; i < cid_list->count; i++) { - if (!strcmp((char*)id->id, - cid_list->ids[i].string)) - return 0; - } - } - } return -ENOENT; } EXPORT_SYMBOL(acpi_match_device_ids); +static void acpi_free_ids(struct acpi_device *device) +{ + struct acpi_hardware_id *id, *tmp; + + list_for_each_entry_safe(id, tmp, &device->pnp.ids, list) { + kfree(id->id); + kfree(id); + } +} + static void acpi_device_release(struct device *dev) { struct acpi_device *acpi_dev = to_acpi_device(dev); - kfree(acpi_dev->pnp.cid_list); - if (acpi_dev->flags.hardware_id) - kfree(acpi_dev->pnp.hardware_id); - if (acpi_dev->flags.unique_id) - kfree(acpi_dev->pnp.unique_id); + acpi_free_ids(acpi_dev); kfree(acpi_dev); } @@ -378,15 +343,13 @@ static acpi_status acpi_device_notify_fixed(void *data) static int acpi_device_install_notify_handler(struct acpi_device *device) { acpi_status status; - char *hid; - hid = acpi_device_hid(device); - if (!strcmp(hid, ACPI_BUTTON_HID_POWERF)) + if (device->device_type == ACPI_BUS_TYPE_POWER_BUTTON) status = acpi_install_fixed_event_handler(ACPI_EVENT_POWER_BUTTON, acpi_device_notify_fixed, device); - else if (!strcmp(hid, ACPI_BUTTON_HID_SLEEPF)) + else if (device->device_type == ACPI_BUS_TYPE_SLEEP_BUTTON) status = acpi_install_fixed_event_handler(ACPI_EVENT_SLEEP_BUTTON, acpi_device_notify_fixed, @@ -404,10 +367,10 @@ static int acpi_device_install_notify_handler(struct acpi_device *device) static void acpi_device_remove_notify_handler(struct acpi_device *device) { - if (!strcmp(acpi_device_hid(device), ACPI_BUTTON_HID_POWERF)) + if (device->device_type == ACPI_BUS_TYPE_POWER_BUTTON) acpi_remove_fixed_event_handler(ACPI_EVENT_POWER_BUTTON, acpi_device_notify_fixed); - else if (!strcmp(acpi_device_hid(device), ACPI_BUTTON_HID_SLEEPF)) + else if (device->device_type == ACPI_BUS_TYPE_SLEEP_BUTTON) acpi_remove_fixed_event_handler(ACPI_EVENT_SLEEP_BUTTON, acpi_device_notify_fixed); else @@ -474,12 +437,12 @@ struct bus_type acpi_bus_type = { .uevent = acpi_device_uevent, }; -static int acpi_device_register(struct acpi_device *device, - struct acpi_device *parent) +static int acpi_device_register(struct acpi_device *device) { int result; struct acpi_device_bus_id *acpi_device_bus_id, *new_bus_id; int found = 0; + /* * Linkage * ------- @@ -501,8 +464,9 @@ static int acpi_device_register(struct acpi_device *device, * If failed, create one and link it into acpi_bus_id_list */ list_for_each_entry(acpi_device_bus_id, &acpi_bus_id_list, node) { - if(!strcmp(acpi_device_bus_id->bus_id, device->flags.hardware_id? device->pnp.hardware_id : "device")) { - acpi_device_bus_id->instance_no ++; + if (!strcmp(acpi_device_bus_id->bus_id, + acpi_device_hid(device))) { + acpi_device_bus_id->instance_no++; found = 1; kfree(new_bus_id); break; @@ -510,7 +474,7 @@ static int acpi_device_register(struct acpi_device *device, } if (!found) { acpi_device_bus_id = new_bus_id; - strcpy(acpi_device_bus_id->bus_id, device->flags.hardware_id ? device->pnp.hardware_id : "device"); + strcpy(acpi_device_bus_id->bus_id, acpi_device_hid(device)); acpi_device_bus_id->instance_no = 0; list_add_tail(&acpi_device_bus_id->node, &acpi_bus_id_list); } @@ -524,7 +488,7 @@ static int acpi_device_register(struct acpi_device *device, mutex_unlock(&acpi_device_lock); if (device->parent) - device->dev.parent = &parent->dev; + device->dev.parent = &device->parent->dev; device->dev.bus = &acpi_bus_type; device->dev.release = &acpi_device_release; result = device_register(&device->dev); @@ -664,6 +628,33 @@ EXPORT_SYMBOL(acpi_bus_unregister_driver); /* -------------------------------------------------------------------------- Device Enumeration -------------------------------------------------------------------------- */ +static struct acpi_device *acpi_bus_get_parent(acpi_handle handle) +{ + acpi_status status; + int ret; + struct acpi_device *device; + + /* + * Fixed hardware devices do not appear in the namespace and do not + * have handles, but we fabricate acpi_devices for them, so we have + * to deal with them specially. + */ + if (handle == NULL) + return acpi_root; + + do { + status = acpi_get_parent(handle, &handle); + if (status == AE_NULL_ENTRY) + return NULL; + if (ACPI_FAILURE(status)) + return acpi_root; + + ret = acpi_bus_get_device(handle, &device); + if (ret == 0) + return device; + } while (1); +} + acpi_status acpi_bus_get_ejd(acpi_handle handle, acpi_handle *ejd) { @@ -876,11 +867,6 @@ static int acpi_bus_get_flags(struct acpi_device *device) if (ACPI_SUCCESS(status)) device->flags.dynamic_status = 1; - /* Presence of _CID indicates 'compatible_ids' */ - status = acpi_get_handle(device->handle, "_CID", &temp); - if (ACPI_SUCCESS(status)) - device->flags.compatible_ids = 1; - /* Presence of _RMV indicates 'removable' */ status = acpi_get_handle(device->handle, "_RMV", &temp); if (ACPI_SUCCESS(status)) @@ -918,8 +904,7 @@ static int acpi_bus_get_flags(struct acpi_device *device) return 0; } -static void acpi_device_get_busid(struct acpi_device *device, - acpi_handle handle, int type) +static void acpi_device_get_busid(struct acpi_device *device) { char bus_id[5] = { '?', 0 }; struct acpi_buffer buffer = { sizeof(bus_id), bus_id }; @@ -931,10 +916,12 @@ static void acpi_device_get_busid(struct acpi_device *device, * The device's Bus ID is simply the object name. * TBD: Shouldn't this value be unique (within the ACPI namespace)? */ - switch (type) { - case ACPI_BUS_TYPE_SYSTEM: + if (ACPI_IS_ROOT_DEVICE(device)) { strcpy(device->pnp.bus_id, "ACPI"); - break; + return; + } + + switch (device->device_type) { case ACPI_BUS_TYPE_POWER_BUTTON: strcpy(device->pnp.bus_id, "PWRF"); break; @@ -942,7 +929,7 @@ static void acpi_device_get_busid(struct acpi_device *device, strcpy(device->pnp.bus_id, "SLPF"); break; default: - acpi_get_name(handle, ACPI_SINGLE_NAME, &buffer); + acpi_get_name(device->handle, ACPI_SINGLE_NAME, &buffer); /* Clean up trailing underscores (if any) */ for (i = 3; i > 1; i--) { if (bus_id[i] == '_') @@ -1000,204 +987,134 @@ static int acpi_dock_match(struct acpi_device *device) return acpi_get_handle(device->handle, "_DCK", &tmp); } -static struct acpica_device_id_list* -acpi_add_cid( - struct acpi_device_info *info, - struct acpica_device_id *new_cid) +char *acpi_device_hid(struct acpi_device *device) { - struct acpica_device_id_list *cid; - char *next_id_string; - acpi_size cid_length; - acpi_size new_cid_length; - u32 i; - - - /* Allocate new CID list with room for the new CID */ - - if (!new_cid) - new_cid_length = info->compatible_id_list.list_size; - else if (info->compatible_id_list.list_size) - new_cid_length = info->compatible_id_list.list_size + - new_cid->length + sizeof(struct acpica_device_id); - else - new_cid_length = sizeof(struct acpica_device_id_list) + new_cid->length; - - cid = ACPI_ALLOCATE_ZEROED(new_cid_length); - if (!cid) { - return NULL; - } - - cid->list_size = new_cid_length; - cid->count = info->compatible_id_list.count; - if (new_cid) - cid->count++; - next_id_string = (char *) cid->ids + (cid->count * sizeof(struct acpica_device_id)); - - /* Copy all existing CIDs */ - - for (i = 0; i < info->compatible_id_list.count; i++) { - cid_length = info->compatible_id_list.ids[i].length; - cid->ids[i].string = next_id_string; - cid->ids[i].length = cid_length; + struct acpi_hardware_id *hid; - ACPI_MEMCPY(next_id_string, info->compatible_id_list.ids[i].string, - cid_length); - - next_id_string += cid_length; - } + hid = list_first_entry(&device->pnp.ids, struct acpi_hardware_id, list); + return hid->id; +} +EXPORT_SYMBOL(acpi_device_hid); - /* Append the new CID */ +static void acpi_add_id(struct acpi_device *device, const char *dev_id) +{ + struct acpi_hardware_id *id; - if (new_cid) { - cid->ids[i].string = next_id_string; - cid->ids[i].length = new_cid->length; + id = kmalloc(sizeof(*id), GFP_KERNEL); + if (!id) + return; - ACPI_MEMCPY(next_id_string, new_cid->string, new_cid->length); + id->id = kmalloc(strlen(dev_id) + 1, GFP_KERNEL); + if (!id->id) { + kfree(id); + return; } - return cid; + strcpy(id->id, dev_id); + list_add_tail(&id->list, &device->pnp.ids); } -static void acpi_device_set_id(struct acpi_device *device, - struct acpi_device *parent, acpi_handle handle, - int type) +static void acpi_device_set_id(struct acpi_device *device) { - struct acpi_device_info *info = NULL; - char *hid = NULL; - char *uid = NULL; - struct acpica_device_id_list *cid_list = NULL; - char *cid_add = NULL; acpi_status status; + struct acpi_device_info *info; + struct acpica_device_id_list *cid_list; + int i; - switch (type) { + switch (device->device_type) { case ACPI_BUS_TYPE_DEVICE: - status = acpi_get_object_info(handle, &info); + if (ACPI_IS_ROOT_DEVICE(device)) { + acpi_add_id(device, ACPI_SYSTEM_HID); + break; + } else if (ACPI_IS_ROOT_DEVICE(device->parent)) { + /* \_SB_, the only root-level namespace device */ + acpi_add_id(device, ACPI_BUS_HID); + strcpy(device->pnp.device_name, ACPI_BUS_DEVICE_NAME); + strcpy(device->pnp.device_class, ACPI_BUS_CLASS); + break; + } + + status = acpi_get_object_info(device->handle, &info); if (ACPI_FAILURE(status)) { printk(KERN_ERR PREFIX "%s: Error reading device info\n", __func__); return; } if (info->valid & ACPI_VALID_HID) - hid = info->hardware_id.string; - if (info->valid & ACPI_VALID_UID) - uid = info->unique_id.string; - if (info->valid & ACPI_VALID_CID) + acpi_add_id(device, info->hardware_id.string); + if (info->valid & ACPI_VALID_CID) { cid_list = &info->compatible_id_list; + for (i = 0; i < cid_list->count; i++) + acpi_add_id(device, cid_list->ids[i].string); + } if (info->valid & ACPI_VALID_ADR) { device->pnp.bus_address = info->address; device->flags.bus_address = 1; } - /* If we have a video/bay/dock device, add our selfdefined - HID to the CID list. Like that the video/bay/dock drivers - will get autoloaded and the device might still match - against another driver. - */ + kfree(info); + + /* + * Some devices don't reliably have _HIDs & _CIDs, so add + * synthetic HIDs to make sure drivers can find them. + */ if (acpi_is_video_device(device)) - cid_add = ACPI_VIDEO_HID; + acpi_add_id(device, ACPI_VIDEO_HID); else if (ACPI_SUCCESS(acpi_bay_match(device))) - cid_add = ACPI_BAY_HID; + acpi_add_id(device, ACPI_BAY_HID); else if (ACPI_SUCCESS(acpi_dock_match(device))) - cid_add = ACPI_DOCK_HID; + acpi_add_id(device, ACPI_DOCK_HID); break; case ACPI_BUS_TYPE_POWER: - hid = ACPI_POWER_HID; + acpi_add_id(device, ACPI_POWER_HID); break; case ACPI_BUS_TYPE_PROCESSOR: - hid = ACPI_PROCESSOR_OBJECT_HID; - break; - case ACPI_BUS_TYPE_SYSTEM: - hid = ACPI_SYSTEM_HID; + acpi_add_id(device, ACPI_PROCESSOR_OBJECT_HID); break; case ACPI_BUS_TYPE_THERMAL: - hid = ACPI_THERMAL_HID; + acpi_add_id(device, ACPI_THERMAL_HID); break; case ACPI_BUS_TYPE_POWER_BUTTON: - hid = ACPI_BUTTON_HID_POWERF; + acpi_add_id(device, ACPI_BUTTON_HID_POWERF); break; case ACPI_BUS_TYPE_SLEEP_BUTTON: - hid = ACPI_BUTTON_HID_SLEEPF; + acpi_add_id(device, ACPI_BUTTON_HID_SLEEPF); break; } /* - * \_SB - * ---- - * Fix for the system root bus device -- the only root-level device. + * We build acpi_devices for some objects that don't have _HID or _CID, + * e.g., PCI bridges and slots. Drivers can't bind to these objects, + * but we do use them indirectly by traversing the acpi_device tree. + * This generic ID isn't useful for driver binding, but it provides + * the useful property that "every acpi_device has an ID." */ - if (((acpi_handle)parent == ACPI_ROOT_OBJECT) && (type == ACPI_BUS_TYPE_DEVICE)) { - hid = ACPI_BUS_HID; - strcpy(device->pnp.device_name, ACPI_BUS_DEVICE_NAME); - strcpy(device->pnp.device_class, ACPI_BUS_CLASS); - } - - if (hid) { - device->pnp.hardware_id = ACPI_ALLOCATE_ZEROED(strlen (hid) + 1); - if (device->pnp.hardware_id) { - strcpy(device->pnp.hardware_id, hid); - device->flags.hardware_id = 1; - } - } - if (!device->flags.hardware_id) - device->pnp.hardware_id = ""; - - if (uid) { - device->pnp.unique_id = ACPI_ALLOCATE_ZEROED(strlen (uid) + 1); - if (device->pnp.unique_id) { - strcpy(device->pnp.unique_id, uid); - device->flags.unique_id = 1; - } - } - if (!device->flags.unique_id) - device->pnp.unique_id = ""; - - if (cid_list || cid_add) { - struct acpica_device_id_list *list; - - if (cid_add) { - struct acpica_device_id cid; - cid.length = strlen (cid_add) + 1; - cid.string = cid_add; - - list = acpi_add_cid(info, &cid); - } else { - list = acpi_add_cid(info, NULL); - } - - if (list) { - device->pnp.cid_list = list; - if (cid_add) - device->flags.compatible_ids = 1; - } - } - - kfree(info); + if (list_empty(&device->pnp.ids)) + acpi_add_id(device, "device"); } -static int acpi_device_set_context(struct acpi_device *device, int type) +static int acpi_device_set_context(struct acpi_device *device) { - acpi_status status = AE_OK; - int result = 0; + acpi_status status; + /* * Context * ------- * Attach this 'struct acpi_device' to the ACPI object. This makes - * resolutions from handle->device very efficient. Note that we need - * to be careful with fixed-feature devices as they all attach to the - * root object. + * resolutions from handle->device very efficient. Fixed hardware + * devices have no handles, so we skip them. */ - if (type != ACPI_BUS_TYPE_POWER_BUTTON && - type != ACPI_BUS_TYPE_SLEEP_BUTTON) { - status = acpi_attach_data(device->handle, - acpi_bus_data_handler, device); + if (!device->handle) + return 0; - if (ACPI_FAILURE(status)) { - printk(KERN_ERR PREFIX "Error attaching device data\n"); - result = -ENODEV; - } - } - return result; + status = acpi_attach_data(device->handle, + acpi_bus_data_handler, device); + if (ACPI_SUCCESS(status)) + return 0; + + printk(KERN_ERR PREFIX "Error attaching device data\n"); + return -ENODEV; } static int acpi_bus_remove(struct acpi_device *dev, int rmdevice) @@ -1223,17 +1140,14 @@ static int acpi_bus_remove(struct acpi_device *dev, int rmdevice) return 0; } -static int -acpi_add_single_object(struct acpi_device **child, - struct acpi_device *parent, acpi_handle handle, int type, - struct acpi_bus_ops *ops) +static int acpi_add_single_object(struct acpi_device **child, + acpi_handle handle, int type, + unsigned long long sta, + struct acpi_bus_ops *ops) { - int result = 0; - struct acpi_device *device = NULL; - - - if (!child) - return -EINVAL; + int result; + struct acpi_device *device; + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; device = kzalloc(sizeof(struct acpi_device), GFP_KERNEL); if (!device) { @@ -1241,75 +1155,31 @@ acpi_add_single_object(struct acpi_device **child, return -ENOMEM; } + INIT_LIST_HEAD(&device->pnp.ids); + device->device_type = type; device->handle = handle; - device->parent = parent; + device->parent = acpi_bus_get_parent(handle); device->bus_ops = *ops; /* workround for not call .start */ + STRUCT_TO_INT(device->status) = sta; - - acpi_device_get_busid(device, handle, type); + acpi_device_get_busid(device); /* * Flags * ----- - * Get prior to calling acpi_bus_get_status() so we know whether - * or not _STA is present. Note that we only look for object - * handles -- cannot evaluate objects until we know the device is - * present and properly initialized. + * Note that we only look for object handles -- cannot evaluate objects + * until we know the device is present and properly initialized. */ result = acpi_bus_get_flags(device); if (result) goto end; /* - * Status - * ------ - * See if the device is present. We always assume that non-Device - * and non-Processor objects (e.g. thermal zones, power resources, - * etc.) are present, functioning, etc. (at least when parent object - * is present). Note that _STA has a different meaning for some - * objects (e.g. power resources) so we need to be careful how we use - * it. - */ - switch (type) { - case ACPI_BUS_TYPE_PROCESSOR: - case ACPI_BUS_TYPE_DEVICE: - result = acpi_bus_get_status(device); - if (ACPI_FAILURE(result)) { - result = -ENODEV; - goto end; - } - /* - * When the device is neither present nor functional, the - * device should not be added to Linux ACPI device tree. - * When the status of the device is not present but functinal, - * it should be added to Linux ACPI tree. For example : bay - * device , dock device. - * In such conditions it is unncessary to check whether it is - * bay device or dock device. - */ - if (!device->status.present && !device->status.functional) { - result = -ENODEV; - goto end; - } - break; - default: - STRUCT_TO_INT(device->status) = - ACPI_STA_DEVICE_PRESENT | ACPI_STA_DEVICE_ENABLED | - ACPI_STA_DEVICE_UI | ACPI_STA_DEVICE_FUNCTIONING; - break; - } - - /* * Initialize Device * ----------------- * TBD: Synch with Core's enumeration/initialization process. */ - - /* - * Hardware ID, Unique ID, & Bus Address - * ------------------------------------- - */ - acpi_device_set_id(device, parent, handle, type); + acpi_device_set_id(device); /* * Power Management @@ -1341,10 +1211,10 @@ acpi_add_single_object(struct acpi_device **child, goto end; } - if ((result = acpi_device_set_context(device, type))) + if ((result = acpi_device_set_context(device))) goto end; - result = acpi_device_register(device, parent); + result = acpi_device_register(device); /* * Bind _ADR-Based Devices when hot add @@ -1355,128 +1225,117 @@ acpi_add_single_object(struct acpi_device **child, } end: - if (!result) + if (!result) { + acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer); + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Adding %s [%s] parent %s\n", dev_name(&device->dev), + (char *) buffer.pointer, + device->parent ? dev_name(&device->parent->dev) : + "(null)")); + kfree(buffer.pointer); *child = device; - else + } else acpi_device_release(&device->dev); return result; } -static int acpi_bus_scan(struct acpi_device *start, struct acpi_bus_ops *ops) -{ - acpi_status status = AE_OK; - struct acpi_device *parent = NULL; - struct acpi_device *child = NULL; - acpi_handle phandle = NULL; - acpi_handle chandle = NULL; - acpi_object_type type = 0; - u32 level = 1; - +#define ACPI_STA_DEFAULT (ACPI_STA_DEVICE_PRESENT | ACPI_STA_DEVICE_ENABLED | \ + ACPI_STA_DEVICE_UI | ACPI_STA_DEVICE_FUNCTIONING) - if (!start) - return -EINVAL; +static int acpi_bus_type_and_status(acpi_handle handle, int *type, + unsigned long long *sta) +{ + acpi_status status; + acpi_object_type acpi_type; - parent = start; - phandle = start->handle; + status = acpi_get_type(handle, &acpi_type); + if (ACPI_FAILURE(status)) + return -ENODEV; - /* - * Parse through the ACPI namespace, identify all 'devices', and - * create a new 'struct acpi_device' for each. - */ - while ((level > 0) && parent) { + switch (acpi_type) { + case ACPI_TYPE_ANY: /* for ACPI_ROOT_OBJECT */ + case ACPI_TYPE_DEVICE: + *type = ACPI_BUS_TYPE_DEVICE; + status = acpi_bus_get_status_handle(handle, sta); + if (ACPI_FAILURE(status)) + return -ENODEV; + break; + case ACPI_TYPE_PROCESSOR: + *type = ACPI_BUS_TYPE_PROCESSOR; + status = acpi_bus_get_status_handle(handle, sta); + if (ACPI_FAILURE(status)) + return -ENODEV; + break; + case ACPI_TYPE_THERMAL: + *type = ACPI_BUS_TYPE_THERMAL; + *sta = ACPI_STA_DEFAULT; + break; + case ACPI_TYPE_POWER: + *type = ACPI_BUS_TYPE_POWER; + *sta = ACPI_STA_DEFAULT; + break; + default: + return -ENODEV; + } - status = acpi_get_next_object(ACPI_TYPE_ANY, phandle, - chandle, &chandle); + return 0; +} - /* - * If this scope is exhausted then move our way back up. - */ - if (ACPI_FAILURE(status)) { - level--; - chandle = phandle; - acpi_get_parent(phandle, &phandle); - if (parent->parent) - parent = parent->parent; - continue; - } +static acpi_status acpi_bus_check_add(acpi_handle handle, u32 lvl, + void *context, void **return_value) +{ + struct acpi_bus_ops *ops = context; + int type; + unsigned long long sta; + struct acpi_device *device; + acpi_status status; + int result; - status = acpi_get_type(chandle, &type); - if (ACPI_FAILURE(status)) - continue; + result = acpi_bus_type_and_status(handle, &type, &sta); + if (result) + return AE_OK; - /* - * If this is a scope object then parse it (depth-first). - */ - if (type == ACPI_TYPE_LOCAL_SCOPE) { - level++; - phandle = chandle; - chandle = NULL; - continue; - } + if (!(sta & ACPI_STA_DEVICE_PRESENT) && + !(sta & ACPI_STA_DEVICE_FUNCTIONING)) + return AE_CTRL_DEPTH; - /* - * We're only interested in objects that we consider 'devices'. - */ - switch (type) { - case ACPI_TYPE_DEVICE: - type = ACPI_BUS_TYPE_DEVICE; - break; - case ACPI_TYPE_PROCESSOR: - type = ACPI_BUS_TYPE_PROCESSOR; - break; - case ACPI_TYPE_THERMAL: - type = ACPI_BUS_TYPE_THERMAL; - break; - case ACPI_TYPE_POWER: - type = ACPI_BUS_TYPE_POWER; - break; - default: - continue; - } + /* + * We may already have an acpi_device from a previous enumeration. If + * so, we needn't add it again, but we may still have to start it. + */ + device = NULL; + acpi_bus_get_device(handle, &device); + if (ops->acpi_op_add && !device) + acpi_add_single_object(&device, handle, type, sta, ops); - if (ops->acpi_op_add) - status = acpi_add_single_object(&child, parent, - chandle, type, ops); - else - status = acpi_bus_get_device(chandle, &child); + if (!device) + return AE_CTRL_DEPTH; + if (ops->acpi_op_start && !(ops->acpi_op_add)) { + status = acpi_start_single_object(device); if (ACPI_FAILURE(status)) - continue; + return AE_CTRL_DEPTH; + } - if (ops->acpi_op_start && !(ops->acpi_op_add)) { - status = acpi_start_single_object(child); - if (ACPI_FAILURE(status)) - continue; - } + if (!*return_value) + *return_value = device; + return AE_OK; +} - /* - * If the device is present, enabled, and functioning then - * parse its scope (depth-first). Note that we need to - * represent absent devices to facilitate PnP notifications - * -- but only the subtree head (not all of its children, - * which will be enumerated when the parent is inserted). - * - * TBD: Need notifications and other detection mechanisms - * in place before we can fully implement this. - */ - /* - * When the device is not present but functional, it is also - * necessary to scan the children of this device. - */ - if (child->status.present || (!child->status.present && - child->status.functional)) { - status = acpi_get_next_object(ACPI_TYPE_ANY, chandle, - NULL, NULL); - if (ACPI_SUCCESS(status)) { - level++; - phandle = chandle; - chandle = NULL; - parent = child; - } - } - } +static int acpi_bus_scan(acpi_handle handle, struct acpi_bus_ops *ops, + struct acpi_device **child) +{ + acpi_status status; + void *device = NULL; + + status = acpi_bus_check_add(handle, 0, ops, &device); + if (ACPI_SUCCESS(status)) + acpi_walk_namespace(ACPI_TYPE_ANY, handle, ACPI_UINT32_MAX, + acpi_bus_check_add, ops, &device); + if (child) + *child = device; return 0; } @@ -1484,36 +1343,25 @@ int acpi_bus_add(struct acpi_device **child, struct acpi_device *parent, acpi_handle handle, int type) { - int result; struct acpi_bus_ops ops; memset(&ops, 0, sizeof(ops)); ops.acpi_op_add = 1; - result = acpi_add_single_object(child, parent, handle, type, &ops); - if (!result) - result = acpi_bus_scan(*child, &ops); - - return result; + acpi_bus_scan(handle, &ops, child); + return 0; } EXPORT_SYMBOL(acpi_bus_add); int acpi_bus_start(struct acpi_device *device) { - int result; struct acpi_bus_ops ops; + memset(&ops, 0, sizeof(ops)); + ops.acpi_op_start = 1; - if (!device) - return -EINVAL; - - result = acpi_start_single_object(device); - if (!result) { - memset(&ops, 0, sizeof(ops)); - ops.acpi_op_start = 1; - result = acpi_bus_scan(device, &ops); - } - return result; + acpi_bus_scan(device->handle, &ops, NULL); + return 0; } EXPORT_SYMBOL(acpi_bus_start); @@ -1572,15 +1420,12 @@ int acpi_bus_trim(struct acpi_device *start, int rmdevice) } EXPORT_SYMBOL_GPL(acpi_bus_trim); -static int acpi_bus_scan_fixed(struct acpi_device *root) +static int acpi_bus_scan_fixed(void) { int result = 0; struct acpi_device *device = NULL; struct acpi_bus_ops ops; - if (!root) - return -ENODEV; - memset(&ops, 0, sizeof(ops)); ops.acpi_op_add = 1; ops.acpi_op_start = 1; @@ -1589,16 +1434,16 @@ static int acpi_bus_scan_fixed(struct acpi_device *root) * Enumerate all fixed-feature devices. */ if ((acpi_gbl_FADT.flags & ACPI_FADT_POWER_BUTTON) == 0) { - result = acpi_add_single_object(&device, acpi_root, - NULL, + result = acpi_add_single_object(&device, NULL, ACPI_BUS_TYPE_POWER_BUTTON, + ACPI_STA_DEFAULT, &ops); } if ((acpi_gbl_FADT.flags & ACPI_FADT_SLEEP_BUTTON) == 0) { - result = acpi_add_single_object(&device, acpi_root, - NULL, + result = acpi_add_single_object(&device, NULL, ACPI_BUS_TYPE_SLEEP_BUTTON, + ACPI_STA_DEFAULT, &ops); } @@ -1621,24 +1466,15 @@ int __init acpi_scan_init(void) } /* - * Create the root device in the bus's device tree - */ - result = acpi_add_single_object(&acpi_root, NULL, ACPI_ROOT_OBJECT, - ACPI_BUS_TYPE_SYSTEM, &ops); - if (result) - goto Done; - - /* * Enumerate devices in the ACPI namespace. */ - result = acpi_bus_scan_fixed(acpi_root); + result = acpi_bus_scan(ACPI_ROOT_OBJECT, &ops, &acpi_root); if (!result) - result = acpi_bus_scan(acpi_root, &ops); + result = acpi_bus_scan_fixed(); if (result) acpi_device_unregister(acpi_root, ACPI_BUS_REMOVAL_NORMAL); -Done: return result; } diff --git a/drivers/acpi/video.c b/drivers/acpi/video.c index 94b1a4c5abab..f6e54bf8dd96 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, @@ -1986,6 +1986,10 @@ acpi_video_switch_brightness(struct acpi_video_device *device, int event) result = acpi_video_device_lcd_set_level(device, level_next); + if (!result) + backlight_force_update(device->backlight, + BACKLIGHT_UPDATE_HOTKEY); + out: if (result) printk(KERN_ERR PREFIX "Failed to switch the brightness\n"); |