pm_locks.c 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  1. // Copyright 2016-2017 Espressif Systems (Shanghai) PTE LTD
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. #include <stdlib.h>
  15. #include <string.h>
  16. #include <sys/lock.h>
  17. #include "esp_pm.h"
  18. #include "esp_system.h"
  19. #include "sys/queue.h"
  20. #include "freertos/FreeRTOS.h"
  21. #include "esp_private/pm_impl.h"
  22. #include "esp_timer.h"
  23. #include "sdkconfig.h"
  24. typedef struct esp_pm_lock {
  25. esp_pm_lock_type_t type; /*!< type passed to esp_pm_lock_create */
  26. int arg; /*!< argument passed to esp_pm_lock_create */
  27. pm_mode_t mode; /*!< implementation-defined mode for this type of lock*/
  28. const char* name; /*!< used to identify the lock */
  29. SLIST_ENTRY(esp_pm_lock) next; /*!< linked list pointer */
  30. size_t count; /*!< lock count */
  31. portMUX_TYPE spinlock; /*!< spinlock used when operating on 'count' */
  32. #ifdef WITH_PROFILING
  33. pm_time_t last_taken; /*!< time what the lock was taken (valid if count > 0) */
  34. pm_time_t time_held; /*!< total time the lock was taken.
  35. If count > 0, this doesn't include the time since last_taken */
  36. size_t times_taken; /*!< number of times the lock was ever taken */
  37. #endif
  38. } esp_pm_lock_t;
  39. static const char* s_lock_type_names[] = {
  40. "CPU_FREQ_MAX",
  41. "APB_FREQ_MAX",
  42. "NO_LIGHT_SLEEP"
  43. };
  44. /* List of all existing locks, used for esp_pm_dump_locks */
  45. static SLIST_HEAD(esp_pm_locks_head, esp_pm_lock) s_list =
  46. SLIST_HEAD_INITIALIZER(s_head);
  47. /* Protects the above list */
  48. static _lock_t s_list_lock;
  49. esp_err_t esp_pm_lock_create(esp_pm_lock_type_t lock_type, int arg,
  50. const char* name, esp_pm_lock_handle_t* out_handle)
  51. {
  52. #ifndef CONFIG_PM_ENABLE
  53. return ESP_ERR_NOT_SUPPORTED;
  54. #endif
  55. if (out_handle == NULL) {
  56. return ESP_ERR_INVALID_ARG;
  57. }
  58. esp_pm_lock_t* new_lock = (esp_pm_lock_t*) calloc(1, sizeof(*new_lock));
  59. if (!new_lock) {
  60. return ESP_ERR_NO_MEM;
  61. }
  62. new_lock->type = lock_type;
  63. new_lock->arg = arg;
  64. new_lock->mode = esp_pm_impl_get_mode(lock_type, arg);
  65. new_lock->name = name;
  66. new_lock->spinlock = (portMUX_TYPE) portMUX_INITIALIZER_UNLOCKED;
  67. *out_handle = new_lock;
  68. _lock_acquire(&s_list_lock);
  69. SLIST_INSERT_HEAD(&s_list, new_lock, next);
  70. _lock_release(&s_list_lock);
  71. return ESP_OK;
  72. }
  73. esp_err_t esp_pm_lock_delete(esp_pm_lock_handle_t handle)
  74. {
  75. #ifndef CONFIG_PM_ENABLE
  76. return ESP_ERR_NOT_SUPPORTED;
  77. #endif
  78. if (handle == NULL) {
  79. return ESP_ERR_INVALID_ARG;
  80. }
  81. if (handle->count > 0) {
  82. return ESP_ERR_INVALID_STATE;
  83. }
  84. _lock_acquire(&s_list_lock);
  85. SLIST_REMOVE(&s_list, handle, esp_pm_lock, next);
  86. _lock_release(&s_list_lock);
  87. free(handle);
  88. return ESP_OK;
  89. }
  90. esp_err_t IRAM_ATTR esp_pm_lock_acquire(esp_pm_lock_handle_t handle)
  91. {
  92. #ifndef CONFIG_PM_ENABLE
  93. return ESP_ERR_NOT_SUPPORTED;
  94. #endif
  95. if (handle == NULL) {
  96. return ESP_ERR_INVALID_ARG;
  97. }
  98. portENTER_CRITICAL_SAFE(&handle->spinlock);
  99. if (handle->count++ == 0) {
  100. pm_time_t now = 0;
  101. #ifdef WITH_PROFILING
  102. now = pm_get_time();
  103. #endif
  104. esp_pm_impl_switch_mode(handle->mode, MODE_LOCK, now);
  105. #ifdef WITH_PROFILING
  106. handle->last_taken = now;
  107. handle->times_taken++;
  108. #endif
  109. }
  110. portEXIT_CRITICAL_SAFE(&handle->spinlock);
  111. return ESP_OK;
  112. }
  113. esp_err_t IRAM_ATTR esp_pm_lock_release(esp_pm_lock_handle_t handle)
  114. {
  115. #ifndef CONFIG_PM_ENABLE
  116. return ESP_ERR_NOT_SUPPORTED;
  117. #endif
  118. if (handle == NULL) {
  119. return ESP_ERR_INVALID_ARG;
  120. }
  121. esp_err_t ret = ESP_OK;
  122. portENTER_CRITICAL_SAFE(&handle->spinlock);
  123. if (handle->count == 0) {
  124. ret = ESP_ERR_INVALID_STATE;
  125. goto out;
  126. }
  127. if (--handle->count == 0) {
  128. pm_time_t now = 0;
  129. #ifdef WITH_PROFILING
  130. now = pm_get_time();
  131. handle->time_held += now - handle->last_taken;
  132. #endif
  133. esp_pm_impl_switch_mode(handle->mode, MODE_UNLOCK, now);
  134. }
  135. out:
  136. portEXIT_CRITICAL_SAFE(&handle->spinlock);
  137. return ret;
  138. }
  139. esp_err_t esp_pm_dump_locks(FILE* stream)
  140. {
  141. #ifndef CONFIG_PM_ENABLE
  142. return ESP_ERR_NOT_SUPPORTED;
  143. #endif
  144. #ifdef WITH_PROFILING
  145. pm_time_t cur_time = pm_get_time();
  146. pm_time_t cur_time_d100 = cur_time / 100;
  147. #endif // WITH_PROFILING
  148. _lock_acquire(&s_list_lock);
  149. #ifdef WITH_PROFILING
  150. fprintf(stream, "Time since bootup: %lld us\n", cur_time);
  151. #endif
  152. fprintf(stream, "Lock stats:\n");
  153. #ifdef WITH_PROFILING
  154. fprintf(stream, "%-15s %-14s %-5s %-8s %-13s %-8s %-8s\n",
  155. "Name", "Type", "Arg", "Active", "Total_count", "Time(us)", "Time(%)");
  156. #else
  157. fprintf(stream, "%-15s %-14s %-5s %-8s\n", "Name", "Type", "Arg", "Active");
  158. #endif
  159. esp_pm_lock_t* it;
  160. char line[80];
  161. SLIST_FOREACH(it, &s_list, next) {
  162. char *buf = line;
  163. size_t len = sizeof(line);
  164. size_t cb;
  165. portENTER_CRITICAL(&it->spinlock);
  166. if (it->name == NULL) {
  167. cb = snprintf(buf, len, "lock@%p ", it);
  168. } else {
  169. cb = snprintf(buf, len, "%-15.15s ", it->name);
  170. }
  171. assert(cb <= len); // above formats should fit into sizeof(line)
  172. buf += cb;
  173. len -= cb;
  174. #ifdef WITH_PROFILING
  175. pm_time_t time_held = it->time_held;
  176. if (it->count > 0) {
  177. time_held += cur_time - it->last_taken;
  178. }
  179. snprintf(buf, len, "%-14s %-5d %-8d %-13d %-8lld %-3lld%%\n",
  180. s_lock_type_names[it->type], it->arg,
  181. it->count, it->times_taken, time_held,
  182. (time_held + cur_time_d100 - 1) / cur_time_d100);
  183. #else
  184. snprintf(buf, len, "%-14s %-5d %-8d\n", s_lock_type_names[it->type], it->arg, it->count);
  185. #endif // WITH_PROFILING
  186. portEXIT_CRITICAL(&it->spinlock);
  187. fputs(line, stream);
  188. }
  189. _lock_release(&s_list_lock);
  190. #ifdef WITH_PROFILING
  191. esp_pm_impl_dump_stats(stream);
  192. #endif
  193. return ESP_OK;
  194. }