ACPI / OSL: Mark the function acpi_table_checksum() as static
[linux-2.6-block.git] / drivers / acpi / nvs.c
CommitLineData
fce2b111 1/*
d146df18 2 * nvs.c - Routines for saving and restoring ACPI NVS memory region
fce2b111 3 *
d146df18 4 * Copyright (C) 2008-2011 Rafael J. Wysocki <rjw@sisk.pl>, Novell Inc.
fce2b111
CH
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>
5a0e3ad6 13#include <linux/slab.h>
d146df18 14#include <linux/acpi.h>
fce2b111 15
b54ac6d2
HY
16/* ACPI NVS regions, APEI may use it */
17
18struct nvs_region {
19 __u64 phys_start;
20 __u64 size;
21 struct list_head node;
22};
23
24static LIST_HEAD(nvs_region_list);
25
26#ifdef CONFIG_ACPI_SLEEP
27static int suspend_nvs_register(unsigned long start, unsigned long size);
28#else
29static inline int suspend_nvs_register(unsigned long a, unsigned long b)
30{
31 return 0;
32}
33#endif
34
35int acpi_nvs_register(__u64 start, __u64 size)
36{
37 struct nvs_region *region;
38
39 region = kmalloc(sizeof(*region), GFP_KERNEL);
40 if (!region)
41 return -ENOMEM;
42 region->phys_start = start;
43 region->size = size;
44 list_add_tail(&region->node, &nvs_region_list);
45
46 return suspend_nvs_register(start, size);
47}
48
49int acpi_nvs_for_each_region(int (*func)(__u64 start, __u64 size, void *data),
50 void *data)
51{
52 int rc;
53 struct nvs_region *region;
54
55 list_for_each_entry(region, &nvs_region_list, node) {
56 rc = func(region->phys_start, region->size, data);
57 if (rc)
58 return rc;
59 }
60
61 return 0;
62}
63
64
65#ifdef CONFIG_ACPI_SLEEP
fce2b111
CH
66/*
67 * Platforms, like ACPI, may want us to save some memory used by them during
dd4c4f17 68 * suspend and to restore the contents of this memory during the subsequent
fce2b111
CH
69 * resume. The code below implements a mechanism allowing us to do that.
70 */
71
72struct nvs_page {
73 unsigned long phys_start;
74 unsigned int size;
75 void *kaddr;
76 void *data;
bb45e394 77 bool unmap;
fce2b111
CH
78 struct list_head node;
79};
80
81static LIST_HEAD(nvs_list);
82
83/**
dd4c4f17 84 * suspend_nvs_register - register platform NVS memory region to save
fce2b111
CH
85 * @start - physical address of the region
86 * @size - size of the region
87 *
88 * The NVS region need not be page-aligned (both ends) and we arrange
89 * things so that the data from page-aligned addresses in this region will
90 * be copied into separate RAM pages.
91 */
b54ac6d2 92static int suspend_nvs_register(unsigned long start, unsigned long size)
fce2b111
CH
93{
94 struct nvs_page *entry, *next;
95
c6436f5a
BH
96 pr_info("PM: Registering ACPI NVS region [mem %#010lx-%#010lx] (%ld bytes)\n",
97 start, start + size - 1, size);
bb45e394 98
fce2b111
CH
99 while (size > 0) {
100 unsigned int nr_bytes;
101
102 entry = kzalloc(sizeof(struct nvs_page), GFP_KERNEL);
103 if (!entry)
104 goto Error;
105
106 list_add_tail(&entry->node, &nvs_list);
107 entry->phys_start = start;
108 nr_bytes = PAGE_SIZE - (start & ~PAGE_MASK);
109 entry->size = (size < nr_bytes) ? size : nr_bytes;
110
111 start += entry->size;
112 size -= entry->size;
113 }
114 return 0;
115
116 Error:
117 list_for_each_entry_safe(entry, next, &nvs_list, node) {
118 list_del(&entry->node);
119 kfree(entry);
120 }
121 return -ENOMEM;
122}
123
124/**
dd4c4f17 125 * suspend_nvs_free - free data pages allocated for saving NVS regions
fce2b111 126 */
dd4c4f17 127void suspend_nvs_free(void)
fce2b111
CH
128{
129 struct nvs_page *entry;
130
131 list_for_each_entry(entry, &nvs_list, node)
132 if (entry->data) {
133 free_page((unsigned long)entry->data);
134 entry->data = NULL;
135 if (entry->kaddr) {
bb45e394
RW
136 if (entry->unmap) {
137 iounmap(entry->kaddr);
138 entry->unmap = false;
139 } else {
140 acpi_os_unmap_memory(entry->kaddr,
141 entry->size);
142 }
fce2b111
CH
143 entry->kaddr = NULL;
144 }
145 }
146}
147
148/**
dd4c4f17 149 * suspend_nvs_alloc - allocate memory necessary for saving NVS regions
fce2b111 150 */
dd4c4f17 151int suspend_nvs_alloc(void)
fce2b111
CH
152{
153 struct nvs_page *entry;
154
155 list_for_each_entry(entry, &nvs_list, node) {
156 entry->data = (void *)__get_free_page(GFP_KERNEL);
157 if (!entry->data) {
dd4c4f17 158 suspend_nvs_free();
fce2b111
CH
159 return -ENOMEM;
160 }
161 }
162 return 0;
163}
164
165/**
dd4c4f17 166 * suspend_nvs_save - save NVS memory regions
fce2b111 167 */
26fcaf60 168int suspend_nvs_save(void)
fce2b111
CH
169{
170 struct nvs_page *entry;
171
172 printk(KERN_INFO "PM: Saving platform NVS memory\n");
173
174 list_for_each_entry(entry, &nvs_list, node)
175 if (entry->data) {
bb45e394
RW
176 unsigned long phys = entry->phys_start;
177 unsigned int size = entry->size;
178
179 entry->kaddr = acpi_os_get_iomem(phys, size);
180 if (!entry->kaddr) {
181 entry->kaddr = acpi_os_ioremap(phys, size);
182 entry->unmap = !!entry->kaddr;
183 }
26fcaf60
JS
184 if (!entry->kaddr) {
185 suspend_nvs_free();
186 return -ENOMEM;
187 }
fce2b111
CH
188 memcpy(entry->data, entry->kaddr, entry->size);
189 }
26fcaf60
JS
190
191 return 0;
fce2b111
CH
192}
193
194/**
dd4c4f17 195 * suspend_nvs_restore - restore NVS memory regions
fce2b111
CH
196 *
197 * This function is going to be called with interrupts disabled, so it
198 * cannot iounmap the virtual addresses used to access the NVS region.
199 */
dd4c4f17 200void suspend_nvs_restore(void)
fce2b111
CH
201{
202 struct nvs_page *entry;
203
204 printk(KERN_INFO "PM: Restoring platform NVS memory\n");
205
206 list_for_each_entry(entry, &nvs_list, node)
207 if (entry->data)
208 memcpy(entry->kaddr, entry->data, entry->size);
209}
b54ac6d2 210#endif