hibernate_nvs.c 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. /*
  2. * linux/kernel/power/hibernate_nvs.c - Routines for handling NVS memory
  3. *
  4. * Copyright (C) 2008,2009 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/suspend.h>
  13. /*
  14. * Platforms, like ACPI, may want us to save some memory used by them during
  15. * hibernation and to restore the contents of this memory during the subsequent
  16. * resume. The code below implements a mechanism allowing us to do that.
  17. */
  18. struct nvs_page {
  19. unsigned long phys_start;
  20. unsigned int size;
  21. void *kaddr;
  22. void *data;
  23. struct list_head node;
  24. };
  25. static LIST_HEAD(nvs_list);
  26. /**
  27. * hibernate_nvs_register - register platform NVS memory region to save
  28. * @start - physical address of the region
  29. * @size - size of the region
  30. *
  31. * The NVS region need not be page-aligned (both ends) and we arrange
  32. * things so that the data from page-aligned addresses in this region will
  33. * be copied into separate RAM pages.
  34. */
  35. int hibernate_nvs_register(unsigned long start, unsigned long size)
  36. {
  37. struct nvs_page *entry, *next;
  38. while (size > 0) {
  39. unsigned int nr_bytes;
  40. entry = kzalloc(sizeof(struct nvs_page), GFP_KERNEL);
  41. if (!entry)
  42. goto Error;
  43. list_add_tail(&entry->node, &nvs_list);
  44. entry->phys_start = start;
  45. nr_bytes = PAGE_SIZE - (start & ~PAGE_MASK);
  46. entry->size = (size < nr_bytes) ? size : nr_bytes;
  47. start += entry->size;
  48. size -= entry->size;
  49. }
  50. return 0;
  51. Error:
  52. list_for_each_entry_safe(entry, next, &nvs_list, node) {
  53. list_del(&entry->node);
  54. kfree(entry);
  55. }
  56. return -ENOMEM;
  57. }
  58. /**
  59. * hibernate_nvs_free - free data pages allocated for saving NVS regions
  60. */
  61. void hibernate_nvs_free(void)
  62. {
  63. struct nvs_page *entry;
  64. list_for_each_entry(entry, &nvs_list, node)
  65. if (entry->data) {
  66. free_page((unsigned long)entry->data);
  67. entry->data = NULL;
  68. if (entry->kaddr) {
  69. iounmap(entry->kaddr);
  70. entry->kaddr = NULL;
  71. }
  72. }
  73. }
  74. /**
  75. * hibernate_nvs_alloc - allocate memory necessary for saving NVS regions
  76. */
  77. int hibernate_nvs_alloc(void)
  78. {
  79. struct nvs_page *entry;
  80. list_for_each_entry(entry, &nvs_list, node) {
  81. entry->data = (void *)__get_free_page(GFP_KERNEL);
  82. if (!entry->data) {
  83. hibernate_nvs_free();
  84. return -ENOMEM;
  85. }
  86. }
  87. return 0;
  88. }
  89. /**
  90. * hibernate_nvs_save - save NVS memory regions
  91. */
  92. void hibernate_nvs_save(void)
  93. {
  94. struct nvs_page *entry;
  95. printk(KERN_INFO "PM: Saving platform NVS memory\n");
  96. list_for_each_entry(entry, &nvs_list, node)
  97. if (entry->data) {
  98. entry->kaddr = ioremap(entry->phys_start, entry->size);
  99. memcpy(entry->data, entry->kaddr, entry->size);
  100. }
  101. }
  102. /**
  103. * hibernate_nvs_restore - restore NVS memory regions
  104. *
  105. * This function is going to be called with interrupts disabled, so it
  106. * cannot iounmap the virtual addresses used to access the NVS region.
  107. */
  108. void hibernate_nvs_restore(void)
  109. {
  110. struct nvs_page *entry;
  111. printk(KERN_INFO "PM: Restoring platform NVS memory\n");
  112. list_for_each_entry(entry, &nvs_list, node)
  113. if (entry->data)
  114. memcpy(entry->kaddr, entry->data, entry->size);
  115. }