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