Merge branch 'for-38-rc2' of git://codeaurora.org/quic/kernel/davidb/linux-msm
[linux-2.6/cjktty.git] / drivers / acpi / nvs.c
blob54b6ab8040a6ed05039ce59d576969371e7fa3f8
1 /*
2 * nvs.c - Routines for saving and restoring ACPI NVS memory region
4 * Copyright (C) 2008-2011 Rafael J. Wysocki <rjw@sisk.pl>, Novell Inc.
6 * This file is released under the GPLv2.
7 */
9 #include <linux/io.h>
10 #include <linux/kernel.h>
11 #include <linux/list.h>
12 #include <linux/mm.h>
13 #include <linux/slab.h>
14 #include <linux/acpi.h>
15 #include <acpi/acpiosxf.h>
18 * Platforms, like ACPI, may want us to save some memory used by them during
19 * suspend and to restore the contents of this memory during the subsequent
20 * resume. The code below implements a mechanism allowing us to do that.
23 struct nvs_page {
24 unsigned long phys_start;
25 unsigned int size;
26 void *kaddr;
27 void *data;
28 struct list_head node;
31 static LIST_HEAD(nvs_list);
33 /**
34 * suspend_nvs_register - register platform NVS memory region to save
35 * @start - physical address of the region
36 * @size - size of the region
38 * The NVS region need not be page-aligned (both ends) and we arrange
39 * things so that the data from page-aligned addresses in this region will
40 * be copied into separate RAM pages.
42 int suspend_nvs_register(unsigned long start, unsigned long size)
44 struct nvs_page *entry, *next;
46 while (size > 0) {
47 unsigned int nr_bytes;
49 entry = kzalloc(sizeof(struct nvs_page), GFP_KERNEL);
50 if (!entry)
51 goto Error;
53 list_add_tail(&entry->node, &nvs_list);
54 entry->phys_start = start;
55 nr_bytes = PAGE_SIZE - (start & ~PAGE_MASK);
56 entry->size = (size < nr_bytes) ? size : nr_bytes;
58 start += entry->size;
59 size -= entry->size;
61 return 0;
63 Error:
64 list_for_each_entry_safe(entry, next, &nvs_list, node) {
65 list_del(&entry->node);
66 kfree(entry);
68 return -ENOMEM;
71 /**
72 * suspend_nvs_free - free data pages allocated for saving NVS regions
74 void suspend_nvs_free(void)
76 struct nvs_page *entry;
78 list_for_each_entry(entry, &nvs_list, node)
79 if (entry->data) {
80 free_page((unsigned long)entry->data);
81 entry->data = NULL;
82 if (entry->kaddr) {
83 acpi_os_unmap_memory(entry->kaddr, entry->size);
84 entry->kaddr = NULL;
89 /**
90 * suspend_nvs_alloc - allocate memory necessary for saving NVS regions
92 int suspend_nvs_alloc(void)
94 struct nvs_page *entry;
96 list_for_each_entry(entry, &nvs_list, node) {
97 entry->data = (void *)__get_free_page(GFP_KERNEL);
98 if (!entry->data) {
99 suspend_nvs_free();
100 return -ENOMEM;
103 return 0;
107 * suspend_nvs_save - save NVS memory regions
109 int suspend_nvs_save(void)
111 struct nvs_page *entry;
113 printk(KERN_INFO "PM: Saving platform NVS memory\n");
115 list_for_each_entry(entry, &nvs_list, node)
116 if (entry->data) {
117 entry->kaddr = acpi_os_map_memory(entry->phys_start,
118 entry->size);
119 if (!entry->kaddr) {
120 suspend_nvs_free();
121 return -ENOMEM;
123 memcpy(entry->data, entry->kaddr, entry->size);
126 return 0;
130 * suspend_nvs_restore - restore NVS memory regions
132 * This function is going to be called with interrupts disabled, so it
133 * cannot iounmap the virtual addresses used to access the NVS region.
135 void suspend_nvs_restore(void)
137 struct nvs_page *entry;
139 printk(KERN_INFO "PM: Restoring platform NVS memory\n");
141 list_for_each_entry(entry, &nvs_list, node)
142 if (entry->data)
143 memcpy(entry->kaddr, entry->data, entry->size);