nvs.c 3.6 KB

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