mirror of
				https://github.com/espressif/esp-idf.git
				synced 2025-11-04 00:51:42 +01:00 
			
		
		
		
	
		
			
	
	
		
			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;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 |