unsigned long acpi_wakeup_address = 0;
++#ifdef CONFIG_IA64_GENERIC
++static unsigned long __init acpi_find_rsdp(void)
++{
++ unsigned long rsdp_phys = 0;
++
++ if (efi.acpi20 != EFI_INVALID_TABLE_ADDR)
++ rsdp_phys = efi.acpi20;
++ else if (efi.acpi != EFI_INVALID_TABLE_ADDR)
++ printk(KERN_WARNING PREFIX
++ "v1.0/r0.71 tables no longer supported\n");
++ return rsdp_phys;
++}
++#endif
++
const char __init *
acpi_get_sysname(void)
{
return vector;
}
- -char *__acpi_map_table(unsigned long phys_addr, unsigned long size)
+ +char *__init __acpi_map_table(unsigned long phys_addr, unsigned long size)
{
return __va(phys_addr);
}
return 0;
}
--unsigned long __init acpi_find_rsdp(void)
--{
-- unsigned long rsdp_phys = 0;
--
-- if (efi.acpi20 != EFI_INVALID_TABLE_ADDR)
-- rsdp_phys = efi.acpi20;
-- else if (efi.acpi != EFI_INVALID_TABLE_ADDR)
-- printk(KERN_WARNING PREFIX
-- "v1.0/r0.71 tables no longer supported\n");
-- return rsdp_phys;
--}
--
int __init acpi_boot_init(void)
{
#ifdef CONFIG_X86_64
/* rely on all ACPI tables being in the direct mapping */
- -char *__acpi_map_table(unsigned long phys_addr, unsigned long size)
+ +char *__init __acpi_map_table(unsigned long phys_addr, unsigned long size)
{
if (!phys_addr || !size)
return NULL;
* from the fixed base. That's why we start at FIX_IO_APIC_BASE_END and
* count idx down while incrementing the phys address.
*/
- -char *__acpi_map_table(unsigned long phys, unsigned long size)
+ +char *__init __acpi_map_table(unsigned long phys, unsigned long size)
{
unsigned long base, offset, mapped_size;
int idx;
* ACPI based hotplug support for CPU
*/
#ifdef CONFIG_ACPI_HOTPLUG_CPU
- int acpi_map_lsapic(acpi_handle handle, int *pcpu)
+
+ static int __cpuinit _acpi_map_lsapic(acpi_handle handle, int *pcpu)
{
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
union acpi_object *obj;
return 0;
}
+ /* wrapper to silence section mismatch warning */
+ int __ref acpi_map_lsapic(acpi_handle handle, int *pcpu)
+ {
+ return _acpi_map_lsapic(handle, pcpu);
+ }
EXPORT_SYMBOL(acpi_map_lsapic);
int acpi_unmap_lsapic(int cpu)
EXPORT_SYMBOL(acpi_unregister_ioapic);
--static unsigned long __init
--acpi_scan_rsdp(unsigned long start, unsigned long length)
--{
-- unsigned long offset = 0;
-- unsigned long sig_len = sizeof("RSD PTR ") - 1;
--
-- /*
-- * Scan all 16-byte boundaries of the physical memory region for the
-- * RSDP signature.
-- */
-- for (offset = 0; offset < length; offset += 16) {
-- if (strncmp((char *)(phys_to_virt(start) + offset), "RSD PTR ", sig_len))
-- continue;
-- return (start + offset);
-- }
--
-- return 0;
--}
--
static int __init acpi_parse_sbf(struct acpi_table_header *table)
{
struct acpi_table_boot *sb;
return 0;
}
--unsigned long __init acpi_find_rsdp(void)
--{
-- unsigned long rsdp_phys = 0;
--
-- if (efi_enabled) {
-- if (efi.acpi20 != EFI_INVALID_TABLE_ADDR)
-- return efi.acpi20;
-- else if (efi.acpi != EFI_INVALID_TABLE_ADDR)
-- return efi.acpi;
-- }
-- /*
-- * Scan memory looking for the RSDP signature. First search EBDA (low
-- * memory) paragraphs and then search upper memory (E0000-FFFFF).
-- */
-- rsdp_phys = acpi_scan_rsdp(0, 0x400);
-- if (!rsdp_phys)
-- rsdp_phys = acpi_scan_rsdp(0xE0000, 0x20000);
--
-- return rsdp_phys;
--}
--
#ifdef CONFIG_X86_LOCAL_APIC
/*
* Parse LAPIC entries in MADT
#define OSI_STRING_LENGTH_MAX 64 /* arbitrary */
static char osi_additional_string[OSI_STRING_LENGTH_MAX];
- static int osi_linux; /* disable _OSI(Linux) by default */
+ /*
+ * "Ode to _OSI(Linux)"
+ *
+ * osi_linux -- Control response to BIOS _OSI(Linux) query.
+ *
+ * As Linux evolves, the features that it supports change.
+ * So an OSI string such as "Linux" is not specific enough
+ * to be useful across multiple versions of Linux. It
+ * doesn't identify any particular feature, interface,
+ * or even any particular version of Linux...
+ *
+ * Unfortunately, Linux-2.6.22 and earlier responded "yes"
+ * to a BIOS _OSI(Linux) query. When
+ * a reference mobile BIOS started using it, its use
+ * started to spread to many vendor platforms.
+ * As it is not supportable, we need to halt that spread.
+ *
+ * Today, most BIOS references to _OSI(Linux) are noise --
+ * they have no functional effect and are just dead code
+ * carried over from the reference BIOS.
+ *
+ * The next most common case is that _OSI(Linux) harms Linux,
+ * usually by causing the BIOS to follow paths that are
+ * not tested during Windows validation.
+ *
+ * Finally, there is a short list of platforms
+ * where OSI(Linux) benefits Linux.
+ *
+ * In Linux-2.6.23, OSI(Linux) is first disabled by default.
+ * DMI is used to disable the dmesg warning about OSI(Linux)
+ * on platforms where it is known to have no effect.
+ * But a dmesg warning remains for systems where
+ * we do not know if OSI(Linux) is good or bad for the system.
+ * DMI is also used to enable OSI(Linux) for the machines
+ * that are known to need it.
+ *
+ * BIOS writers should NOT query _OSI(Linux) on future systems.
+ * It will be ignored by default, and to get Linux to
+ * not ignore it will require a kernel source update to
+ * add a DMI entry, or a boot-time "acpi_osi=Linux" invocation.
+ */
+ #define OSI_LINUX_ENABLE 0
- #ifdef CONFIG_DMI
- static struct __initdata dmi_system_id acpi_osl_dmi_table[];
- #endif
-struct osi_linux {
++static struct osi_linux {
+ unsigned int enable:1;
+ unsigned int dmi:1;
+ unsigned int cmdline:1;
+ unsigned int known:1;
+ } osi_linux = { OSI_LINUX_ENABLE, 0, 0, 0};
static void __init acpi_request_region (struct acpi_generic_address *addr,
unsigned int length, char *desc)
acpi_status __init acpi_os_initialize(void)
{
- dmi_check_system(acpi_osl_dmi_table);
return AE_OK;
}
"System description tables not found\n");
return 0;
}
-- } else
-- return acpi_find_rsdp();
++ } else {
++ acpi_physical_address pa = 0;
++
++ acpi_find_root_pointer(&pa);
++ return pa;
++ }
}
- -void __iomem *acpi_os_map_memory(acpi_physical_address phys, acpi_size size)
+ +void __iomem *__init_refok
+ +acpi_os_map_memory(acpi_physical_address phys, acpi_size size)
{
if (phys > ULONG_MAX) {
printk(KERN_ERR PREFIX "Cannot map memory that high\n");
dpc->function(dpc->context);
kfree(dpc);
-- /* Yield cpu to notify thread */
-- cond_resched();
--
-- return;
--}
--
--static void acpi_os_execute_notify(struct work_struct *work)
--{
-- struct acpi_os_dpc *dpc = container_of(work, struct acpi_os_dpc, work);
--
-- if (!dpc) {
-- printk(KERN_ERR PREFIX "Invalid (NULL) context\n");
-- return;
-- }
--
-- dpc->function(dpc->context);
--
-- kfree(dpc);
--
return;
}
{
acpi_status status = AE_OK;
struct acpi_os_dpc *dpc;
--
++ struct workqueue_struct *queue;
ACPI_DEBUG_PRINT((ACPI_DB_EXEC,
"Scheduling function [%p(%p)] for deferred execution.\n",
function, context));
dpc->function = function;
dpc->context = context;
-- if (type == OSL_NOTIFY_HANDLER) {
-- INIT_WORK(&dpc->work, acpi_os_execute_notify);
-- if (!queue_work(kacpi_notify_wq, &dpc->work)) {
-- status = AE_ERROR;
-- kfree(dpc);
-- }
-- } else {
-- INIT_WORK(&dpc->work, acpi_os_execute_deferred);
-- if (!queue_work(kacpid_wq, &dpc->work)) {
-- ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-- "Call to queue_work() failed.\n"));
-- status = AE_ERROR;
-- kfree(dpc);
-- }
++ INIT_WORK(&dpc->work, acpi_os_execute_deferred);
++ queue = (type == OSL_NOTIFY_HANDLER) ? kacpi_notify_wq : kacpid_wq;
++ if (!queue_work(queue, &dpc->work)) {
++ ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
++ "Call to queue_work() failed.\n"));
++ status = AE_ERROR;
++ kfree(dpc);
}
return_ACPI_STATUS(status);
}
__setup("acpi_os_name=", acpi_os_name_setup);
- static void enable_osi_linux(int enable) {
+ static void __init set_osi_linux(unsigned int enable)
+ {
+ if (osi_linux.enable != enable) {
+ osi_linux.enable = enable;
+ printk(KERN_NOTICE PREFIX "%sed _OSI(Linux)\n",
+ enable ? "Add": "Delet");
+ }
+ return;
+ }
+
+ static void __init acpi_cmdline_osi_linux(unsigned int enable)
+ {
+ osi_linux.cmdline = 1; /* cmdline set the default */
+ set_osi_linux(enable);
+
+ return;
+ }
+
+ void __init acpi_dmi_osi_linux(int enable, const struct dmi_system_id *d)
+ {
+ osi_linux.dmi = 1; /* DMI knows that this box asks OSI(Linux) */
+
+ printk(KERN_NOTICE PREFIX "DMI detected: %s\n", d->ident);
+
+ if (enable == -1)
+ return;
+
+ osi_linux.known = 1; /* DMI knows which OSI(Linux) default needed */
- if (osi_linux != enable)
- printk(KERN_INFO PREFIX "%sabled _OSI(Linux)\n",
- enable ? "En": "Dis");
+ set_osi_linux(enable);
- osi_linux = enable;
return;
}
printk(KERN_INFO PREFIX "_OSI method disabled\n");
acpi_gbl_create_osi_method = FALSE;
} else if (!strcmp("!Linux", str)) {
- enable_osi_linux(0);
+ acpi_cmdline_osi_linux(0); /* !enable */
} else if (*str == '!') {
if (acpi_osi_invalidate(++str) == AE_OK)
printk(KERN_INFO PREFIX "Deleted _OSI(%s)\n", str);
} else if (!strcmp("Linux", str)) {
- enable_osi_linux(1);
+ acpi_cmdline_osi_linux(1); /* enable */
} else if (*osi_additional_string == '\0') {
strncpy(osi_additional_string, str, OSI_STRING_LENGTH_MAX);
printk(KERN_INFO PREFIX "Added _OSI(%s)\n", str);
return (AE_OK);
}
-int acpi_dmi_dump(void)
+ /**
+ * acpi_dmi_dump - dump DMI slots needed for blacklist entry
+ *
+ * Returns 0 on success
+ */
- dmi_get_slot(DMI_SYS_VENDOR));
++static int acpi_dmi_dump(void)
+ {
+
+ if (!dmi_available)
+ return -1;
+
+ printk(KERN_NOTICE PREFIX "DMI System Vendor: %s\n",
- dmi_get_slot(DMI_PRODUCT_NAME));
++ dmi_get_system_info(DMI_SYS_VENDOR));
+ printk(KERN_NOTICE PREFIX "DMI Product Name: %s\n",
- dmi_get_slot(DMI_PRODUCT_VERSION));
++ dmi_get_system_info(DMI_PRODUCT_NAME));
+ printk(KERN_NOTICE PREFIX "DMI Product Version: %s\n",
- dmi_get_slot(DMI_BOARD_NAME));
++ dmi_get_system_info(DMI_PRODUCT_VERSION));
+ printk(KERN_NOTICE PREFIX "DMI Board Name: %s\n",
- dmi_get_slot(DMI_BIOS_VENDOR));
++ dmi_get_system_info(DMI_BOARD_NAME));
+ printk(KERN_NOTICE PREFIX "DMI BIOS Vendor: %s\n",
- dmi_get_slot(DMI_BIOS_DATE));
++ dmi_get_system_info(DMI_BIOS_VENDOR));
+ printk(KERN_NOTICE PREFIX "DMI BIOS Date: %s\n",
++ dmi_get_system_info(DMI_BIOS_DATE));
+
+ return 0;
+ }
+
+
/******************************************************************************
*
* FUNCTION: acpi_os_validate_interface
if (!strncmp(osi_additional_string, interface, OSI_STRING_LENGTH_MAX))
return AE_OK;
if (!strcmp("Linux", interface)) {
- printk(KERN_WARNING PREFIX
- "System BIOS is requesting _OSI(Linux)\n");
- printk(KERN_WARNING PREFIX
- "If \"acpi_osi=Linux\" works better,\n"
- "Please send dmidecode "
- "to linux-acpi@vger.kernel.org\n");
- if(osi_linux)
+
+ printk(KERN_NOTICE PREFIX
+ "BIOS _OSI(Linux) query %s%s\n",
+ osi_linux.enable ? "honored" : "ignored",
+ osi_linux.cmdline ? " via cmdline" :
+ osi_linux.dmi ? " via DMI" : "");
+
+ if (!osi_linux.dmi) {
+ if (acpi_dmi_dump())
+ printk(KERN_NOTICE PREFIX
+ "[please extract dmidecode output]\n");
+ printk(KERN_NOTICE PREFIX
+ "Please send DMI info above to "
+ "linux-acpi@vger.kernel.org\n");
+ }
+ if (!osi_linux.known && !osi_linux.cmdline) {
+ printk(KERN_NOTICE PREFIX
+ "If \"acpi_osi=%sLinux\" works better, "
+ "please notify linux-acpi@vger.kernel.org\n",
+ osi_linux.enable ? "!" : "");
+ }
+
+ if (osi_linux.enable)
return AE_OK;
}
return AE_SUPPORT;
return AE_OK;
}
- #ifdef CONFIG_DMI
- static int dmi_osi_linux(const struct dmi_system_id *d)
- {
- printk(KERN_NOTICE "%s detected: enabling _OSI(Linux)\n", d->ident);
- enable_osi_linux(1);
- return 0;
- }
-
- static struct dmi_system_id acpi_osl_dmi_table[] __initdata = {
- /*
- * Boxes that need _OSI(Linux)
- */
- {
- .callback = dmi_osi_linux,
- .ident = "Intel Napa CRB",
- .matches = {
- DMI_MATCH(DMI_BOARD_VENDOR, "Intel Corporation"),
- DMI_MATCH(DMI_BOARD_NAME, "MPAD-MSAE Customer Reference Boards"),
- },
- },
- {}
- };
- #endif /* CONFIG_DMI */
-
#endif
#ifdef CONFIG_PM_SLEEP
static u32 acpi_target_sleep_state = ACPI_STATE_S0;
+ static bool acpi_sleep_finish_wake_up;
+
+ /*
+ * ACPI 2.0 and later want us to execute _PTS after suspending devices, so we
+ * allow the user to request that behavior by using the 'acpi_new_pts_ordering'
+ * kernel command line option that causes the following variable to be set.
+ */
+ static bool new_pts_ordering;
+
+ static int __init acpi_new_pts_ordering(char *str)
+ {
+ new_pts_ordering = true;
+ return 1;
+ }
+ __setup("acpi_new_pts_ordering", acpi_new_pts_ordering);
#endif
- int acpi_sleep_prepare(u32 acpi_state)
+ static int acpi_sleep_prepare(u32 acpi_state)
{
#ifdef CONFIG_ACPI_SLEEP
/* do we have a wakeup address for S2 and S3? */
ACPI_FLUSH_CPU_CACHE();
acpi_enable_wakeup_device_prep(acpi_state);
#endif
+ printk(KERN_INFO PREFIX "Preparing to enter system sleep state S%d\n",
+ acpi_state);
acpi_enter_sleep_state_prep(acpi_state);
return 0;
}
static int init_8259A_after_S1;
/**
- * acpi_pm_set_target - Set the target system sleep state to the state
+ * acpi_pm_begin - Set the target system sleep state to the state
* associated with given @pm_state, if supported.
*/
- static int acpi_pm_set_target(suspend_state_t pm_state)
+ static int acpi_pm_begin(suspend_state_t pm_state)
{
u32 acpi_state = acpi_suspend_states[pm_state];
int error = 0;
if (sleep_states[acpi_state]) {
acpi_target_sleep_state = acpi_state;
+ if (new_pts_ordering)
+ return 0;
+
+ error = acpi_sleep_prepare(acpi_state);
+ if (error)
+ acpi_target_sleep_state = ACPI_STATE_S0;
+ else
+ acpi_sleep_finish_wake_up = true;
} else {
printk(KERN_ERR "ACPI does not support this state: %d\n",
pm_state);
static int acpi_pm_prepare(void)
{
- int error = acpi_sleep_prepare(acpi_target_sleep_state);
+ if (new_pts_ordering) {
+ int error = acpi_sleep_prepare(acpi_target_sleep_state);
- if (error)
- acpi_target_sleep_state = ACPI_STATE_S0;
+ if (error) {
+ acpi_target_sleep_state = ACPI_STATE_S0;
+ return error;
+ }
+ acpi_sleep_finish_wake_up = true;
+ }
- return error;
+ return ACPI_SUCCESS(acpi_hw_disable_all_gpes()) ? 0 : -EFAULT;
}
/**
if (acpi_state == ACPI_STATE_S3) {
int error = acpi_save_state_mem();
- if (error) {
- acpi_target_sleep_state = ACPI_STATE_S0;
+ if (error)
return error;
- }
}
local_irq_save(flags);
break;
}
- /* ACPI 3.0 specs (P62) says that it's the responsabilty
+ /* Reprogram control registers and execute _BFS */
+ acpi_leave_sleep_state_prep(acpi_state);
+
- /* ACPI 3.0 specs (P62) says that it's the responsabilty
++ /* ACPI 3.0 specs (P62) says that it's the responsibility
* of the OSPM to clear the status bit [ implying that the
* POWER_BUTTON event should not reach userspace ]
*/
if (ACPI_SUCCESS(status) && (acpi_state == ACPI_STATE_S3))
acpi_clear_event(ACPI_EVENT_POWER_BUTTON);
+ /*
+ * Disable and clear GPE status before interrupt is enabled. Some GPEs
+ * (like wakeup GPE) haven't handler, this can avoid such GPE misfire.
+ * acpi_leave_sleep_state will reenable specific GPEs later
+ */
+ acpi_hw_disable_all_gpes();
+
local_irq_restore(flags);
printk(KERN_DEBUG "Back to C!\n");
}
/**
- * acpi_pm_finish - Finish up suspend sequence.
+ * acpi_pm_finish - Instruct the platform to leave a sleep state.
*
* This is called after we wake back up (or if entering the sleep state
* failed).
acpi_set_firmware_waking_vector((acpi_physical_address) 0);
acpi_target_sleep_state = ACPI_STATE_S0;
+ acpi_sleep_finish_wake_up = false;
#ifdef CONFIG_X86
if (init_8259A_after_S1) {
#endif
}
+ /**
+ * acpi_pm_end - Finish up suspend sequence.
+ */
+
+ static void acpi_pm_end(void)
+ {
+ /*
+ * This is necessary in case acpi_pm_finish() is not called directly
+ * during a failing transition to a sleep state.
+ */
+ if (acpi_sleep_finish_wake_up)
+ acpi_pm_finish();
+ }
+
static int acpi_pm_state_valid(suspend_state_t pm_state)
{
u32 acpi_state;
static struct platform_suspend_ops acpi_pm_ops = {
.valid = acpi_pm_state_valid,
- .set_target = acpi_pm_set_target,
+ .begin = acpi_pm_begin,
.prepare = acpi_pm_prepare,
.enter = acpi_pm_enter,
.finish = acpi_pm_finish,
+ .end = acpi_pm_end,
};
/*
#endif /* CONFIG_SUSPEND */
#ifdef CONFIG_HIBERNATION
- static int acpi_hibernation_start(void)
+ static int acpi_hibernation_begin(void)
{
+ int error;
+
acpi_target_sleep_state = ACPI_STATE_S4;
- return 0;
+ if (new_pts_ordering)
+ return 0;
+
+ error = acpi_sleep_prepare(ACPI_STATE_S4);
+ if (error)
+ acpi_target_sleep_state = ACPI_STATE_S0;
+ else
+ acpi_sleep_finish_wake_up = true;
+
+ return error;
}
static int acpi_hibernation_prepare(void)
{
- return acpi_sleep_prepare(ACPI_STATE_S4);
+ if (new_pts_ordering) {
+ int error = acpi_sleep_prepare(ACPI_STATE_S4);
+
+ if (error) {
+ acpi_target_sleep_state = ACPI_STATE_S0;
+ return error;
+ }
+ acpi_sleep_finish_wake_up = true;
+ }
+
+ return ACPI_SUCCESS(acpi_hw_disable_all_gpes()) ? 0 : -EFAULT;
}
static int acpi_hibernation_enter(void)
acpi_enable_wakeup_device(ACPI_STATE_S4);
/* This shouldn't return. If it returns, we have a problem */
status = acpi_enter_sleep_state(ACPI_STATE_S4);
+ /* Reprogram control registers and execute _BFS */
+ acpi_leave_sleep_state_prep(ACPI_STATE_S4);
local_irq_restore(flags);
return ACPI_SUCCESS(status) ? 0 : -EFAULT;
* enable it here.
*/
acpi_enable();
+ /* Reprogram control registers and execute _BFS */
+ acpi_leave_sleep_state_prep(ACPI_STATE_S4);
}
static void acpi_hibernation_finish(void)
{
- /*
- * If ACPI is not enabled by the BIOS and the boot kernel, we need to
- * enable it here.
- */
- acpi_enable();
acpi_disable_wakeup_device(ACPI_STATE_S4);
acpi_leave_sleep_state(ACPI_STATE_S4);
acpi_set_firmware_waking_vector((acpi_physical_address) 0);
acpi_target_sleep_state = ACPI_STATE_S0;
+ acpi_sleep_finish_wake_up = false;
+ }
+
+ static void acpi_hibernation_end(void)
+ {
+ /*
+ * This is necessary in case acpi_hibernation_finish() is not called
+ * directly during a failing transition to the sleep state.
+ */
+ if (acpi_sleep_finish_wake_up)
+ acpi_hibernation_finish();
}
static int acpi_hibernation_pre_restore(void)
}
static struct platform_hibernation_ops acpi_hibernation_ops = {
- .start = acpi_hibernation_start,
+ .begin = acpi_hibernation_begin,
+ .end = acpi_hibernation_end,
.pre_snapshot = acpi_hibernation_prepare,
.finish = acpi_hibernation_finish,
.prepare = acpi_hibernation_prepare,
if (acpi_target_sleep_state == ACPI_STATE_S0 ||
(wake && adev->wakeup.state.enabled &&
adev->wakeup.sleep_state <= acpi_target_sleep_state)) {
++ acpi_status status;
++
acpi_method[3] = 'W';
-- acpi_evaluate_integer(handle, acpi_method, NULL, &d_max);
-- /* Sanity check */
-- if (d_max < d_min)
++ status = acpi_evaluate_integer(handle, acpi_method, NULL,
++ &d_max);
++ if (ACPI_FAILURE(status)) {
++ d_max = d_min;
++ } else if (d_max < d_min) {
++ /* Warn the user of the broken DSDT */
++ printk(KERN_WARNING "ACPI: Wrong value from %s\n",
++ acpi_method);
++ /* Sanitize it */
d_min = d_max;
++ }
}
if (d_min_p)
{
/* Prepare to power off the system */
acpi_sleep_prepare(ACPI_STATE_S5);
+ acpi_hw_disable_all_gpes();
}
static void acpi_power_off(void)