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

Commit a3d25c27 authored by Rafael J. Wysocki's avatar Rafael J. Wysocki Committed by Linus Torvalds
Browse files

PM: Separate hibernation code from suspend code



[ With Johannes Berg <johannes@sipsolutions.net> ]

Separate the hibernation (aka suspend to disk code) from the other suspend
code.  In particular:

 * Remove the definitions related to hibernation from include/linux/pm.h
 * Introduce struct hibernation_ops and a new hibernate() function to hibernate
   the system, defined in include/linux/suspend.h
 * Separate suspend code in kernel/power/main.c from hibernation-related code
   in kernel/power/disk.c and kernel/power/user.c (with the help of
   hibernation_ops)
 * Switch ACPI (the only user of pm_ops.pm_disk_mode) to hibernation_ops

Signed-off-by: default avatarRafael J. Wysocki <rjw@sisk.pl>
Cc: Greg KH <greg@kroah.com>
Cc: Pavel Machek <pavel@ucw.cz>
Cc: Nigel Cunningham <nigel@nigel.suspend2.net>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent d60846c4
Loading
Loading
Loading
Loading
+14 −12
Original line number Original line Diff line number Diff line
@@ -93,21 +93,23 @@ SNAPSHOT_S2RAM - suspend to RAM; using this call causes the kernel to
	to resume the system from RAM if there's enough battery power or restore
	to resume the system from RAM if there's enough battery power or restore
	its state on the basis of the saved suspend image otherwise)
	its state on the basis of the saved suspend image otherwise)


SNAPSHOT_PMOPS - enable the usage of the pmops->prepare, pmops->enter and
SNAPSHOT_PMOPS - enable the usage of the hibernation_ops->prepare,
	pmops->finish methods (the in-kernel swsusp knows these as the "platform
	hibernate_ops->enter and hibernation_ops->finish methods (the in-kernel
	method") which are needed on many machines to (among others) speed up
	swsusp knows these as the "platform method") which are needed on many
	the resume by letting the BIOS skip some steps or to let the system
	machines to (among others) speed up the resume by letting the BIOS skip
	recognise the correct state of the hardware after the resume (in
	some steps or to let the system recognise the correct state of the
	particular on many machines this ensures that unplugged AC
	hardware after the resume (in particular on many machines this ensures
	adapters get correctly detected and that kacpid does not run wild after
	that unplugged AC adapters get correctly detected and that kacpid does
	the resume).  The last ioctl() argument can take one of the three
	not run wild after the resume).  The last ioctl() argument can take one
	values, defined in kernel/power/power.h:
	of the three values, defined in kernel/power/power.h:
	PMOPS_PREPARE - make the kernel carry out the
	PMOPS_PREPARE - make the kernel carry out the
		pm_ops->prepare(PM_SUSPEND_DISK) operation
		hibernation_ops->prepare() operation
	PMOPS_ENTER - make the kernel power off the system by calling
	PMOPS_ENTER - make the kernel power off the system by calling
		pm_ops->enter(PM_SUSPEND_DISK)
		hibernation_ops->enter()
	PMOPS_FINISH - make the kernel carry out the
	PMOPS_FINISH - make the kernel carry out the
		pm_ops->finish(PM_SUSPEND_DISK) operation
		hibernation_ops->finish() operation
	Note that the actual constants are misnamed because they surface
	internal kernel implementation details that have changed.


The device's read() operation can be used to transfer the snapshot image from
The device's read() operation can be used to transfer the snapshot image from
the kernel.  It has the following limitations:
the kernel.  It has the following limitations:
+53 −14
Original line number Original line Diff line number Diff line
@@ -29,7 +29,6 @@ static u32 acpi_suspend_states[] = {
	[PM_SUSPEND_ON] = ACPI_STATE_S0,
	[PM_SUSPEND_ON] = ACPI_STATE_S0,
	[PM_SUSPEND_STANDBY] = ACPI_STATE_S1,
	[PM_SUSPEND_STANDBY] = ACPI_STATE_S1,
	[PM_SUSPEND_MEM] = ACPI_STATE_S3,
	[PM_SUSPEND_MEM] = ACPI_STATE_S3,
	[PM_SUSPEND_DISK] = ACPI_STATE_S4,
	[PM_SUSPEND_MAX] = ACPI_STATE_S5
	[PM_SUSPEND_MAX] = ACPI_STATE_S5
};
};


