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