Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e2a5b420 authored by Alexey Starikovskiy's avatar Alexey Starikovskiy Committed by Len Brown
Browse files

[ACPI] ACPI poweroff fix

Register an "acpi" system device to be notified of shutdown preparation.
This depends on CONFIG_PM

http://bugzilla.kernel.org/show_bug.cgi?id=4041



Signed-off-by: default avatarAlexey Starikovskiy <alexey.y.starikovskiy@intel.com>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLen Brown <len.brown@intel.com>
parent be91492c
Loading
Loading
Loading
Loading
+32 −42
Original line number Diff line number Diff line
/*
 * sleep.c - ACPI sleep support.
 *
 * Copyright (c) 2005 Alexey Starikovskiy <alexey.y.starikovskiy@intel.com>
 * Copyright (c) 2004 David Shaohua Li <shaohua.li@intel.com>
 * Copyright (c) 2000-2003 Patrick Mochel
 * Copyright (c) 2003 Open Source Development Lab
@@ -14,7 +15,6 @@
#include <linux/dmi.h>
#include <linux/device.h>
#include <linux/suspend.h>
#include <asm/io.h>
#include <acpi/acpi_bus.h>
#include <acpi/acpi_drivers.h>
#include "sleep.h"
@@ -31,6 +31,7 @@ static u32 acpi_suspend_states[] = {
	[PM_SUSPEND_STANDBY] = ACPI_STATE_S1,
	[PM_SUSPEND_MEM] = ACPI_STATE_S3,
	[PM_SUSPEND_DISK] = ACPI_STATE_S4,
	[PM_SUSPEND_MAX] = ACPI_STATE_S5
};

static int init_8259A_after_S1;
@@ -44,30 +45,20 @@ static int init_8259A_after_S1;
 *	wakeup code to the waking vector. 
 */

extern int acpi_sleep_prepare(u32 acpi_state);
extern void acpi_power_off(void);

