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