gianfar: Fix misleading indentation in startup_gfar()
[pandora-kernel.git] / drivers / acpi / nvs.c
1 /*
2  * nvs.c - Routines for saving and restoring ACPI NVS memory region
3  *
4  * Copyright (C) 2008-2011 Rafael J. Wysocki <rjw@sisk.pl>, Novell Inc.
5  *
6  * This file is released under the GPLv2.
7  */
8
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>
16
17 /*
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.
21  */
22
23 struct nvs_page {
24         unsigned long phys_start;
25         unsigned int size;
26         void *kaddr;
27         void *data;
28         struct list_head node;
29 };
30
31 static LIST_HEAD(nvs_list);
32
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
37  *
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.
41  */
42 int suspend_nvs_register(unsigned long start, unsigned long size)
43 {
44         struct nvs_page *entry, *next;
45
46         while (size > 0) {
47                 unsigned int nr_bytes;
48
49                 entry = kzalloc(sizeof(struct nvs_page), GFP_KERNEL);
50                 if (!entry)
51                         goto Error;
52
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;
57
58                 start += entry->size;
59                 size -= entry->size;
60         }
61         return 0;
62
63  Error:
64         list_for_each_entry_safe(entry, next, &nvs_list, node) {
65                 list_del(&entry->node);
66                 kfree(entry);
67         }
68         return -ENOMEM;
69 }
70
71 /**
72  *      suspend_nvs_free - free data pages allocated for saving NVS regions
73  */
74 void suspend_nvs_free(void)
75 {
76         struct nvs_page *entry;
77
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;
85                         }
86                 }
87 }
88
89 /**
90  *      suspend_nvs_alloc - allocate memory necessary for saving NVS regions
91  */
92 int suspend_nvs_alloc(void)
93 {
94         struct nvs_page *entry;
95
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;
101                 }
102         }
103         return 0;
104 }
105
106 /**
107  *      suspend_nvs_save - save NVS memory regions
108  */
109 int suspend_nvs_save(void)
110 {
111         struct nvs_page *entry;
112
113         printk(KERN_INFO "PM: Saving platform NVS memory\n");
114
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;
122                         }
123                         memcpy(entry->data, entry->kaddr, entry->size);
124                 }
125
126         return 0;
127 }
128
129 /**
130  *      suspend_nvs_restore - restore NVS memory regions
131  *
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.
134  */
135 void suspend_nvs_restore(void)
136 {
137         struct nvs_page *entry;
138
139         printk(KERN_INFO "PM: Restoring platform NVS memory\n");
140
141         list_for_each_entry(entry, &nvs_list, node)
142                 if (entry->data)
143                         memcpy(entry->kaddr, entry->data, entry->size);
144 }