nvs.c 4.6 KB

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