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

Commit fce2b111 authored by Cornelia Huck's avatar Cornelia Huck Committed by Rafael J. Wysocki
Browse files

PM/Hibernate: Move NVS routines into a seperate file (v2).



The *_nvs_* routines in swsusp.c make use of the io*map()
functions, which are only provided for HAS_IOMEM, thus
breaking compilation if HAS_IOMEM is not set. Fix this
by moving the *_nvs_* routines into hibernate_nvs.c, which
is only compiled if HAS_IOMEM is set.

[rjw: Change the name of the new file to hibernate_nvs.c, add the
 license line to the header comment.]

Signed-off-by: default avatarCornelia Huck <cornelia.huck@de.ibm.com>
Acked-by: default avatarPavel Machek <pavel@ucw.cz>
Signed-off-by: default avatarRafael J. Wysocki <rjw@sisk.pl>
parent 8b759b84
Loading
Loading
Loading
Loading
+11 −7
Original line number Diff line number Diff line
@@ -245,11 +245,6 @@ extern unsigned long get_safe_page(gfp_t gfp_mask);

extern void hibernation_set_ops(struct platform_hibernation_ops *ops);
extern int hibernate(void);
extern int hibernate_nvs_register(unsigned long start, unsigned long size);
extern int hibernate_nvs_alloc(void);
extern void hibernate_nvs_free(void);
extern void hibernate_nvs_save(void);
extern void hibernate_nvs_restore(void);
extern bool system_entering_hibernation(void);
#else /* CONFIG_HIBERNATION */
static inline int swsusp_page_is_forbidden(struct page *p) { return 0; }
@@ -258,6 +253,16 @@ static inline void swsusp_unset_page_free(struct page *p) {}

static inline void hibernation_set_ops(struct platform_hibernation_ops *ops) {}
static inline int hibernate(void) { return -ENOSYS; }
static inline bool system_entering_hibernation(void) { return false; }
#endif /* CONFIG_HIBERNATION */

#ifdef CONFIG_HIBERNATION_NVS
extern int hibernate_nvs_register(unsigned long start, unsigned long size);
extern int hibernate_nvs_alloc(void);
extern void hibernate_nvs_free(void);
extern void hibernate_nvs_save(void);
extern void hibernate_nvs_restore(void);
#else /* CONFIG_HIBERNATION_NVS */
static inline int hibernate_nvs_register(unsigned long a, unsigned long b)
{
	return 0;
@@ -266,8 +271,7 @@ static inline int hibernate_nvs_alloc(void) { return 0; }
static inline void hibernate_nvs_free(void) {}
static inline void hibernate_nvs_save(void) {}
static inline void hibernate_nvs_restore(void) {}
static inline bool system_entering_hibernation(void) { return false; }
#endif /* CONFIG_HIBERNATION */
#endif /* CONFIG_HIBERNATION_NVS */

#ifdef CONFIG_PM_SLEEP
void save_processor_state(void);
+4 −0
Original line number Diff line number Diff line
@@ -116,9 +116,13 @@ config SUSPEND_FREEZER

	  Turning OFF this setting is NOT recommended! If in doubt, say Y.

config HIBERNATION_NVS
	bool

config HIBERNATION
	bool "Hibernation (aka 'suspend to disk')"
	depends on PM && SWAP && ARCH_HIBERNATION_POSSIBLE
	select HIBERNATION_NVS if HAS_IOMEM
	---help---
	  Enable the suspend to disk (STD) functionality, which is usually
	  called "hibernation" in user interfaces.  STD checkpoints the
+1 −0
Original line number Diff line number Diff line
@@ -9,5 +9,6 @@ obj-$(CONFIG_FREEZER) += process.o
obj-$(CONFIG_SUSPEND)		+= suspend.o
obj-$(CONFIG_PM_TEST_SUSPEND)	+= suspend_test.o
obj-$(CONFIG_HIBERNATION)	+= swsusp.o hibernate.o snapshot.o swap.o user.o
obj-$(CONFIG_HIBERNATION_NVS)	+= hibernate_nvs.o

obj-$(CONFIG_MAGIC_SYSRQ)	+= poweroff.o
+135 −0
Original line number Diff line number Diff line
/*
 * linux/kernel/power/hibernate_nvs.c - Routines for handling NVS memory
 *
 * Copyright (C) 2008,2009 Rafael J. Wysocki <rjw@sisk.pl>, Novell Inc.
 *
 * This file is released under the GPLv2.
 */

#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/mm.h>
#include <linux/suspend.h>

/*
 * Platforms, like ACPI, may want us to save some memory used by them during
 * hibernation and to restore the contents of this memory during the subsequent
 * resume.  The code below implements a mechanism allowing us to do that.
 */

struct nvs_page {
	unsigned long phys_start;
	unsigned int size;
	void *kaddr;
	void *data;
	struct list_head node;
};

static LIST_HEAD(nvs_list);

/**
 *	hibernate_nvs_register - register platform NVS memory region to save
 *	@start - physical address of the region
 *	@size - size of the region
 *
 *	The NVS region need not be page-aligned (both ends) and we arrange
 *	things so that the data from page-aligned addresses in this region will
 *	be copied into separate RAM pages.
 */
int hibernate_nvs_register(unsigned long start, unsigned long size)
{
	struct nvs_page *entry, *next;

	while (size > 0) {
		unsigned int nr_bytes;

		entry = kzalloc(sizeof(struct nvs_page), GFP_KERNEL);
		if (!entry)
			goto Error;

		list_add_tail(&entry->node, &nvs_list);
		entry->phys_start = start;
		nr_bytes = PAGE_SIZE - (start & ~PAGE_MASK);
		entry->size = (size < nr_bytes) ? size : nr_bytes;

		start += entry->size;
		size -= entry->size;
	}
	return 0;

 Error:
	list_for_each_entry_safe(entry, next, &nvs_list, node) {
		list_del(&entry->node);
		kfree(entry);
	}
	return -ENOMEM;
}

/**
 *	hibernate_nvs_free - free data pages allocated for saving NVS regions
 */
void hibernate_nvs_free(void)
{
	struct nvs_page *entry;

	list_for_each_entry(entry, &nvs_list, node)
		if (entry->data) {
			free_page((unsigned long)entry->data);
			entry->data = NULL;
			if (entry->kaddr) {
				iounmap(entry->kaddr);
				entry->kaddr = NULL;
			}
		}
}

/**
 *	hibernate_nvs_alloc - allocate memory necessary for saving NVS regions
 */
int hibernate_nvs_alloc(void)
{
	struct nvs_page *entry;

	list_for_each_entry(entry, &nvs_list, node) {
		entry->data = (void *)__get_free_page(GFP_KERNEL);
		if (!entry->data) {
			hibernate_nvs_free();
			return -ENOMEM;
		}
	}
	return 0;
}

/**
 *	hibernate_nvs_save - save NVS memory regions
 */
void hibernate_nvs_save(void)
{
	struct nvs_page *entry;

	printk(KERN_INFO "PM: Saving platform NVS memory\n");

	list_for_each_entry(entry, &nvs_list, node)
		if (entry->data) {
			entry->kaddr = ioremap(entry->phys_start, entry->size);
			memcpy(entry->data, entry->kaddr, entry->size);
		}
}

/**
 *	hibernate_nvs_restore - restore NVS memory regions
 *
 *	This function is going to be called with interrupts disabled, so it
 *	cannot iounmap the virtual addresses used to access the NVS region.
 */
void hibernate_nvs_restore(void)
{
	struct nvs_page *entry;

	printk(KERN_INFO "PM: Restoring platform NVS memory\n");

	list_for_each_entry(entry, &nvs_list, node)
		if (entry->data)
			memcpy(entry->kaddr, entry->data, entry->size);
}
+0 −122
Original line number Diff line number Diff line
@@ -186,125 +186,3 @@ void swsusp_show_speed(struct timeval *start, struct timeval *stop,
			centisecs / 100, centisecs % 100,
			kps / 1000, (kps % 1000) / 10);
}

