0001
0002
0003
0004
0005
0006
0007
0008 #define pr_fmt(fmt) "ACPI: PM: " fmt
0009
0010 #include <linux/io.h>
0011 #include <linux/kernel.h>
0012 #include <linux/list.h>
0013 #include <linux/mm.h>
0014 #include <linux/slab.h>
0015 #include <linux/acpi.h>
0016
0017 #include "internal.h"
0018
0019
0020
0021 struct nvs_region {
0022 __u64 phys_start;
0023 __u64 size;
0024 struct list_head node;
0025 };
0026
0027 static LIST_HEAD(nvs_region_list);
0028
0029 #ifdef CONFIG_ACPI_SLEEP
0030 static int suspend_nvs_register(unsigned long start, unsigned long size);
0031 #else
0032 static inline int suspend_nvs_register(unsigned long a, unsigned long b)
0033 {
0034 return 0;
0035 }
0036 #endif
0037
0038 int acpi_nvs_register(__u64 start, __u64 size)
0039 {
0040 struct nvs_region *region;
0041
0042 region = kmalloc(sizeof(*region), GFP_KERNEL);
0043 if (!region)
0044 return -ENOMEM;
0045 region->phys_start = start;
0046 region->size = size;
0047 list_add_tail(®ion->node, &nvs_region_list);
0048
0049 return suspend_nvs_register(start, size);
0050 }
0051
0052 int acpi_nvs_for_each_region(int (*func)(__u64 start, __u64 size, void *data),
0053 void *data)
0054 {
0055 int rc;
0056 struct nvs_region *region;
0057
0058 list_for_each_entry(region, &nvs_region_list, node) {
0059 rc = func(region->phys_start, region->size, data);
0060 if (rc)
0061 return rc;
0062 }
0063
0064 return 0;
0065 }
0066
0067
0068 #ifdef CONFIG_ACPI_SLEEP
0069
0070
0071
0072
0073
0074
0075 struct nvs_page {
0076 unsigned long phys_start;
0077 unsigned int size;
0078 void *kaddr;
0079 void *data;
0080 bool unmap;
0081 struct list_head node;
0082 };
0083
0084 static LIST_HEAD(nvs_list);
0085
0086
0087
0088
0089
0090
0091
0092
0093
0094
0095 static int suspend_nvs_register(unsigned long start, unsigned long size)
0096 {
0097 struct nvs_page *entry, *next;
0098
0099 pr_info("Registering ACPI NVS region [mem %#010lx-%#010lx] (%ld bytes)\n",
0100 start, start + size - 1, size);
0101
0102 while (size > 0) {
0103 unsigned int nr_bytes;
0104
0105 entry = kzalloc(sizeof(struct nvs_page), GFP_KERNEL);
0106 if (!entry)
0107 goto Error;
0108
0109 list_add_tail(&entry->node, &nvs_list);
0110 entry->phys_start = start;
0111 nr_bytes = PAGE_SIZE - (start & ~PAGE_MASK);
0112 entry->size = (size < nr_bytes) ? size : nr_bytes;
0113
0114 start += entry->size;
0115 size -= entry->size;
0116 }
0117 return 0;
0118
0119 Error:
0120 list_for_each_entry_safe(entry, next, &nvs_list, node) {
0121 list_del(&entry->node);
0122 kfree(entry);
0123 }
0124 return -ENOMEM;
0125 }
0126
0127
0128
0129
0130 void suspend_nvs_free(void)
0131 {
0132 struct nvs_page *entry;
0133
0134 list_for_each_entry(entry, &nvs_list, node)
0135 if (entry->data) {
0136 free_page((unsigned long)entry->data);
0137 entry->data = NULL;
0138 if (entry->kaddr) {
0139 if (entry->unmap) {
0140 iounmap(entry->kaddr);
0141 entry->unmap = false;
0142 } else {
0143 acpi_os_unmap_iomem(entry->kaddr,
0144 entry->size);
0145 }
0146 entry->kaddr = NULL;
0147 }
0148 }
0149 }
0150
0151
0152
0153
0154 int suspend_nvs_alloc(void)
0155 {
0156 struct nvs_page *entry;
0157
0158 list_for_each_entry(entry, &nvs_list, node) {
0159 entry->data = (void *)__get_free_page(GFP_KERNEL);
0160 if (!entry->data) {
0161 suspend_nvs_free();
0162 return -ENOMEM;
0163 }
0164 }
0165 return 0;
0166 }
0167
0168
0169
0170
0171 int suspend_nvs_save(void)
0172 {
0173 struct nvs_page *entry;
0174
0175 pr_info("Saving platform NVS memory\n");
0176
0177 list_for_each_entry(entry, &nvs_list, node)
0178 if (entry->data) {
0179 unsigned long phys = entry->phys_start;
0180 unsigned int size = entry->size;
0181
0182 entry->kaddr = acpi_os_get_iomem(phys, size);
0183 if (!entry->kaddr) {
0184 entry->kaddr = acpi_os_ioremap(phys, size);
0185 entry->unmap = !!entry->kaddr;
0186 }
0187 if (!entry->kaddr) {
0188 suspend_nvs_free();
0189 return -ENOMEM;
0190 }
0191 memcpy(entry->data, entry->kaddr, entry->size);
0192 }
0193
0194 return 0;
0195 }
0196
0197
0198
0199
0200
0201
0202
0203 void suspend_nvs_restore(void)
0204 {
0205 struct nvs_page *entry;
0206
0207 pr_info("Restoring platform NVS memory\n");
0208
0209 list_for_each_entry(entry, &nvs_list, node)
0210 if (entry->data)
0211 memcpy(entry->kaddr, entry->data, entry->size);
0212 }
0213 #endif