~ubuntu-branches/ubuntu/precise/linux-lowlatency/precise

« back to all changes in this revision

Viewing changes to drivers/acpi/nvs.c

  • Committer: Package Import Robot
  • Author(s): Alessio Igor Bogani
  • Date: 2011-10-26 11:13:05 UTC
  • Revision ID: package-import@ubuntu.com-20111026111305-tz023xykf0i6eosh
Tags: upstream-3.2.0
ImportĀ upstreamĀ versionĀ 3.2.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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 <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
}