206 lines
5.8 KiB
C
206 lines
5.8 KiB
C
|
// Copyright 2016-2017 Espressif Systems (Shanghai) PTE LTD
|
||
|
//
|
||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||
|
// you may not use this file except in compliance with the License.
|
||
|
// You may obtain a copy of the License at
|
||
|
//
|
||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||
|
//
|
||
|
// Unless required by applicable law or agreed to in writing, software
|
||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
|
// See the License for the specific language governing permissions and
|
||
|
// limitations under the License.
|
||
|
|
||
|
#include <stdlib.h>
|
||
|
#include <string.h>
|
||
|
#include <sys/lock.h>
|
||
|
#include "esp_pm.h"
|
||
|
#include "esp_system.h"
|
||
|
#include "rom/queue.h"
|
||
|
#include "freertos/FreeRTOS.h"
|
||
|
#include "pm_impl.h"
|
||
|
#include "esp_timer.h"
|
||
|
#include "sdkconfig.h"
|
||
|
|
||
|
|
||
|
typedef struct esp_pm_lock {
|
||
|
esp_pm_lock_type_t type; /*!< type passed to esp_pm_lock_create */
|
||
|
int arg; /*!< argument passed to esp_pm_lock_create */
|
||
|
pm_mode_t mode; /*!< implementation-defined mode for this type of lock*/
|
||
|
const char* name; /*!< used to identify the lock */
|
||
|
SLIST_ENTRY(esp_pm_lock) next; /*!< linked list pointer */
|
||
|
size_t count; /*!< lock count */
|
||
|
portMUX_TYPE spinlock; /*!< spinlock used when operating on 'count' */
|
||
|
#ifdef WITH_PROFILING
|
||
|
pm_time_t last_taken; /*!< time what the lock was taken (valid if count > 0) */
|
||
|
pm_time_t time_held; /*!< total time the lock was taken.
|
||
|
If count > 0, this doesn't include the time since last_taken */
|
||
|
size_t times_taken; /*!< number of times the lock was ever taken */
|
||
|
#endif
|
||
|
} esp_pm_lock_t;
|
||
|
|
||
|
|
||
|
static const char* s_lock_type_names[] = {
|
||
|
"CPU_FREQ_MAX",
|
||
|
"APB_FREQ_MAX",
|
||
|
"NO_LIGHT_SLEEP"
|
||
|
};
|
||
|
|
||
|
/* List of all existing locks, used for esp_pm_dump_locks */
|
||
|
static SLIST_HEAD(esp_pm_locks_head, esp_pm_lock) s_list =
|
||
|
SLIST_HEAD_INITIALIZER(s_head);
|
||
|
/* Protects the above list */
|
||
|
static _lock_t s_list_lock;
|
||
|
|
||
|
|
||
|
esp_err_t esp_pm_lock_create(esp_pm_lock_type_t lock_type, int arg,
|
||
|
const char* name, esp_pm_lock_handle_t* out_handle)
|
||
|
{
|
||
|
#ifndef CONFIG_PM_ENABLE
|
||
|
return ESP_ERR_NOT_SUPPORTED;
|
||
|
#endif
|
||
|
|
||
|
if (out_handle == NULL) {
|
||
|
return ESP_ERR_INVALID_ARG;
|
||
|
}
|
||
|
esp_pm_lock_t* new_lock = (esp_pm_lock_t*) calloc(1, sizeof(*new_lock));
|
||
|
if (!new_lock) {
|
||
|
return ESP_ERR_NO_MEM;
|
||
|
}
|
||
|
new_lock->type = lock_type;
|
||
|
new_lock->arg = arg;
|
||
|
new_lock->mode = esp_pm_impl_get_mode(lock_type, arg);
|
||
|
new_lock->name = name;
|
||
|
new_lock->spinlock = (portMUX_TYPE) portMUX_INITIALIZER_UNLOCKED;
|
||
|
*out_handle = new_lock;
|
||
|
|
||
|
_lock_acquire(&s_list_lock);
|
||
|
SLIST_INSERT_HEAD(&s_list, new_lock, next);
|
||
|
_lock_release(&s_list_lock);
|
||
|
return ESP_OK;
|
||
|
}
|
||
|
|
||
|
esp_err_t esp_pm_lock_delete(esp_pm_lock_handle_t handle)
|
||
|
{
|
||
|
#ifndef CONFIG_PM_ENABLE
|
||
|
return ESP_ERR_NOT_SUPPORTED;
|
||
|
#endif
|
||
|
|
||
|
if (handle == NULL) {
|
||
|
return ESP_ERR_INVALID_ARG;
|
||
|
}
|
||
|
|
||
|
if (handle->count > 0) {
|
||
|
return ESP_ERR_INVALID_STATE;
|
||
|
}
|
||
|
_lock_acquire(&s_list_lock);
|
||
|
SLIST_REMOVE(&s_list, handle, esp_pm_lock, next);
|
||
|
_lock_release(&s_list_lock);
|
||
|
free(handle);
|
||
|
return ESP_OK;
|
||
|
}
|
||
|
|
||
|
esp_err_t IRAM_ATTR esp_pm_lock_acquire(esp_pm_lock_handle_t handle)
|
||
|
{
|
||
|
#ifndef CONFIG_PM_ENABLE
|
||
|
return ESP_ERR_NOT_SUPPORTED;
|
||
|
#endif
|
||
|
|
||
|
if (handle == NULL) {
|
||
|
return ESP_ERR_INVALID_ARG;
|
||
|
}
|
||
|
|
||
|
portENTER_CRITICAL(&handle->spinlock);
|
||
|
if (handle->count++ == 0) {
|
||
|
pm_time_t now = 0;
|
||
|
#ifdef WITH_PROFILING
|
||
|
now = pm_get_time();
|
||
|
#endif
|
||
|
esp_pm_impl_switch_mode(handle->mode, MODE_LOCK, now);
|
||
|
#ifdef WITH_PROFILING
|
||
|
handle->last_taken = now;
|
||
|
handle->times_taken++;
|
||
|
#endif
|
||
|
}
|
||
|
portEXIT_CRITICAL(&handle->spinlock);
|
||
|
return ESP_OK;
|
||
|
}
|
||
|
|
||
|
esp_err_t IRAM_ATTR esp_pm_lock_release(esp_pm_lock_handle_t handle)
|
||
|
{
|
||
|
#ifndef CONFIG_PM_ENABLE
|
||
|
return ESP_ERR_NOT_SUPPORTED;
|
||
|
#endif
|
||
|
|
||
|
if (handle == NULL) {
|
||
|
return ESP_ERR_INVALID_ARG;
|
||
|
}
|
||
|
esp_err_t ret = ESP_OK;
|
||
|
portENTER_CRITICAL(&handle->spinlock);
|
||
|
if (handle->count == 0) {
|
||
|
ret = ESP_ERR_INVALID_STATE;
|
||
|
goto out;
|
||
|
}
|
||
|
if (--handle->count == 0) {
|
||
|
pm_time_t now = 0;
|
||
|
#ifdef WITH_PROFILING
|
||
|
now = pm_get_time();
|
||
|
handle->time_held += now - handle->last_taken;
|
||
|
#endif
|
||
|
esp_pm_impl_switch_mode(handle->mode, MODE_UNLOCK, now);
|
||
|
}
|
||
|
out:
|
||
|
portEXIT_CRITICAL(&handle->spinlock);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
|
||
|
esp_err_t esp_pm_dump_locks(FILE* stream)
|
||
|
{
|
||
|
#ifndef CONFIG_PM_ENABLE
|
||
|
return ESP_ERR_NOT_SUPPORTED;
|
||
|
#endif
|
||
|
|
||
|
#ifdef WITH_PROFILING
|
||
|
pm_time_t cur_time = pm_get_time();
|
||
|
pm_time_t cur_time_d100 = cur_time / 100;
|
||
|
#endif // WITH_PROFILING
|
||
|
|
||
|
_lock_acquire(&s_list_lock);
|
||
|
#ifdef WITH_PROFILING
|
||
|
fprintf(stream, "Time: %lld\n", cur_time);
|
||
|
#endif
|
||
|
|
||
|
fprintf(stream, "Lock stats:\n");
|
||
|
esp_pm_lock_t* it;
|
||
|
SLIST_FOREACH(it, &s_list, next) {
|
||
|
portENTER_CRITICAL(&it->spinlock);
|
||
|
if (it->name == NULL) {
|
||
|
fprintf(stream, "lock@%p ", it);
|
||
|
} else {
|
||
|
fprintf(stream, "%-15s ", it->name);
|
||
|
}
|
||
|
#ifdef WITH_PROFILING
|
||
|
pm_time_t time_held = it->time_held;
|
||
|
if (it->count > 0) {
|
||
|
time_held += cur_time - it->last_taken;
|
||
|
}
|
||
|
fprintf(stream, "%10s %3d %3d %9d %9lld %3lld%%\n",
|
||
|
s_lock_type_names[it->type], it->arg,
|
||
|
it->count, it->times_taken, time_held,
|
||
|
(time_held + cur_time_d100 - 1) / cur_time_d100);
|
||
|
#else
|
||
|
fprintf(stream, "%10s %3d %3d\n", s_lock_type_names[it->type], it->arg, it->count);
|
||
|
#endif // WITH_PROFILING
|
||
|
portEXIT_CRITICAL(&it->spinlock);
|
||
|
}
|
||
|
_lock_release(&s_list_lock);
|
||
|
#ifdef WITH_PROFILING
|
||
|
esp_pm_impl_dump_stats(stream);
|
||
|
#endif
|
||
|
return ESP_OK;
|
||
|
}
|
||
|
|
||
|
|