--- /dev/null
+// 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.
+
+#pragma once
+#include <stdint.h>
+#include <stdbool.h>
+#include "esp_err.h"
+
+// Include SoC-specific definitions. Only ESP32 supported for now.
+#include "esp32/pm.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief Power management constraints
+ */
+typedef enum {
+ /**
+ * Require CPU frequency to be at the maximum value set via esp_pm_configure.
+ * Argument is unused and should be set to 0.
+ */
+ ESP_PM_CPU_FREQ_MAX,
+ /**
+ * Require APB frequency to be at the maximum value supported by the chip.
+ * Argument is unused and should be set to 0.
+ */
+ ESP_PM_APB_FREQ_MAX,
+ /**
+ * Prevent the system from going into light sleep.
+ * Argument is unused and should be set to 0.
+ */
+ ESP_PM_NO_LIGHT_SLEEP,
+} esp_pm_lock_type_t;
+
+/**
+ * @brief Set implementation-specific power management configuration
+ * @param config pointer to implementation-specific configuration structure (e.g. esp_pm_config_esp32)
+ * @return
+ * - ESP_OK on success
+ * - ESP_ERR_INVALID_ARG if the configuration values are not correct
+ * - ESP_ERR_NOT_SUPPORTED if certain combination of values is not supported,
+ * or if CONFIG_PM_ENABLE is not enabled in sdkconfig
+ */
+esp_err_t esp_pm_configure(const void* config);
+
+
+/**
+ * @brief Opaque handle to the power management lock
+ */
+typedef struct esp_pm_lock* esp_pm_lock_handle_t;
+
+
+/**
+ * @brief Initialize a lock handle for certain power management parameter
+ *
+ * When lock is created, initially it is not taken.
+ * Call esp_pm_lock_acquire to take the lock.
+ *
+ * This function must not be called from an ISR.
+ *
+ * @param lock_type Power management constraint which the lock should control
+ * @param arg argument, value depends on lock_type, see esp_pm_lock_type_t
+ * @param name arbitrary string identifying the lock (e.g. "wifi" or "spi").
+ * Used by the esp_pm_dump_locks function to list existing locks.
+ * May be set to NULL. If not set to NULL, must point to a string which is valid
+ * for the lifetime of the lock.
+ * @param[out] out_handle handle returned from this function. Use this handle when calling
+ * esp_pm_lock_delete, esp_pm_lock_acquire, esp_pm_lock_release.
+ * Must not be NULL.
+ * @return
+ * - ESP_OK on success
+ * - ESP_ERR_NO_MEM if the lock structure can not be allocated
+ * - ESP_ERR_INVALID_ARG if out_handle is NULL or type argument is not valid
+ * - ESP_ERR_NOT_SUPPORTED if CONFIG_PM_ENABLE is not enabled in sdkconfig
+ */
+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);
+
+/**
+ * @brief Take a power management lock
+ *
+ * Once the lock is taken, power management algorithm will not switch to the
+ * mode specified in a call to esp_pm_lock_create, or any of the lower power
+ * modes (higher numeric values of 'mode').
+ *
+ * The lock is recursive, in the sense that if esp_pm_lock_acquire is called
+ * a number of times, esp_pm_lock_release has to be called the same number of
+ * times in order to release the lock.
+ *
+ * This function may be called from an ISR.
+ *
+ * This function is not thread-safe w.r.t. calls to other esp_pm_lock_*
+ * functions for the same handle.
+ *
+ * @param handle handle obtained from esp_pm_lock_create function
+ * @return
+ * - ESP_OK on success
+ * - ESP_ERR_INVALID_ARG if the handle is invalid
+ * - ESP_ERR_NOT_SUPPORTED if CONFIG_PM_ENABLE is not enabled in sdkconfig
+ */
+esp_err_t esp_pm_lock_acquire(esp_pm_lock_handle_t handle);
+
+/**
+ * @brief Release the lock taken using esp_pm_lock_acquire.
+ *
+ * Call to this functions removes power management restrictions placed when
+ * taking the lock.
+ *
+ * Locks are recursive, so if esp_pm_lock_acquire is called a number of times,
+ * esp_pm_lock_release has to be called the same number of times in order to
+ * actually release the lock.
+ *
+ * This function may be called from an ISR.
+ *
+ * This function is not thread-safe w.r.t. calls to other esp_pm_lock_*
+ * functions for the same handle.
+ *
+ * @param handle handle obtained from esp_pm_lock_create function
+ * @return
+ * - ESP_OK on success
+ * - ESP_ERR_INVALID_ARG if the handle is invalid
+ * - ESP_ERR_INVALID_STATE if lock is not acquired
+ * - ESP_ERR_NOT_SUPPORTED if CONFIG_PM_ENABLE is not enabled in sdkconfig
+ */
+esp_err_t esp_pm_lock_release(esp_pm_lock_handle_t handle);
+
+/**
+ * @brief Delete a lock created using esp_pm_lock
+ *
+ * The lock must be released before calling this function.
+ *
+ * This function must not be called from an ISR.
+ *
+ * @param handle handle obtained from esp_pm_lock_create function
+ * @return
+ * - ESP_OK on success
+ * - ESP_ERR_INVALID_ARG if the handle argument is NULL
+ * - ESP_ERR_INVALID_STATE if the lock is still acquired
+ * - ESP_ERR_NOT_SUPPORTED if CONFIG_PM_ENABLE is not enabled in sdkconfig
+ */
+esp_err_t esp_pm_lock_delete(esp_pm_lock_handle_t handle);
+
+/**
+ * Dump the list of all locks to stderr
+ *
+ * This function dumps debugging information about locks created using
+ * esp_pm_lock_create to an output stream.
+ *
+ * This function must not be called from an ISR. If esp_pm_lock_acquire/release
+ * are called while this function is running, inconsistent results may be
+ * reported.
+ *
+ * @param stream stream to print information to; use stdout or stderr to print
+ * to the console; use fmemopen/open_memstream to print to a
+ * string buffer.
+ * @return
+ * - ESP_OK on success
+ * - ESP_ERR_NOT_SUPPORTED if CONFIG_PM_ENABLE is not enabled in sdkconfig
+ */
+esp_err_t esp_pm_dump_locks(FILE* stream);
+
+
+
+#ifdef __cplusplus
+}
+#endif
--- /dev/null
+// 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;
+}
+
+