/*
 * Platforms, like ACPI, may want us to save some memory used by them during
 * hibernation and to restore the contents of this memory during the subsequent
 * resume.  The code below implements a mechanism allowing us to do that.
 */

struct nvs_page {
	unsigned long phys_start;
	unsigned int size;
	void *kaddr;
	void *data;
	struct list_head node;
};

static LIST_HEAD(nvs_list);

/**
 *	hibernate_nvs_register - register platform NVS memory region to save
 *	@start - physical address of the region
 *	@size - size of the region
 *
 *	The NVS region need not be page-aligned (both ends) and we arrange
 *	things so that the data from page-aligned addresses in this region will
 *	be copied into separate RAM pages.
 */
int hibernate_nvs_register(unsigned long start, unsigned long size)
{
	struct nvs_page *entry, *next;

	while (size > 0) {
		unsigned int nr_bytes;

		entry = kzalloc(sizeof(struct nvs_page), GFP_KERNEL);
		if (!entry)
			goto Error;

		list_add_tail(&entry->node, &nvs_list);
		entry->phys_start = start;
		nr_bytes = PAGE_SIZE - (start & ~PAGE_MASK);
		entry->size = (size < nr_bytes) ? size : nr_bytes;

		start += entry->size;
		size -= entry->size;
	}
	return 0;

 Error:
	list_for_each_entry_safe(entry, next, &nvs_list, node) {
		list_del(&entry->node);
		kfree(entry);
	}
	return -ENOMEM;
}

/**
 *	hibernate_nvs_free - free data pages allocated for saving NVS regions
 */
void hibernate_nvs_free(void)
{
	struct nvs_page *entry;

	list_for_each_entry(entry, &nvs_list, node)
		if (entry->data) {
			free_page((unsigned long)entry->data);
			entry->data = NULL;
			if (entry->kaddr) {
				iounmap(entry->kaddr);
				entry->kaddr = NULL;
			}
		}
}

/**
 *	hibernate_nvs_alloc - allocate memory necessary for saving NVS regions
 */
int hibernate_nvs_alloc(void)
{
	struct nvs_page *entry;

	list_for_each_entry(entry, &nvs_list, node) {
		entry->data = (void *)__get_free_page(GFP_KERNEL);
		if (!entry->data) {
			hibernate_nvs_free();
			return -ENOMEM;
		}
	}
	return 0;
}

/**
 *	hibernate_nvs_save - save NVS memory regions
 */
void hibernate_nvs_save(void)
{
	struct nvs_page *entry;

	printk(KERN_INFO "PM: Saving platform NVS memory\n");

	list_for_each_entry(entry, &nvs_list, node)
		if (entry->data) {
			entry->kaddr = ioremap(entry->phys_start, entry->size);
			memcpy(entry->data, entry->kaddr, entry->size);
		}
}

/**
 *	hibernate_nvs_restore - restore NVS memory regions
 *
 *	This function is going to be called with interrupts disabled, so it
 *	cannot iounmap the virtual addresses used to access the NVS region.
 */
void hibernate_nvs_restore(void)
{
	struct nvs_page *entry;

	printk(KERN_INFO "PM: Restoring platform NVS memory\n");

	list_for_each_entry(entry, &nvs_list, node)
		if (entry->data)
			memcpy(entry->kaddr, entry->data, entry->size);
}