static int acpi_pm_prepare(suspend_state_t pm_state)
{
	u32 acpi_state = acpi_suspend_states[pm_state];

	if (!sleep_states[acpi_state])
	if (!sleep_states[acpi_state]) {
		printk("acpi_pm_prepare does not support %d \n", pm_state);
		return -EPERM;

	/* do we have a wakeup address for S2 and S3? */
	/* Here, we support only S4BIOS, those we set the wakeup address */
	/* S4OS is only supported for now via swsusp.. */
	if (pm_state == PM_SUSPEND_MEM || pm_state == PM_SUSPEND_DISK) {
		if (!acpi_wakeup_address)
			return -EFAULT;
		acpi_set_firmware_waking_vector(
			(acpi_physical_address) virt_to_phys(
				(void *)acpi_wakeup_address));
	}
	ACPI_FLUSH_CPU_CACHE();
	acpi_enable_wakeup_device_prep(acpi_state);
	acpi_enter_sleep_state_prep(acpi_state);
	return 0;
	return acpi_sleep_prepare(acpi_state);
}


/**
 *	acpi_pm_enter - Actually enter a sleep state.
 *	@pm_state:		State we're entering.
@@ -92,11 +83,9 @@ static int acpi_pm_enter(suspend_state_t pm_state)
			return error;
	}


	local_irq_save(flags);
	acpi_enable_wakeup_device(acpi_state);
	switch (pm_state)
	{
	switch (pm_state) {
	case PM_SUSPEND_STANDBY:
		barrier();
		status = acpi_enter_sleep_state(acpi_state);
@@ -112,6 +101,10 @@ static int acpi_pm_enter(suspend_state_t pm_state)
		else
			do_suspend_lowlevel_s4bios();
		break;
	case PM_SUSPEND_MAX:
		acpi_power_off();
		break;

	default:
		return -EINVAL;
	}
@@ -126,11 +119,9 @@ static int acpi_pm_enter(suspend_state_t pm_state)
	if (pm_state > PM_SUSPEND_STANDBY)
		acpi_restore_state_mem();


	return ACPI_SUCCESS(status) ? 0 : -EFAULT;
}


/**
 *	acpi_pm_finish - Finish up suspend sequence.
 *	@pm_state:		State we're coming out of.
@@ -156,16 +147,16 @@ static int acpi_pm_finish(suspend_state_t pm_state)
	return 0;
}


int acpi_suspend(u32 acpi_state)
{
	suspend_state_t states[] = {
		[1] = PM_SUSPEND_STANDBY,
		[3] = PM_SUSPEND_MEM,
		[4] = PM_SUSPEND_DISK,
		[5] = PM_SUSPEND_MAX
	};

	if (acpi_state <= 4 && states[acpi_state])
	if (acpi_state < 6 && states[acpi_state])
		return pm_suspend(states[acpi_state]);
	return -EINVAL;
}
@@ -176,7 +167,6 @@ static struct pm_ops acpi_pm_ops = {
	.finish = acpi_pm_finish,
};


/*
 * Toshiba fails to preserve interrupts over S1, reinitialization
 * of 8259 is needed after S1 resume.
+73 −8
Original line number Diff line number Diff line
@@ -3,35 +3,100 @@
 *
 * AKA S5, but it is independent of whether or not the kernel supports
 * any other sleep support in the system.
 *
 * Copyright (c) 2005 Alexey Starikovskiy <alexey.y.starikovskiy@intel.com>
 *
 * This file is released under the GPLv2.
 */

#include <linux/pm.h>
#include <linux/init.h>
#include <acpi/acpi_bus.h>
#include <linux/sched.h>
#include <linux/sysdev.h>
#include <asm/io.h>
#include "sleep.h"

static void
acpi_power_off (void)
int acpi_sleep_prepare(u32 acpi_state)
{
	/* Flag to do not allow second time invocation for S5 state */
	static int shutdown_prepared = 0;
#ifdef CONFIG_ACPI_SLEEP
	/* do we have a wakeup address for S2 and S3? */
	/* Here, we support only S4BIOS, those we set the wakeup address */
	/* S4OS is only supported for now via swsusp.. */
	if (acpi_state == ACPI_STATE_S3 || acpi_state == ACPI_STATE_S4) {
		if (!acpi_wakeup_address) {
			return -EFAULT;
		}
		acpi_set_firmware_waking_vector((acpi_physical_address)
						virt_to_phys((void *)
							     acpi_wakeup_address));

	}
	ACPI_FLUSH_CPU_CACHE();
	acpi_enable_wakeup_device_prep(acpi_state);
#endif
	if (acpi_state == ACPI_STATE_S5) {
		/* Check if we were already called */
		if (shutdown_prepared)
			return 0;
		acpi_wakeup_gpe_poweroff_prepare();
		shutdown_prepared = 1;
	}
	acpi_enter_sleep_state_prep(acpi_state);
	return 0;
}

void acpi_power_off(void)
{
	printk("%s called\n", __FUNCTION__);
	acpi_sleep_prepare(ACPI_STATE_S5);
	local_irq_disable();
	/* Some SMP machines only can poweroff in boot CPU */
	set_cpus_allowed(current, cpumask_of_cpu(0));
	acpi_wakeup_gpe_poweroff_prepare();
	acpi_enter_sleep_state_prep(ACPI_STATE_S5);
	ACPI_DISABLE_IRQS();
	acpi_enter_sleep_state(ACPI_STATE_S5);
}

#ifdef CONFIG_PM

static int acpi_shutdown(struct sys_device *x)
{
	return acpi_sleep_prepare(ACPI_STATE_S5);
}

static struct sysdev_class acpi_sysclass = {
	set_kset_name("acpi"),
	.shutdown = acpi_shutdown
};

static struct sys_device device_acpi = {
	.id = 0,
	.cls = &acpi_sysclass,
};

#endif

static int acpi_poweroff_init(void)
{
	if (!acpi_disabled) {
		u8 type_a, type_b;
		acpi_status status;

		status = acpi_get_sleep_type_data(ACPI_STATE_S5, &type_a, &type_b);
		if (ACPI_SUCCESS(status))
		status =
		    acpi_get_sleep_type_data(ACPI_STATE_S5, &type_a, &type_b);
		if (ACPI_SUCCESS(status)) {
			pm_power_off = acpi_power_off;
#ifdef CONFIG_PM
			{
				int error;
				error = sysdev_class_register(&acpi_sysclass);
				if (!error)
					error = sysdev_register(&device_acpi);
				return error;
			}
#endif
		}
	}
	return 0;
}
+0 −1
Original line number Diff line number Diff line
@@ -22,7 +22,6 @@
#include <linux/string.h>
#include <linux/pm.h>


extern struct subsystem devices_subsys;

#define to_sysdev(k) container_of(k, struct sys_device, kobj)
+1 −1
Original line number Diff line number Diff line
@@ -175,7 +175,7 @@ struct pm_ops {
};

extern void pm_set_ops(struct pm_ops *);

extern struct pm_ops *pm_ops;
extern int pm_suspend(suspend_state_t state);


+1 −1
Original line number Diff line number Diff line
@@ -190,7 +190,7 @@ int software_suspend(void)

int pm_suspend(suspend_state_t state)
{
	if (state > PM_SUSPEND_ON && state < PM_SUSPEND_MAX)
	if (state > PM_SUSPEND_ON && state <= PM_SUSPEND_MAX)
		return enter_state(state);
	return -EINVAL;
}