1// SPDX-License-Identifier: GPL-2.0-only 2/* 3 * nvs.c - Routines for saving and restoring ACPI NVS memory region 4 * 5 * Copyright (C) 2008-2011 Rafael J. Wysocki <rjw@sisk.pl>, Novell Inc. 6 */ 7 8#include <linux/io.h> 9#include <linux/kernel.h> 10#include <linux/list.h> 11#include <linux/mm.h> 12#include <linux/slab.h> 13#include <linux/acpi.h> 14 15#include "internal.h" 16 17/* ACPI NVS regions, APEI may use it */ 18 19struct nvs_region { 20 __u64 phys_start; 21 __u64 size; 22 struct list_head node; 23}; 24 25static LIST_HEAD(nvs_region_list); 26 27#ifdef CONFIG_ACPI_SLEEP 28static int suspend_nvs_register(unsigned long start, unsigned long size); 29#else 30static inline int suspend_nvs_register(unsigned long a, unsigned long b) 31{ 32 return 0; 33} 34#endif 35 36int 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 50int 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 73struct 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 82static 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 */ 93static 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 */ 128void 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_iomem(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 */ 152int 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 */ 169int 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 */ 201void 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