Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/drivers/acpi/nvs.c
15109 views
1
/*
2
* nvs.c - Routines for saving and restoring ACPI NVS memory region
3
*
4
* Copyright (C) 2008-2011 Rafael J. Wysocki <[email protected]>, 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 <linux/acpi_io.h>
16
#include <acpi/acpiosxf.h>
17
18
/*
19
* Platforms, like ACPI, may want us to save some memory used by them during
20
* suspend and to restore the contents of this memory during the subsequent
21
* resume. The code below implements a mechanism allowing us to do that.
22
*/
23
24
struct nvs_page {
25
unsigned long phys_start;
26
unsigned int size;
27
void *kaddr;
28
void *data;
29
bool unmap;
30
struct list_head node;
31
};
32
33
static LIST_HEAD(nvs_list);
34
35
/**
36
* suspend_nvs_register - register platform NVS memory region to save
37
* @start - physical address of the region
38
* @size - size of the region
39
*
40
* The NVS region need not be page-aligned (both ends) and we arrange
41
* things so that the data from page-aligned addresses in this region will
42
* be copied into separate RAM pages.
43
*/
44
int suspend_nvs_register(unsigned long start, unsigned long size)
45
{
46
struct nvs_page *entry, *next;
47
48
pr_info("PM: Registering ACPI NVS region at %lx (%ld bytes)\n",
49
start, size);
50
51
while (size > 0) {
52
unsigned int nr_bytes;
53
54
entry = kzalloc(sizeof(struct nvs_page), GFP_KERNEL);
55
if (!entry)
56
goto Error;
57
58
list_add_tail(&entry->node, &nvs_list);
59
entry->phys_start = start;
60
nr_bytes = PAGE_SIZE - (start & ~PAGE_MASK);
61
entry->size = (size < nr_bytes) ? size : nr_bytes;
62
63
start += entry->size;
64
size -= entry->size;
65
}
66
return 0;
67
68
Error:
69
list_for_each_entry_safe(entry, next, &nvs_list, node) {
70
list_del(&entry->node);
71
kfree(entry);
72
}
73
return -ENOMEM;
74
}
75
76
/**
77
* suspend_nvs_free - free data pages allocated for saving NVS regions
78
*/
79
void suspend_nvs_free(void)
80
{
81
struct nvs_page *entry;
82
83
list_for_each_entry(entry, &nvs_list, node)
84
if (entry->data) {
85
free_page((unsigned long)entry->data);
86
entry->data = NULL;
87
if (entry->kaddr) {
88
if (entry->unmap) {
89
iounmap(entry->kaddr);
90
entry->unmap = false;
91
} else {
92
acpi_os_unmap_memory(entry->kaddr,
93
entry->size);
94
}
95
entry->kaddr = NULL;
96
}
97
}
98
}
99
100
/**
101
* suspend_nvs_alloc - allocate memory necessary for saving NVS regions
102
*/
103
int suspend_nvs_alloc(void)
104
{
105
struct nvs_page *entry;
106
107
list_for_each_entry(entry, &nvs_list, node) {
108
entry->data = (void *)__get_free_page(GFP_KERNEL);
109
if (!entry->data) {
110
suspend_nvs_free();
111
return -ENOMEM;
112
}
113
}
114
return 0;
115
}
116
117
/**
118
* suspend_nvs_save - save NVS memory regions
119
*/
120
int suspend_nvs_save(void)
121
{
122
struct nvs_page *entry;
123
124
printk(KERN_INFO "PM: Saving platform NVS memory\n");
125
126
list_for_each_entry(entry, &nvs_list, node)
127
if (entry->data) {
128
unsigned long phys = entry->phys_start;
129
unsigned int size = entry->size;
130
131
entry->kaddr = acpi_os_get_iomem(phys, size);
132
if (!entry->kaddr) {
133
entry->kaddr = acpi_os_ioremap(phys, size);
134
entry->unmap = !!entry->kaddr;
135
}
136
if (!entry->kaddr) {
137
suspend_nvs_free();
138
return -ENOMEM;
139
}
140
memcpy(entry->data, entry->kaddr, entry->size);
141
}
142
143
return 0;
144
}
145
146
/**
147
* suspend_nvs_restore - restore NVS memory regions
148
*
149
* This function is going to be called with interrupts disabled, so it
150
* cannot iounmap the virtual addresses used to access the NVS region.
151
*/
152
void suspend_nvs_restore(void)
153
{
154
struct nvs_page *entry;
155
156
printk(KERN_INFO "PM: Restoring platform NVS memory\n");
157
158
list_for_each_entry(entry, &nvs_list, node)
159
if (entry->data)
160
memcpy(entry->kaddr, entry->data, entry->size);
161
}
162
163