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