@@ -94,14 +93,6 @@ static int acpi_pm_enter(suspend_state_t pm_state)
		do_suspend_lowlevel();
		do_suspend_lowlevel();
		break;
		break;


	case PM_SUSPEND_DISK:
		if (acpi_pm_ops.pm_disk_mode == PM_DISK_PLATFORM)
			status = acpi_enter_sleep_state(acpi_state);
		break;
	case PM_SUSPEND_MAX:
		acpi_power_off();
		break;

	default:
	default:
		return -EINVAL;
		return -EINVAL;
	}
	}
@@ -157,12 +148,13 @@ int acpi_suspend(u32 acpi_state)
	suspend_state_t states[] = {
	suspend_state_t states[] = {
		[1] = PM_SUSPEND_STANDBY,
		[1] = PM_SUSPEND_STANDBY,
		[3] = PM_SUSPEND_MEM,
		[3] = PM_SUSPEND_MEM,
		[4] = PM_SUSPEND_DISK,
		[5] = PM_SUSPEND_MAX
		[5] = PM_SUSPEND_MAX
	};
	};


	if (acpi_state < 6 && states[acpi_state])
	if (acpi_state < 6 && states[acpi_state])
		return pm_suspend(states[acpi_state]);
		return pm_suspend(states[acpi_state]);
	if (acpi_state == 4)
		return hibernate();
	return -EINVAL;
	return -EINVAL;
}
}


@@ -189,6 +181,49 @@ static struct pm_ops acpi_pm_ops = {
	.finish = acpi_pm_finish,
	.finish = acpi_pm_finish,
};
};


#ifdef CONFIG_SOFTWARE_SUSPEND
static int acpi_hibernation_prepare(void)
{
	return acpi_sleep_prepare(ACPI_STATE_S4);
}

static int acpi_hibernation_enter(void)
{
	acpi_status status = AE_OK;
	unsigned long flags = 0;

	ACPI_FLUSH_CPU_CACHE();

	local_irq_save(flags);
	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);
	local_irq_restore(flags);

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

static void acpi_hibernation_finish(void)
{
	acpi_leave_sleep_state(ACPI_STATE_S4);
	acpi_disable_wakeup_device(ACPI_STATE_S4);

	/* reset firmware waking vector */
	acpi_set_firmware_waking_vector((acpi_physical_address) 0);

	if (init_8259A_after_S1) {
		printk("Broken toshiba laptop -> kicking interrupts\n");
		init_8259A(0);
	}
}

static struct hibernation_ops acpi_hibernation_ops = {
	.prepare = acpi_hibernation_prepare,
	.enter = acpi_hibernation_enter,
	.finish = acpi_hibernation_finish,
};
#endif /* CONFIG_SOFTWARE_SUSPEND */

/*
/*
 * Toshiba fails to preserve interrupts over S1, reinitialization
 * Toshiba fails to preserve interrupts over S1, reinitialization
 * of 8259 is needed after S1 resume.
 * of 8259 is needed after S1 resume.
@@ -227,14 +262,18 @@ int __init acpi_sleep_init(void)
			sleep_states[i] = 1;
			sleep_states[i] = 1;
			printk(" S%d", i);
			printk(" S%d", i);
		}
		}
		if (i == ACPI_STATE_S4) {
			if (sleep_states[i])
				acpi_pm_ops.pm_disk_mode = PM_DISK_PLATFORM;
		}
	}
	}
	printk(")\n");
	printk(")\n");


	pm_set_ops(&acpi_pm_ops);
	pm_set_ops(&acpi_pm_ops);

#ifdef CONFIG_SOFTWARE_SUSPEND
	if (sleep_states[ACPI_STATE_S4])
		hibernation_set_ops(&acpi_hibernation_ops);
#else
	sleep_states[ACPI_STATE_S4] = 0;
#endif

	return 0;
	return 0;
}
}
+1 −1
Original line number Original line Diff line number Diff line
@@ -60,7 +60,7 @@ acpi_system_write_sleep(struct file *file,
	state = simple_strtoul(str, NULL, 0);
	state = simple_strtoul(str, NULL, 0);
#ifdef CONFIG_SOFTWARE_SUSPEND
#ifdef CONFIG_SOFTWARE_SUSPEND
	if (state == 4) {
	if (state == 4) {
		error = pm_suspend(PM_SUSPEND_DISK);
		error = hibernate();
		goto Done;
		goto Done;
	}
	}
#endif
#endif
+1 −1
Original line number Original line Diff line number Diff line
@@ -354,7 +354,7 @@ static void tps65010_interrupt(struct tps65010 *tps)
			 * also needs to get error handling and probably
			 * also needs to get error handling and probably
			 * an #ifdef CONFIG_SOFTWARE_SUSPEND
			 * an #ifdef CONFIG_SOFTWARE_SUSPEND
			 */
			 */
			pm_suspend(PM_SUSPEND_DISK);
			hibernate();
#endif
#endif
			poll = 1;
			poll = 1;
		}
		}
+1 −30
Original line number Original line Diff line number Diff line
@@ -107,26 +107,11 @@ typedef int __bitwise suspend_state_t;
#define PM_SUSPEND_ON		((__force suspend_state_t) 0)
#define PM_SUSPEND_ON		((__force suspend_state_t) 0)
#define PM_SUSPEND_STANDBY	((__force suspend_state_t) 1)
#define PM_SUSPEND_STANDBY	((__force suspend_state_t) 1)
#define PM_SUSPEND_MEM		((__force suspend_state_t) 3)
#define PM_SUSPEND_MEM		((__force suspend_state_t) 3)
#define PM_SUSPEND_DISK		((__force suspend_state_t) 4)
#define PM_SUSPEND_MAX		((__force suspend_state_t) 4)
#define PM_SUSPEND_MAX		((__force suspend_state_t) 5)

typedef int __bitwise suspend_disk_method_t;

/* invalid must be 0 so struct pm_ops initialisers can leave it out */
#define PM_DISK_INVALID		((__force suspend_disk_method_t) 0)
#define	PM_DISK_PLATFORM	((__force suspend_disk_method_t) 1)
#define	PM_DISK_SHUTDOWN	((__force suspend_disk_method_t) 2)
#define	PM_DISK_REBOOT		((__force suspend_disk_method_t) 3)
#define	PM_DISK_TEST		((__force suspend_disk_method_t) 4)
#define	PM_DISK_TESTPROC	((__force suspend_disk_method_t) 5)
#define	PM_DISK_MAX		((__force suspend_disk_method_t) 6)


/**
/**
 * struct pm_ops - Callbacks for managing platform dependent suspend states.
 * struct pm_ops - Callbacks for managing platform dependent suspend states.
 * @valid: Callback to determine whether the given state can be entered.
 * @valid: Callback to determine whether the given state can be entered.
 * 	If %CONFIG_SOFTWARE_SUSPEND is set then %PM_SUSPEND_DISK is
 *	always valid and never passed to this call. If not assigned,
 *	no suspend states are valid.
 *	Valid states are advertised in /sys/power/state but can still
 *	Valid states are advertised in /sys/power/state but can still
 *	be rejected by prepare or enter if the conditions aren't right.
 *	be rejected by prepare or enter if the conditions aren't right.
 *	There is a %pm_valid_only_mem function available that can be assigned
 *	There is a %pm_valid_only_mem function available that can be assigned
@@ -140,24 +125,12 @@ typedef int __bitwise suspend_disk_method_t;
 *
 *
 * @finish: Called when the system has left the given state and all devices
 * @finish: Called when the system has left the given state and all devices
 *	are resumed. The return value is ignored.
 *	are resumed. The return value is ignored.
 *
 * @pm_disk_mode: The generic code always allows one of the shutdown methods
 *	%PM_DISK_SHUTDOWN, %PM_DISK_REBOOT, %PM_DISK_TEST and
 *	%PM_DISK_TESTPROC. If this variable is set, the mode it is set
 *	to is allowed in addition to those modes and is also made default.
 *	When this mode is sent selected, the @prepare call will be called
 *	before suspending to disk (if present), the @enter call should be
 *	present and will be called after all state has been saved and the
 *	machine is ready to be powered off; the @finish callback is called
 *	after state has been restored. All these calls are called with
 *	%PM_SUSPEND_DISK as the state.
 */
 */
struct pm_ops {
struct pm_ops {
	int (*valid)(suspend_state_t state);
	int (*valid)(suspend_state_t state);
	int (*prepare)(suspend_state_t state);
	int (*prepare)(suspend_state_t state);
	int (*enter)(suspend_state_t state);
	int (*enter)(suspend_state_t state);
	int (*finish)(suspend_state_t state);
	int (*finish)(suspend_state_t state);
	suspend_disk_method_t pm_disk_mode;
};
};


/**
/**
@@ -276,8 +249,6 @@ extern void device_power_up(void);
extern void device_resume(void);
extern void device_resume(void);


#ifdef CONFIG_PM
#ifdef CONFIG_PM
extern suspend_disk_method_t pm_disk_mode;

extern int device_suspend(pm_message_t state);
extern int device_suspend(pm_message_t state);
extern int device_prepare_suspend(pm_message_t state);
extern int device_prepare_suspend(pm_message_t state);


Loading