mirror of
https://github.com/espressif/esp-idf
synced 2025-03-10 01:29:21 -04:00
Merge branch 'feature/dfs' into 'master'
Dynamic frequency scaling See merge request !1189
This commit is contained in:
commit
59b7d98fec
@ -63,13 +63,13 @@ config SYSVIEW_ENABLE
|
||||
help
|
||||
Enables supporrt for SEGGER SystemView tracing functionality.
|
||||
|
||||
if !FREERTOS_UNICORE
|
||||
choice SYSVIEW_TS_SOURCE
|
||||
prompt "ESP32 timer to use as SystemView timestamp source"
|
||||
depends on SYSVIEW_ENABLE
|
||||
default SYSVIEW_TS_SOURCE_TIMER_00
|
||||
help
|
||||
SystemView needs one source for timestamps when tracing events from both cores.
|
||||
SystemView needs to use a hardware timer as the source of timestamps
|
||||
when tracing
|
||||
This option selects HW timer for it.
|
||||
|
||||
config SYSVIEW_TS_SOURCE_TIMER_00
|
||||
@ -93,7 +93,6 @@ config SYSVIEW_TS_SOURCE_TIMER_11
|
||||
Select this to use timer 1 of group 1
|
||||
|
||||
endchoice
|
||||
endif #FREERTOS_UNICORE
|
||||
|
||||
config SYSVIEW_EVT_OVERFLOW_ENABLE
|
||||
bool "Trace Buffer Overflow Event"
|
||||
|
@ -203,8 +203,6 @@ const static char *TAG = "esp_apptrace";
|
||||
#define ESP_APPTRACE_LOGV( format, ... ) ESP_APPTRACE_LOG_LEV(V, ESP_LOG_VERBOSE, format, ##__VA_ARGS__)
|
||||
#define ESP_APPTRACE_LOGO( format, ... ) ESP_APPTRACE_LOG_LEV(E, ESP_LOG_NONE, format, ##__VA_ARGS__)
|
||||
|
||||
#define ESP_APPTRACE_CPUTICKS2US(_t_) ((_t_)/(XT_CLOCK_FREQ/1000000))
|
||||
|
||||
// TODO: move these (and same definitions in trax.c to dport_reg.h)
|
||||
#define TRACEMEM_MUX_PROBLK0_APPBLK1 0
|
||||
#define TRACEMEM_MUX_BLK0_ONLY 1
|
||||
|
@ -15,23 +15,24 @@
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "esp_app_trace_util.h"
|
||||
#include "esp_clk.h"
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////// TIMEOUT /////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// TODO: get actual clock from PLL config
|
||||
#define ESP_APPTRACE_CPUTICKS2US(_t_) ((_t_)/(XT_CLOCK_FREQ/1000000))
|
||||
#define ESP_APPTRACE_US2CPUTICKS(_t_) ((_t_)*(XT_CLOCK_FREQ/1000000))
|
||||
#define ESP_APPTRACE_CPUTICKS2US(_t_, _cpu_freq_) ((_t_)/(_cpu_freq_/1000000))
|
||||
#define ESP_APPTRACE_US2CPUTICKS(_t_, _cpu_freq_) ((_t_)*(_cpu_freq_/1000000))
|
||||
|
||||
esp_err_t esp_apptrace_tmo_check(esp_apptrace_tmo_t *tmo)
|
||||
{
|
||||
int cpu_freq = esp_clk_cpu_freq();
|
||||
if (tmo->tmo != ESP_APPTRACE_TMO_INFINITE) {
|
||||
unsigned cur = portGET_RUN_TIME_COUNTER_VALUE();
|
||||
if (tmo->start <= cur) {
|
||||
tmo->elapsed = ESP_APPTRACE_CPUTICKS2US(cur - tmo->start);
|
||||
tmo->elapsed = ESP_APPTRACE_CPUTICKS2US(cur - tmo->start, cpu_freq);
|
||||
} else {
|
||||
tmo->elapsed = ESP_APPTRACE_CPUTICKS2US(0xFFFFFFFF - tmo->start + cur);
|
||||
tmo->elapsed = ESP_APPTRACE_CPUTICKS2US(0xFFFFFFFF - tmo->start + cur, cpu_freq);
|
||||
}
|
||||
if (tmo->elapsed >= tmo->tmo) {
|
||||
return ESP_ERR_TIMEOUT;
|
||||
|
@ -70,6 +70,7 @@ Revision: $Rev: 3734 $
|
||||
#include "esp_app_trace.h"
|
||||
#include "esp_app_trace_util.h"
|
||||
#include "esp_intr_alloc.h"
|
||||
#include "esp_clk.h"
|
||||
|
||||
extern const SEGGER_SYSVIEW_OS_API SYSVIEW_X_OS_TraceAPI;
|
||||
|
||||
@ -85,14 +86,12 @@ extern const SEGGER_SYSVIEW_OS_API SYSVIEW_X_OS_TraceAPI;
|
||||
// The target device name
|
||||
#define SYSVIEW_DEVICE_NAME "ESP32"
|
||||
|
||||
// Timer group timer divisor
|
||||
#define SYSVIEW_TIMER_DIV 2
|
||||
// Frequency of the timestamp.
|
||||
#if CONFIG_FREERTOS_UNICORE == 0
|
||||
#define SYSVIEW_TIMESTAMP_FREQ (TIMER_BASE_CLK/2)
|
||||
#else
|
||||
#define SYSVIEW_TIMESTAMP_FREQ (XT_CLOCK_FREQ)
|
||||
#endif
|
||||
#define SYSVIEW_TIMESTAMP_FREQ (esp_clk_apb_freq() / SYSVIEW_TIMER_DIV)
|
||||
// System Frequency.
|
||||
#define SYSVIEW_CPU_FREQ (XT_CLOCK_FREQ)
|
||||
#define SYSVIEW_CPU_FREQ (esp_clk_cpu_freq())
|
||||
|
||||
// The lowest RAM address used for IDs (pointers)
|
||||
#define SYSVIEW_RAM_BASE (0x3F400000)
|
||||
@ -104,10 +103,8 @@ extern const SEGGER_SYSVIEW_OS_API SYSVIEW_X_OS_TraceAPI;
|
||||
#define SYSTICK_INTR_ID (ETS_INTERNAL_TIMER1_INTR_SOURCE+ETS_INTERNAL_INTR_SOURCE_OFF)
|
||||
#endif
|
||||
|
||||
#if CONFIG_FREERTOS_UNICORE == 0
|
||||
static timer_idx_t s_ts_timer_idx;
|
||||
static timer_group_t s_ts_timer_group;
|
||||
#endif
|
||||
|
||||
// SystemView is single core specific: it implies that SEGGER_SYSVIEW_LOCK()
|
||||
// disables IRQs (disables rescheduling globaly). So we can not use finite timeouts for locks and return error
|
||||
@ -214,7 +211,6 @@ static void _cbSendSystemDesc(void) {
|
||||
*
|
||||
**********************************************************************
|
||||
*/
|
||||
#if CONFIG_FREERTOS_UNICORE == 0
|
||||
static void SEGGER_SYSVIEW_TS_Init()
|
||||
{
|
||||
timer_config_t config;
|
||||
@ -238,7 +234,7 @@ static void SEGGER_SYSVIEW_TS_Init()
|
||||
config.alarm_en = 0;
|
||||
config.auto_reload = 0;
|
||||
config.counter_dir = TIMER_COUNT_UP;
|
||||
config.divider = 2;
|
||||
config.divider = SYSVIEW_TIMER_DIV;
|
||||
config.counter_en = 0;
|
||||
/*Configure timer*/
|
||||
timer_init(s_ts_timer_group, s_ts_timer_idx, &config);
|
||||
@ -247,14 +243,11 @@ static void SEGGER_SYSVIEW_TS_Init()
|
||||
/*Enable timer interrupt*/
|
||||
timer_start(s_ts_timer_group, s_ts_timer_idx);
|
||||
}
|
||||
#endif
|
||||
|
||||
void SEGGER_SYSVIEW_Conf(void) {
|
||||
U32 disable_evts = 0;
|
||||
|
||||
#if CONFIG_FREERTOS_UNICORE == 0
|
||||
SEGGER_SYSVIEW_TS_Init();
|
||||
#endif
|
||||
SEGGER_SYSVIEW_Init(SYSVIEW_TIMESTAMP_FREQ, SYSVIEW_CPU_FREQ,
|
||||
&SYSVIEW_X_OS_TraceAPI, _cbSendSystemDesc);
|
||||
SEGGER_SYSVIEW_SetRAMBase(SYSVIEW_RAM_BASE);
|
||||
|
@ -26,16 +26,6 @@ const static char *TAG = "segger_rtt";
|
||||
|
||||
#define SYSVIEW_EVENTS_BUF_SZ 255U
|
||||
|
||||
#if SYSVIEW_RTT_MAX_DATA_RATE > 0
|
||||
#include "SEGGER_SYSVIEW_Conf.h"
|
||||
#if CONFIG_FREERTOS_UNICORE == 0
|
||||
#include "driver/timer.h"
|
||||
#define SYSVIEW_TIMESTAMP_FREQ (TIMER_BASE_CLK/2)
|
||||
#else
|
||||
#define SYSVIEW_TIMESTAMP_FREQ (XT_CLOCK_FREQ)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// size of down channel data buf
|
||||
#define SYSVIEW_DOWN_BUF_SIZE 32
|
||||
#define SEGGER_HOST_WAIT_TMO 500 //us
|
||||
|
@ -87,8 +87,6 @@ static void esp_apptrace_test_timer_init(int timer_group, int timer_idx, uint32_
|
||||
#define ESP_APPTRACE_TEST_WRITE_FROM_ISR(_b_, _s_) esp_apptrace_write(ESP_APPTRACE_DEST_TRAX, _b_, _s_, 0UL)
|
||||
#define ESP_APPTRACE_TEST_WRITE_NOWAIT(_b_, _s_) esp_apptrace_write(ESP_APPTRACE_DEST_TRAX, _b_, _s_, 0)
|
||||
|
||||
#define ESP_APPTRACE_TEST_CPUTICKS2US(_t_) ((_t_)/(XT_CLOCK_FREQ/1000000))
|
||||
|
||||
typedef struct {
|
||||
uint8_t *buf;
|
||||
uint32_t buf_sz;
|
||||
|
@ -33,6 +33,7 @@
|
||||
#include "bt.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_pm.h"
|
||||
|
||||
#if CONFIG_BT_ENABLED
|
||||
|
||||
@ -147,6 +148,10 @@ static esp_bt_controller_status_t btdm_controller_status = ESP_BT_CONTROLLER_STA
|
||||
|
||||
static portMUX_TYPE global_int_mux = portMUX_INITIALIZER_UNLOCKED;
|
||||
|
||||
#ifdef CONFIG_PM_ENABLE
|
||||
static esp_pm_lock_handle_t s_pm_lock;
|
||||
#endif
|
||||
|
||||
static void IRAM_ATTR interrupt_disable(void)
|
||||
{
|
||||
portENTER_CRITICAL(&global_int_mux);
|
||||
@ -442,6 +447,13 @@ esp_err_t esp_bt_controller_init(esp_bt_controller_config_t *cfg)
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_ENABLE
|
||||
esp_err_t err = esp_pm_lock_create(ESP_PM_APB_FREQ_MAX, 0, "bt", &s_pm_lock);
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
#endif
|
||||
|
||||
btdm_osi_funcs_register(&osi_funcs);
|
||||
|
||||
btdm_controller_mem_init();
|
||||
@ -450,6 +462,10 @@ esp_err_t esp_bt_controller_init(esp_bt_controller_config_t *cfg)
|
||||
|
||||
ret = btdm_controller_init(btdm_cfg_mask, cfg);
|
||||
if (ret) {
|
||||
#ifdef CONFIG_PM_ENABLE
|
||||
esp_pm_lock_delete(s_pm_lock);
|
||||
s_pm_lock = NULL;
|
||||
#endif
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
@ -468,6 +484,12 @@ esp_err_t esp_bt_controller_deinit(void)
|
||||
}
|
||||
|
||||
btdm_controller_status = ESP_BT_CONTROLLER_STATUS_IDLE;
|
||||
|
||||
#ifdef CONFIG_PM_ENABLE
|
||||
esp_pm_lock_delete(s_pm_lock);
|
||||
s_pm_lock = NULL;
|
||||
#endif
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
@ -484,6 +506,10 @@ esp_err_t esp_bt_controller_enable(esp_bt_mode_t mode)
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_ENABLE
|
||||
esp_pm_lock_acquire(s_pm_lock);
|
||||
#endif
|
||||
|
||||
esp_phy_load_cal_and_init();
|
||||
|
||||
if (btdm_bb_init_flag == false) {
|
||||
@ -519,6 +545,10 @@ esp_err_t esp_bt_controller_disable(void)
|
||||
btdm_controller_status = ESP_BT_CONTROLLER_STATUS_INITED;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_ENABLE
|
||||
esp_pm_lock_release(s_pm_lock);
|
||||
#endif
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
|
@ -106,7 +106,8 @@ typedef struct {
|
||||
uart_parity_t parity; /*!< UART parity mode*/
|
||||
uart_stop_bits_t stop_bits; /*!< UART stop bits*/
|
||||
uart_hw_flowcontrol_t flow_ctrl; /*!< UART HW flow control mode (cts/rts)*/
|
||||
uint8_t rx_flow_ctrl_thresh ; /*!< UART HW RTS threshold*/
|
||||
uint8_t rx_flow_ctrl_thresh; /*!< UART HW RTS threshold*/
|
||||
bool use_ref_tick; /*!< Set to true if UART should be clocked from REF_TICK */
|
||||
} uart_config_t;
|
||||
|
||||
/**
|
||||
|
@ -15,6 +15,7 @@
|
||||
#include <string.h>
|
||||
#include "esp_err.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_pm.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/queue.h"
|
||||
#include "freertos/semphr.h"
|
||||
@ -67,6 +68,9 @@ static sdmmc_desc_t s_dma_desc[SDMMC_DMA_DESC_CNT];
|
||||
static sdmmc_transfer_state_t s_cur_transfer = { 0 };
|
||||
static QueueHandle_t s_request_mutex;
|
||||
static bool s_is_app_cmd; // This flag is set if the next command is an APP command
|
||||
#ifdef CONFIG_PM_ENABLE
|
||||
static esp_pm_lock_handle_t s_pm_lock;
|
||||
#endif
|
||||
|
||||
static esp_err_t handle_idle_state_events();
|
||||
static sdmmc_hw_cmd_t make_hw_cmd(sdmmc_command_t* cmd);
|
||||
@ -83,12 +87,24 @@ esp_err_t sdmmc_host_transaction_handler_init()
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
s_is_app_cmd = false;
|
||||
#ifdef CONFIG_PM_ENABLE
|
||||
esp_err_t err = esp_pm_lock_create(ESP_PM_APB_FREQ_MAX, 0, "sdmmc", &s_pm_lock);
|
||||
if (err != ESP_OK) {
|
||||
vSemaphoreDelete(s_request_mutex);
|
||||
s_request_mutex = NULL;
|
||||
return err;
|
||||
}
|
||||
#endif
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
void sdmmc_host_transaction_handler_deinit()
|
||||
{
|
||||
assert(s_request_mutex);
|
||||
#ifdef CONFIG_PM_ENABLE
|
||||
esp_pm_lock_delete(s_pm_lock);
|
||||
s_pm_lock = NULL;
|
||||
#endif
|
||||
vSemaphoreDelete(s_request_mutex);
|
||||
s_request_mutex = NULL;
|
||||
}
|
||||
@ -96,6 +112,9 @@ void sdmmc_host_transaction_handler_deinit()
|
||||
esp_err_t sdmmc_host_do_transaction(int slot, sdmmc_command_t* cmdinfo)
|
||||
{
|
||||
xSemaphoreTake(s_request_mutex, portMAX_DELAY);
|
||||
#ifdef CONFIG_PM_ENABLE
|
||||
esp_pm_lock_acquire(s_pm_lock);
|
||||
#endif
|
||||
// dispose of any events which happened asynchronously
|
||||
handle_idle_state_events();
|
||||
// convert cmdinfo to hardware register value
|
||||
@ -141,6 +160,9 @@ esp_err_t sdmmc_host_do_transaction(int slot, sdmmc_command_t* cmdinfo)
|
||||
}
|
||||
}
|
||||
s_is_app_cmd = (ret == ESP_OK && cmdinfo->opcode == MMC_APP_CMD);
|
||||
#ifdef CONFIG_PM_ENABLE
|
||||
esp_pm_lock_release(s_pm_lock);
|
||||
#endif
|
||||
xSemaphoreGive(s_request_mutex);
|
||||
return ret;
|
||||
}
|
||||
|
@ -47,6 +47,7 @@ queue and re-enabling the interrupt will trigger the interrupt again, which can
|
||||
#include "esp_intr_alloc.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_pm.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "freertos/xtensa_api.h"
|
||||
@ -84,6 +85,9 @@ typedef struct {
|
||||
bool no_gpio_matrix;
|
||||
int dma_chan;
|
||||
int max_transfer_sz;
|
||||
#ifdef CONFIG_PM_ENABLE
|
||||
esp_pm_lock_handle_t pm_lock;
|
||||
#endif
|
||||
} spi_host_t;
|
||||
|
||||
struct spi_device_t {
|
||||
@ -129,6 +133,13 @@ esp_err_t spi_bus_initialize(spi_host_device_t host, const spi_bus_config_t *bus
|
||||
spihost[host]=malloc(sizeof(spi_host_t));
|
||||
if (spihost[host]==NULL) goto nomem;
|
||||
memset(spihost[host], 0, sizeof(spi_host_t));
|
||||
#ifdef CONFIG_PM_ENABLE
|
||||
esp_err_t err = esp_pm_lock_create(ESP_PM_APB_FREQ_MAX, 0, "spi_master",
|
||||
&spihost[host]->pm_lock);
|
||||
if (err != ESP_OK) {
|
||||
goto nomem;
|
||||
}
|
||||
#endif //CONFIG_PM_ENABLE
|
||||
|
||||
spicommon_bus_initialize_io(host, bus_config, dma_chan, SPICOMMON_BUSFLAG_MASTER|SPICOMMON_BUSFLAG_QUAD, &native);
|
||||
spihost[host]->no_gpio_matrix=native;
|
||||
@ -180,6 +191,11 @@ nomem:
|
||||
if (spihost[host]) {
|
||||
free(spihost[host]->dmadesc_tx);
|
||||
free(spihost[host]->dmadesc_rx);
|
||||
#ifdef CONFIG_PM_ENABLE
|
||||
if (spihost[host]->pm_lock) {
|
||||
esp_pm_lock_delete(spihost[host]->pm_lock);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
free(spihost[host]);
|
||||
spicommon_periph_free(host);
|
||||
@ -199,6 +215,9 @@ esp_err_t spi_bus_free(spi_host_device_t host)
|
||||
if ( spihost[host]->dma_chan > 0 ) {
|
||||
spicommon_dma_chan_free ( spihost[host]->dma_chan );
|
||||
}
|
||||
#ifdef CONFIG_PM_ENABLE
|
||||
esp_pm_lock_delete(spihost[host]->pm_lock);
|
||||
#endif
|
||||
spihost[host]->hw->slave.trans_inten=0;
|
||||
spihost[host]->hw->slave.trans_done=0;
|
||||
esp_intr_free(spihost[host]->intr);
|
||||
@ -412,6 +431,10 @@ static void IRAM_ATTR spi_intr(void *arg)
|
||||
if (i==NO_CS) {
|
||||
//No packet waiting. Disable interrupt.
|
||||
esp_intr_disable(host->intr);
|
||||
#ifdef CONFIG_PM_ENABLE
|
||||
//Release APB frequency lock
|
||||
esp_pm_lock_release(host->pm_lock);
|
||||
#endif
|
||||
} else {
|
||||
host->hw->slave.trans_done=0; //clear int bit
|
||||
//We have a transaction. Send it.
|
||||
@ -649,6 +672,9 @@ esp_err_t spi_device_queue_trans(spi_device_handle_t handle, spi_transaction_t *
|
||||
// else use the original buffer (forced-conversion) or assign to NULL
|
||||
trans_buf.buffer_to_send = (uint32_t*)txdata;
|
||||
}
|
||||
#ifdef CONFIG_PM_ENABLE
|
||||
esp_pm_lock_acquire(handle->host->pm_lock);
|
||||
#endif
|
||||
|
||||
r=xQueueSend(handle->trans_queue, (void*)&trans_buf, ticks_to_wait);
|
||||
if (!r) return ESP_ERR_TIMEOUT;
|
||||
|
@ -26,6 +26,7 @@
|
||||
#include "esp_intr_alloc.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_pm.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "freertos/xtensa_api.h"
|
||||
@ -60,6 +61,9 @@ typedef struct {
|
||||
QueueHandle_t trans_queue;
|
||||
QueueHandle_t ret_queue;
|
||||
int dma_chan;
|
||||
#ifdef CONFIG_PM_ENABLE
|
||||
esp_pm_lock_handle_t pm_lock;
|
||||
#endif
|
||||
} spi_slave_t;
|
||||
|
||||
static spi_slave_t *spihost[3];
|
||||
@ -106,6 +110,15 @@ esp_err_t spi_slave_initialize(spi_host_device_t host, const spi_bus_config_t *b
|
||||
//We're limited to non-DMA transfers: the SPI work registers can hold 64 bytes at most.
|
||||
spihost[host]->max_transfer_sz = 16 * 4;
|
||||
}
|
||||
#ifdef CONFIG_PM_ENABLE
|
||||
esp_err_t err = esp_pm_lock_create(ESP_PM_APB_FREQ_MAX, 0, "spi_slave",
|
||||
&spihost[host]->pm_lock);
|
||||
if (err != ESP_OK) {
|
||||
goto nomem;
|
||||
}
|
||||
// Lock APB frequency while SPI slave driver is in use
|
||||
esp_pm_lock_acquire(spihost[host]->pm_lock);
|
||||
#endif //CONFIG_PM_ENABLE
|
||||
|
||||
//Create queues
|
||||
spihost[host]->trans_queue = xQueueCreate(slave_config->queue_size, sizeof(spi_slave_transaction_t *));
|
||||
@ -184,6 +197,12 @@ nomem:
|
||||
if (spihost[host]->ret_queue) vQueueDelete(spihost[host]->ret_queue);
|
||||
free(spihost[host]->dmadesc_tx);
|
||||
free(spihost[host]->dmadesc_rx);
|
||||
#ifdef CONFIG_PM_ENABLE
|
||||
if (spihost[host]->pm_lock) {
|
||||
esp_pm_lock_release(spihost[host]->pm_lock);
|
||||
esp_pm_lock_delete(spihost[host]->pm_lock);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
free(spihost[host]);
|
||||
spihost[host] = NULL;
|
||||
@ -203,6 +222,10 @@ esp_err_t spi_slave_free(spi_host_device_t host)
|
||||
}
|
||||
free(spihost[host]->dmadesc_tx);
|
||||
free(spihost[host]->dmadesc_rx);
|
||||
#ifdef CONFIG_PM_ENABLE
|
||||
esp_pm_lock_release(spihost[host]->pm_lock);
|
||||
esp_pm_lock_delete(spihost[host]->pm_lock);
|
||||
#endif //CONFIG_PM_ENABLE
|
||||
free(spihost[host]);
|
||||
spihost[host] = NULL;
|
||||
spicommon_periph_free(host);
|
||||
|
@ -18,6 +18,7 @@
|
||||
#include "esp_intr_alloc.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_clk.h"
|
||||
#include "malloc.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/semphr.h"
|
||||
@ -172,13 +173,25 @@ esp_err_t uart_get_parity(uart_port_t uart_num, uart_parity_t* parity_mode)
|
||||
esp_err_t uart_set_baudrate(uart_port_t uart_num, uint32_t baud_rate)
|
||||
{
|
||||
UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_FAIL);
|
||||
UART_CHECK((baud_rate <= UART_BITRATE_MAX), "baud_rate error", ESP_FAIL);
|
||||
uint32_t clk_div = (((UART_CLK_FREQ) << 4) / baud_rate);
|
||||
esp_err_t ret = ESP_OK;
|
||||
UART_ENTER_CRITICAL(&uart_spinlock[uart_num]);
|
||||
UART[uart_num]->clk_div.div_int = clk_div >> 4;
|
||||
UART[uart_num]->clk_div.div_frag = clk_div & 0xf;
|
||||
int uart_clk_freq;
|
||||
if (UART[uart_num]->conf0.tick_ref_always_on == 0) {
|
||||
/* this UART has been configured to use REF_TICK */
|
||||
uart_clk_freq = REF_CLK_FREQ;
|
||||
} else {
|
||||
uart_clk_freq = esp_clk_apb_freq();
|
||||
}
|
||||
uint32_t clk_div = (((uart_clk_freq) << 4) / baud_rate);
|
||||
if (clk_div < 16) {
|
||||
/* baud rate is too high for this clock frequency */
|
||||
ret = ESP_ERR_INVALID_ARG;
|
||||
} else {
|
||||
UART[uart_num]->clk_div.div_int = clk_div >> 4;
|
||||
UART[uart_num]->clk_div.div_frag = clk_div & 0xf;
|
||||
}
|
||||
UART_EXIT_CRITICAL(&uart_spinlock[uart_num]);
|
||||
return ESP_OK;
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t uart_get_baudrate(uart_port_t uart_num, uint32_t* baudrate)
|
||||
@ -468,17 +481,18 @@ esp_err_t uart_param_config(uart_port_t uart_num, const uart_config_t *uart_conf
|
||||
} else if(uart_num == UART_NUM_2) {
|
||||
periph_module_enable(PERIPH_UART2_MODULE);
|
||||
}
|
||||
r=uart_set_hw_flow_ctrl(uart_num, uart_config->flow_ctrl, uart_config->rx_flow_ctrl_thresh);
|
||||
if (r!=ESP_OK) return r;
|
||||
r=uart_set_baudrate(uart_num, uart_config->baud_rate);
|
||||
if (r!=ESP_OK) return r;
|
||||
r = uart_set_hw_flow_ctrl(uart_num, uart_config->flow_ctrl, uart_config->rx_flow_ctrl_thresh);
|
||||
if (r != ESP_OK) return r;
|
||||
|
||||
UART[uart_num]->conf0.val = (
|
||||
(uart_config->parity << UART_PARITY_S)
|
||||
| (uart_config->data_bits << UART_BIT_NUM_S)
|
||||
| ((uart_config->flow_ctrl & UART_HW_FLOWCTRL_CTS) ? UART_TX_FLOW_EN : 0x0)
|
||||
| UART_TICK_REF_ALWAYS_ON_M);
|
||||
r=uart_set_stop_bits(uart_num, uart_config->stop_bits);
|
||||
UART[uart_num]->conf0.val =
|
||||
(uart_config->parity << UART_PARITY_S)
|
||||
| (uart_config->data_bits << UART_BIT_NUM_S)
|
||||
| ((uart_config->flow_ctrl & UART_HW_FLOWCTRL_CTS) ? UART_TX_FLOW_EN : 0x0)
|
||||
| (uart_config->use_ref_tick ? 0 : UART_TICK_REF_ALWAYS_ON_M);
|
||||
|
||||
r = uart_set_baudrate(uart_num, uart_config->baud_rate);
|
||||
if (r != ESP_OK) return r;
|
||||
r = uart_set_stop_bits(uart_num, uart_config->stop_bits);
|
||||
return r;
|
||||
}
|
||||
|
||||
|
@ -945,3 +945,68 @@ config ESP32_PHY_MAX_TX_POWER
|
||||
default ESP32_PHY_MAX_WIFI_TX_POWER
|
||||
|
||||
endmenu # PHY
|
||||
|
||||
|
||||
menu "Power Management"
|
||||
|
||||
config PM_ENABLE
|
||||
bool "Support for power management"
|
||||
default n
|
||||
help
|
||||
If enabled, application is compiled with support for power management.
|
||||
This option has run-time overhead (increased interrupt latency,
|
||||
longer time to enter idle state), and it also reduces accuracy of
|
||||
RTOS ticks and timers used for timekeeping.
|
||||
Enable this option if application uses power management APIs.
|
||||
|
||||
config PM_DFS_INIT_AUTO
|
||||
bool "Enable dynamic frequency scaling (DFS) at startup"
|
||||
depends on PM_ENABLE
|
||||
default n
|
||||
help
|
||||
If enabled, startup code configures dynamic frequency scaling.
|
||||
Max CPU frequency is set to CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ setting,
|
||||
min frequency is set to XTAL frequency.
|
||||
If disabled, DFS will not be active until the application
|
||||
configures it using esp_pm_configure function.
|
||||
|
||||
config PM_USE_RTC_TIMER_REF
|
||||
bool "Use RTC timer to prevent time drift (EXPERIMENTAL)"
|
||||
depends on PM_ENABLE && (ESP32_TIME_SYSCALL_USE_RTC || ESP32_TIME_SYSCALL_USE_RTC_FRC1)
|
||||
default n
|
||||
help
|
||||
When APB clock frequency changes, high-resolution timer (esp_timer)
|
||||
scale and base value need to be adjusted. Each adjustment may cause
|
||||
small error, and over time such small errors may cause time drift.
|
||||
If this option is enabled, RTC timer will be used as a reference to
|
||||
compensate for the drift.
|
||||
It is recommended that this option is only used if 32k XTAL is selected
|
||||
as RTC clock source.
|
||||
|
||||
config PM_PROFILING
|
||||
bool "Enable profiling counters for PM locks"
|
||||
depends on PM_ENABLE
|
||||
default n
|
||||
help
|
||||
If enabled, esp_pm_* functions will keep track of the amount of time
|
||||
each of the power management locks has been held, and esp_pm_dump_locks
|
||||
function will print this information.
|
||||
This feature can be used to analyze which locks are preventing the chip
|
||||
from going into a lower power state, and see what time the chip spends
|
||||
in each power saving mode. This feature does incur some run-time
|
||||
overhead, so should typically be disabled in production builds.
|
||||
|
||||
config PM_TRACE
|
||||
bool "Enable debug tracing of PM using GPIOs"
|
||||
depends on PM_ENABLE
|
||||
default n
|
||||
help
|
||||
If enabled, some GPIOs will be used to signal events such as RTOS ticks,
|
||||
frequency switching, entry/exit from idle state. Refer to pm_trace.c
|
||||
file for the list of GPIOs.
|
||||
This feature is intended to be used when analyzing/debugging behavior
|
||||
of power management implementation, and should be kept disabled in
|
||||
applications.
|
||||
|
||||
|
||||
endmenu # "Power Management"
|
@ -15,10 +15,12 @@
|
||||
#include <stdint.h>
|
||||
#include <sys/cdefs.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/param.h>
|
||||
#include "sdkconfig.h"
|
||||
#include "esp_attr.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_clk.h"
|
||||
#include "esp_clk_internal.h"
|
||||
#include "rom/ets_sys.h"
|
||||
#include "rom/uart.h"
|
||||
#include "rom/rtc.h"
|
||||
@ -40,14 +42,13 @@
|
||||
|
||||
static void select_rtc_slow_clk(rtc_slow_freq_t slow_clk);
|
||||
|
||||
// g_ticks_us defined in ROMs for PRO and APP CPU
|
||||
extern uint32_t g_ticks_per_us_pro;
|
||||
extern uint32_t g_ticks_per_us_app;
|
||||
|
||||
static const char* TAG = "clk";
|
||||
/*
|
||||
* This function is not exposed as an API at this point,
|
||||
* because FreeRTOS doesn't yet support dynamic changing of
|
||||
* CPU frequency. Also we need to implement hooks for
|
||||
* components which want to be notified of CPU frequency
|
||||
* changes.
|
||||
*/
|
||||
|
||||
|
||||
void esp_clk_init(void)
|
||||
{
|
||||
rtc_config_t cfg = RTC_CONFIG_DEFAULT();
|
||||
@ -90,10 +91,19 @@ void esp_clk_init(void)
|
||||
XTHAL_SET_CCOUNT( XTHAL_GET_CCOUNT() * freq_after / freq_before );
|
||||
}
|
||||
|
||||
int IRAM_ATTR esp_clk_cpu_freq(void)
|
||||
{
|
||||
return g_ticks_per_us_pro * 1000000;
|
||||
}
|
||||
|
||||
int IRAM_ATTR esp_clk_apb_freq(void)
|
||||
{
|
||||
return MIN(g_ticks_per_us_pro, 80) * 1000000;
|
||||
}
|
||||
|
||||
void IRAM_ATTR ets_update_cpu_frequency(uint32_t ticks_per_us)
|
||||
{
|
||||
extern uint32_t g_ticks_per_us_pro; // g_ticks_us defined in ROM for PRO CPU
|
||||
extern uint32_t g_ticks_per_us_app; // same defined for APP CPU
|
||||
/* Update scale factors used by ets_delay_us */
|
||||
g_ticks_per_us_pro = ticks_per_us;
|
||||
g_ticks_per_us_app = ticks_per_us;
|
||||
}
|
||||
|
@ -23,6 +23,7 @@
|
||||
|
||||
#include "esp_panic.h"
|
||||
#include "esp_partition.h"
|
||||
#include "esp_clk.h"
|
||||
|
||||
#if CONFIG_ESP32_ENABLE_COREDUMP
|
||||
#define LOG_LOCAL_LEVEL CONFIG_ESP32_CORE_DUMP_LOG_LEVEL
|
||||
@ -522,10 +523,11 @@ void esp_core_dump_to_uart(XtExcFrame *frame)
|
||||
PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0TXD_U, FUNC_U0TXD_U0TXD);
|
||||
|
||||
ESP_COREDUMP_LOGI("Press Enter to print core dump to UART...");
|
||||
tm_end = xthal_get_ccount() / (XT_CLOCK_FREQ / 1000) + CONFIG_ESP32_CORE_DUMP_UART_DELAY;
|
||||
const int cpu_ticks_per_ms = esp_clk_cpu_freq() / 1000;
|
||||
tm_end = xthal_get_ccount() / cpu_ticks_per_ms + CONFIG_ESP32_CORE_DUMP_UART_DELAY;
|
||||
ch = esp_core_dump_uart_get_char();
|
||||
while (!(ch == '\n' || ch == '\r')) {
|
||||
tm_cur = xthal_get_ccount() / (XT_CLOCK_FREQ / 1000);
|
||||
tm_cur = xthal_get_ccount() / cpu_ticks_per_ms;
|
||||
if (tm_cur >= tm_end)
|
||||
break;
|
||||
ch = esp_core_dump_uart_get_char();
|
||||
|
@ -64,8 +64,10 @@
|
||||
#include "esp_app_trace.h"
|
||||
#include "esp_efuse.h"
|
||||
#include "esp_spiram.h"
|
||||
#include "esp_clk.h"
|
||||
#include "esp_clk_internal.h"
|
||||
#include "esp_timer.h"
|
||||
#include "esp_pm.h"
|
||||
#include "pm_impl.h"
|
||||
#include "trax.h"
|
||||
|
||||
#define STRINGIFY(s) STRINGIFY2(s)
|
||||
@ -287,9 +289,18 @@ void start_cpu0_default(void)
|
||||
esp_clk_init();
|
||||
esp_perip_clk_init();
|
||||
intr_matrix_clear();
|
||||
|
||||
#ifndef CONFIG_CONSOLE_UART_NONE
|
||||
uart_div_modify(CONFIG_CONSOLE_UART_NUM, (rtc_clk_apb_freq_get() << 4) / CONFIG_CONSOLE_UART_BAUDRATE);
|
||||
#endif
|
||||
#ifdef CONFIG_PM_ENABLE
|
||||
const int uart_clk_freq = REF_CLK_FREQ;
|
||||
/* When DFS is enabled, use REFTICK as UART clock source */
|
||||
CLEAR_PERI_REG_MASK(UART_CONF0_REG(CONFIG_CONSOLE_UART_NUM), UART_TICK_REF_ALWAYS_ON);
|
||||
#else
|
||||
const int uart_clk_freq = APB_CLK_FREQ;
|
||||
#endif // CONFIG_PM_DFS_ENABLE
|
||||
uart_div_modify(CONFIG_CONSOLE_UART_NUM, (uart_clk_freq << 4) / CONFIG_CONSOLE_UART_BAUDRATE);
|
||||
#endif // CONFIG_CONSOLE_UART_NONE
|
||||
|
||||
#if CONFIG_BROWNOUT_DET
|
||||
esp_brownout_init();
|
||||
#endif
|
||||
@ -337,6 +348,18 @@ void start_cpu0_default(void)
|
||||
spi_flash_init();
|
||||
/* init default OS-aware flash access critical section */
|
||||
spi_flash_guard_set(&g_flash_guard_default_ops);
|
||||
#ifdef CONFIG_PM_ENABLE
|
||||
esp_pm_impl_init();
|
||||
#ifdef CONFIG_PM_DFS_INIT_AUTO
|
||||
rtc_cpu_freq_t max_freq;
|
||||
rtc_clk_cpu_freq_from_mhz(CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ, &max_freq);
|
||||
esp_pm_config_esp32_t cfg = {
|
||||
.max_cpu_freq = max_freq,
|
||||
.min_cpu_freq = RTC_CPU_FREQ_XTAL
|
||||
};
|
||||
esp_pm_configure(&cfg);
|
||||
#endif //CONFIG_PM_DFS_INIT_AUTO
|
||||
#endif //CONFIG_PM_ENABLE
|
||||
|
||||
#if CONFIG_ESP32_ENABLE_COREDUMP
|
||||
esp_core_dump_init();
|
||||
|
@ -34,20 +34,25 @@
|
||||
#include "freertos/portmacro.h"
|
||||
|
||||
|
||||
#define REASON_YIELD (1<<0)
|
||||
#define REASON_YIELD BIT(0)
|
||||
#define REASON_FREQ_SWITCH BIT(1)
|
||||
|
||||
static portMUX_TYPE reasonSpinlock = portMUX_INITIALIZER_UNLOCKED;
|
||||
static portMUX_TYPE reason_spinlock = portMUX_INITIALIZER_UNLOCKED;
|
||||
static volatile uint32_t reason[ portNUM_PROCESSORS ];
|
||||
|
||||
|
||||
/*
|
||||
ToDo: There is a small chance the CPU already has yielded when this ISR is serviced. In that case, it's running the intended task but
|
||||
the ISR will cause it to switch _away_ from it. portYIELD_FROM_ISR will probably just schedule the task again, but have to check that.
|
||||
*/
|
||||
static void esp_crosscore_isr_handle_yield()
|
||||
{
|
||||
portYIELD_FROM_ISR();
|
||||
}
|
||||
|
||||
static void IRAM_ATTR esp_crosscore_isr(void *arg) {
|
||||
uint32_t myReasonVal;
|
||||
uint32_t my_reason_val;
|
||||
//A pointer to the correct reason array item is passed to this ISR.
|
||||
volatile uint32_t *myReason=arg;
|
||||
volatile uint32_t *my_reason=arg;
|
||||
|
||||
//Clear the interrupt first.
|
||||
if (xPortGetCoreID()==0) {
|
||||
@ -56,43 +61,59 @@ static void IRAM_ATTR esp_crosscore_isr(void *arg) {
|
||||
DPORT_WRITE_PERI_REG(DPORT_CPU_INTR_FROM_CPU_1_REG, 0);
|
||||
}
|
||||
//Grab the reason and clear it.
|
||||
portENTER_CRITICAL(&reasonSpinlock);
|
||||
myReasonVal=*myReason;
|
||||
*myReason=0;
|
||||
portEXIT_CRITICAL(&reasonSpinlock);
|
||||
portENTER_CRITICAL(&reason_spinlock);
|
||||
my_reason_val=*my_reason;
|
||||
*my_reason=0;
|
||||
portEXIT_CRITICAL(&reason_spinlock);
|
||||
|
||||
//Check what we need to do.
|
||||
if (myReasonVal&REASON_YIELD) {
|
||||
portYIELD_FROM_ISR();
|
||||
if (my_reason_val & REASON_YIELD) {
|
||||
esp_crosscore_isr_handle_yield();
|
||||
}
|
||||
if (my_reason_val & REASON_FREQ_SWITCH) {
|
||||
/* Nothing to do here; the frequency switch event was already
|
||||
* handled by a hook in xtensa_vectors.S. Could be used in the future
|
||||
* to allow DFS features without the extra latency of the ISR hook.
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
//Initialize the crosscore interrupt on this core. Call this once
|
||||
//on each active core.
|
||||
void esp_crosscore_int_init() {
|
||||
portENTER_CRITICAL(&reasonSpinlock);
|
||||
portENTER_CRITICAL(&reason_spinlock);
|
||||
reason[xPortGetCoreID()]=0;
|
||||
portEXIT_CRITICAL(&reasonSpinlock);
|
||||
portEXIT_CRITICAL(&reason_spinlock);
|
||||
esp_err_t err;
|
||||
if (xPortGetCoreID()==0) {
|
||||
err = esp_intr_alloc(ETS_FROM_CPU_INTR0_SOURCE, ESP_INTR_FLAG_IRAM, esp_crosscore_isr, (void*)&reason[xPortGetCoreID()], NULL);
|
||||
err = esp_intr_alloc(ETS_FROM_CPU_INTR0_SOURCE, ESP_INTR_FLAG_IRAM, esp_crosscore_isr, (void*)&reason[0], NULL);
|
||||
} else {
|
||||
err = esp_intr_alloc(ETS_FROM_CPU_INTR1_SOURCE, ESP_INTR_FLAG_IRAM, esp_crosscore_isr, (void*)&reason[xPortGetCoreID()], NULL);
|
||||
err = esp_intr_alloc(ETS_FROM_CPU_INTR1_SOURCE, ESP_INTR_FLAG_IRAM, esp_crosscore_isr, (void*)&reason[1], NULL);
|
||||
}
|
||||
assert(err == ESP_OK);
|
||||
}
|
||||
|
||||
void IRAM_ATTR esp_crosscore_int_send_yield(int coreId) {
|
||||
assert(coreId<portNUM_PROCESSORS);
|
||||
static void IRAM_ATTR esp_crosscore_int_send(int core_id, uint32_t reason_mask) {
|
||||
assert(core_id<portNUM_PROCESSORS);
|
||||
//Mark the reason we interrupt the other CPU
|
||||
portENTER_CRITICAL(&reasonSpinlock);
|
||||
reason[coreId]|=REASON_YIELD;
|
||||
portEXIT_CRITICAL(&reasonSpinlock);
|
||||
portENTER_CRITICAL(&reason_spinlock);
|
||||
reason[core_id] |= reason_mask;
|
||||
portEXIT_CRITICAL(&reason_spinlock);
|
||||
//Poke the other CPU.
|
||||
if (coreId==0) {
|
||||
if (core_id==0) {
|
||||
DPORT_WRITE_PERI_REG(DPORT_CPU_INTR_FROM_CPU_0_REG, DPORT_CPU_INTR_FROM_CPU_0);
|
||||
} else {
|
||||
DPORT_WRITE_PERI_REG(DPORT_CPU_INTR_FROM_CPU_1_REG, DPORT_CPU_INTR_FROM_CPU_1);
|
||||
}
|
||||
}
|
||||
|
||||
void IRAM_ATTR esp_crosscore_int_send_yield(int core_id)
|
||||
{
|
||||
esp_crosscore_int_send(core_id, REASON_YIELD);
|
||||
}
|
||||
|
||||
void IRAM_ATTR esp_crosscore_int_send_freq_switch(int core_id)
|
||||
{
|
||||
esp_crosscore_int_send(core_id, REASON_FREQ_SWITCH);
|
||||
}
|
||||
|
||||
|
39
components/esp32/esp_clk_internal.h
Normal file
39
components/esp32/esp_clk_internal.h
Normal file
@ -0,0 +1,39 @@
|
||||
// Copyright 2015-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
|
||||
|
||||
/**
|
||||
* @file esp_clk_internal.h
|
||||
*
|
||||
* Private clock-related functions
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Initialize clock-related settings
|
||||
*
|
||||
* Called from cpu_start.c, not intended to be called from other places.
|
||||
* This function configures the CPU clock, RTC slow and fast clocks, and
|
||||
* performs RTC slow clock calibration.
|
||||
*/
|
||||
void esp_clk_init(void);
|
||||
|
||||
|
||||
/**
|
||||
* @brief Disables clock of some peripherals
|
||||
*
|
||||
* Called from cpu_start.c, not intended to be called from other places.
|
||||
* This function disables clock of useless peripherals when cpu starts.
|
||||
*/
|
||||
void esp_perip_clk_init(void);
|
@ -19,6 +19,7 @@
|
||||
#include "esp_attr.h"
|
||||
#include "esp_intr_alloc.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_clk.h"
|
||||
#include "esp_timer_impl.h"
|
||||
#include "soc/frc_timer_reg.h"
|
||||
#include "soc/rtc.h"
|
||||
@ -112,6 +113,14 @@ static uint32_t s_timer_us_per_overflow;
|
||||
// will not increment s_time_base_us if this flag is set.
|
||||
static bool s_mask_overflow;
|
||||
|
||||
#ifdef CONFIG_PM_DFS_USE_RTC_TIMER_REF
|
||||
// If DFS is enabled, upon the first frequency change this value is set to the
|
||||
// difference between esp_timer value and RTC timer value. On every subsequent
|
||||
// frequency change, s_time_base_us is adjusted to maintain the same difference
|
||||
// between esp_timer and RTC timer. (All mentioned values are in microseconds.)
|
||||
static uint64_t s_rtc_time_diff = 0;
|
||||
#endif
|
||||
|
||||
// Spinlock used to protect access to static variables above and to the hardware
|
||||
// registers.
|
||||
portMUX_TYPE s_time_update_lock = portMUX_INITIALIZER_UNLOCKED;
|
||||
@ -208,6 +217,55 @@ static void IRAM_ATTR timer_alarm_isr(void *arg)
|
||||
(*s_alarm_handler)(arg);
|
||||
}
|
||||
|
||||
void IRAM_ATTR esp_timer_impl_update_apb_freq(uint32_t apb_ticks_per_us)
|
||||
{
|
||||
portENTER_CRITICAL(&s_time_update_lock);
|
||||
/* Bail out if the timer is not initialized yet */
|
||||
if (s_timer_interrupt_handle == NULL) {
|
||||
portEXIT_CRITICAL(&s_time_update_lock);
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t new_ticks_per_us = apb_ticks_per_us / TIMER_DIV;
|
||||
uint32_t alarm = REG_READ(FRC_TIMER_ALARM_REG(1));
|
||||
uint32_t count = REG_READ(FRC_TIMER_COUNT_REG(1));
|
||||
uint64_t ticks_to_alarm = alarm - count;
|
||||
uint64_t new_ticks = (ticks_to_alarm * new_ticks_per_us) / s_timer_ticks_per_us;
|
||||
uint32_t new_alarm_val;
|
||||
if (alarm > count && new_ticks <= FRC_TIMER_LOAD_VALUE(1)) {
|
||||
new_alarm_val = new_ticks;
|
||||
} else {
|
||||
new_alarm_val = ALARM_OVERFLOW_VAL;
|
||||
if (alarm != ALARM_OVERFLOW_VAL) {
|
||||
s_mask_overflow = true;
|
||||
}
|
||||
}
|
||||
REG_WRITE(FRC_TIMER_ALARM_REG(1), new_alarm_val);
|
||||
REG_WRITE(FRC_TIMER_LOAD_REG(1), 0);
|
||||
|
||||
s_time_base_us += count / s_timer_ticks_per_us;
|
||||
|
||||
#ifdef CONFIG_PM_DFS_USE_RTC_TIMER_REF
|
||||
// Due to the extra time required to read RTC time, don't attempt this
|
||||
// adjustment when switching to a higher frequency (which usually
|
||||
// happens in an interrupt).
|
||||
if (new_ticks_per_us < s_timer_ticks_per_us) {
|
||||
uint64_t rtc_time = esp_clk_rtc_time();
|
||||
uint64_t new_rtc_time_diff = s_time_base_us - rtc_time;
|
||||
if (s_rtc_time_diff != 0) {
|
||||
uint64_t correction = new_rtc_time_diff - s_rtc_time_diff;
|
||||
s_time_base_us -= correction;
|
||||
} else {
|
||||
s_rtc_time_diff = new_rtc_time_diff;
|
||||
}
|
||||
}
|
||||
#endif // CONFIG_PM_DFS_USE_RTC_TIMER_REF
|
||||
|
||||
s_timer_ticks_per_us = new_ticks_per_us;
|
||||
s_timer_us_per_overflow = FRC_TIMER_LOAD_VALUE(1) / new_ticks_per_us;
|
||||
|
||||
portEXIT_CRITICAL(&s_time_update_lock);
|
||||
}
|
||||
|
||||
esp_err_t esp_timer_impl_init(intr_handler_t alarm_handler)
|
||||
{
|
||||
|
@ -51,6 +51,15 @@ void esp_timer_impl_deinit();
|
||||
*/
|
||||
void esp_timer_impl_set_alarm(uint64_t timestamp);
|
||||
|
||||
/**
|
||||
* @brief Notify esp_timer implementation that APB frequency has changed
|
||||
*
|
||||
* Called by the frequency switching code.
|
||||
*
|
||||
* @param apb_ticks_per_us new number of APB clock ticks per microsecond
|
||||
*/
|
||||
void esp_timer_impl_update_apb_freq(uint32_t apb_ticks_per_us);
|
||||
|
||||
/**
|
||||
* @brief Get time, in microseconds, since esp_timer_impl_init was called
|
||||
* @return timestamp in microseconds
|
||||
|
@ -20,6 +20,10 @@
|
||||
#include "esp_attr.h"
|
||||
#include "esp_freertos_hooks.h"
|
||||
|
||||
#include "sdkconfig.h"
|
||||
#include "esp_pm.h"
|
||||
#include "pm_impl.h"
|
||||
|
||||
//We use just a static array here because it's not expected many components will need
|
||||
//an idle or tick hook.
|
||||
#define MAX_HOOKS 8
|
||||
@ -41,20 +45,21 @@ void IRAM_ATTR esp_vApplicationTickHook()
|
||||
|
||||
void esp_vApplicationIdleHook()
|
||||
{
|
||||
bool doWait=true;
|
||||
bool r;
|
||||
int n;
|
||||
bool can_go_idle=true;
|
||||
int core = xPortGetCoreID();
|
||||
for (n=0; n<MAX_HOOKS; n++) {
|
||||
if (idle_cb[core][n]!=NULL) {
|
||||
r=idle_cb[core][n]();
|
||||
if (!r) doWait=false;
|
||||
for (int n = 0; n < MAX_HOOKS; n++) {
|
||||
if (idle_cb[core][n] != NULL && !idle_cb[core][n]()) {
|
||||
can_go_idle = false;
|
||||
}
|
||||
}
|
||||
if (doWait) {
|
||||
//Wait for whatever interrupt comes next... this should save some power.
|
||||
asm("waiti 0");
|
||||
if (!can_go_idle) {
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_ENABLE
|
||||
esp_pm_impl_idle_hook();
|
||||
#endif
|
||||
asm("waiti 0");
|
||||
}
|
||||
|
||||
esp_err_t esp_register_freertos_idle_hook_for_cpu(esp_freertos_idle_cb_t new_idle_cb, UBaseType_t cpuid)
|
||||
|
@ -17,6 +17,7 @@
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include "esp_attr.h"
|
||||
#include "esp_clk.h"
|
||||
#include "soc/wdev_reg.h"
|
||||
#include "freertos/FreeRTOSConfig.h"
|
||||
#include "xtensa/core-macros.h"
|
||||
@ -35,13 +36,21 @@ uint32_t IRAM_ATTR esp_random(void)
|
||||
* WDEV_RND_REG reads while waiting.
|
||||
*/
|
||||
|
||||
/* This code does not run in a critical section, so CPU frequency switch may
|
||||
* happens while this code runs (this will not happen in the current
|
||||
* implementation, but possible in the future). However if that happens,
|
||||
* the number of cycles spent on frequency switching will certainly be more
|
||||
* than the number of cycles we need to wait here.
|
||||
*/
|
||||
uint32_t cpu_to_apb_freq_ratio = esp_clk_cpu_freq() / esp_clk_apb_freq();
|
||||
|
||||
static uint32_t last_ccount = 0;
|
||||
uint32_t ccount;
|
||||
uint32_t result = 0;
|
||||
do {
|
||||
ccount = XTHAL_GET_CCOUNT();
|
||||
result ^= REG_READ(WDEV_RND_REG);
|
||||
} while (ccount - last_ccount < XT_CLOCK_FREQ / APB_CLK_FREQ * 16);
|
||||
} while (ccount - last_ccount < cpu_to_apb_freq_ratio * 16);
|
||||
last_ccount = ccount;
|
||||
return result ^ REG_READ(WDEV_RND_REG);
|
||||
}
|
||||
|
42
components/esp32/include/esp32/pm.h
Normal file
42
components/esp32/include/esp32/pm.h
Normal file
@ -0,0 +1,42 @@
|
||||
// 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/rtc.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
/**
|
||||
* @brief Power management config for ESP32
|
||||
*
|
||||
* Pass a pointer to this structure as an argument to esp_pm_configure function.
|
||||
*/
|
||||
typedef struct {
|
||||
rtc_cpu_freq_t max_cpu_freq; /*!< Maximum CPU frequency to use */
|
||||
rtc_cpu_freq_t min_cpu_freq; /*!< Minimum CPU frequency to use when no frequency locks are taken */
|
||||
bool light_sleep_enable; /*!< Enter light sleep when no locks are taken */
|
||||
} esp_pm_config_esp32_t;
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
@ -18,20 +18,8 @@
|
||||
* @file esp_clk.h
|
||||
*
|
||||
* This file contains declarations of clock related functions.
|
||||
* These functions are used in ESP-IDF components, but should not be considered
|
||||
* to be part of public API.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Initialize clock-related settings
|
||||
*
|
||||
* Called from cpu_start.c, not intended to be called from other places.
|
||||
* This function configures the CPU clock, RTC slow and fast clocks, and
|
||||
* performs RTC slow clock calibration.
|
||||
*/
|
||||
void esp_clk_init(void);
|
||||
|
||||
|
||||
/**
|
||||
* @brief Get the calibration value of RTC slow clock
|
||||
*
|
||||
@ -42,7 +30,6 @@ void esp_clk_init(void);
|
||||
*/
|
||||
uint32_t esp_clk_slowclk_cal_get();
|
||||
|
||||
|
||||
/**
|
||||
* @brief Update the calibration value of RTC slow clock
|
||||
*
|
||||
@ -55,10 +42,34 @@ uint32_t esp_clk_slowclk_cal_get();
|
||||
void esp_clk_slowclk_cal_set(uint32_t value);
|
||||
|
||||
/**
|
||||
* @brief Disables clock of some peripherals
|
||||
* @brief Return current CPU clock frequency
|
||||
* When frequency switching is performed, this frequency may change.
|
||||
* However it is guaranteed that the frequency never changes with a critical
|
||||
* section.
|
||||
*
|
||||
* Called from cpu_start.c, not intended to be called from other places.
|
||||
* This function disables clock of useless peripherals when cpu starts.
|
||||
* @return CPU clock frequency, in Hz
|
||||
*/
|
||||
void esp_perip_clk_init(void);
|
||||
int esp_clk_cpu_freq(void);
|
||||
|
||||
/**
|
||||
* @brief Return current APB clock frequency
|
||||
*
|
||||
* When frequency switching is performed, this frequency may change.
|
||||
* However it is guaranteed that the frequency never changes with a critical
|
||||
* section.
|
||||
*
|
||||
* @return APB clock frequency, in Hz
|
||||
*/
|
||||
int esp_clk_apb_freq(void);
|
||||
|
||||
|
||||
/**
|
||||
* @brief Read value of RTC counter, converting it to microseconds
|
||||
* @attention The value returned by this function may change abruptly when
|
||||
* calibration value of RTC counter is updated via esp_clk_slowclk_cal_set
|
||||
* function. This should not happen unless application calls esp_clk_slowclk_cal_set.
|
||||
* In ESP-IDF, esp_clk_slowclk_cal_set is only called in startup code.
|
||||
*
|
||||
* @return Value or RTC counter, expressed in microseconds
|
||||
*/
|
||||
uint64_t esp_clk_rtc_time();
|
||||
|
@ -35,8 +35,20 @@ void esp_crosscore_int_init();
|
||||
* This is used internally by FreeRTOS in multicore mode
|
||||
* and should not be called by the user.
|
||||
*
|
||||
* @param coreID Core that should do the yielding
|
||||
* @param core_id Core that should do the yielding
|
||||
*/
|
||||
void esp_crosscore_int_send_yield(int coreId);
|
||||
void esp_crosscore_int_send_yield(int core_id);
|
||||
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Send an interrupt to a CPU indicating it should update its
|
||||
* CCOMPARE1 value due to a frequency switch.
|
||||
*
|
||||
* This is used internally when dynamic frequency switching is
|
||||
* enabled, and should not be called from application code.
|
||||
*
|
||||
* @param core_id Core that should update its CCOMPARE1 value
|
||||
*/
|
||||
void esp_crosscore_int_send_freq_switch(int core_id);
|
||||
|
||||
#endif
|
||||
|
179
components/esp32/include/esp_pm.h
Normal file
179
components/esp32/include/esp_pm.h
Normal file
@ -0,0 +1,179 @@
|
||||
// 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
|
@ -18,9 +18,6 @@
|
||||
* @file esp_timer.h
|
||||
* @brief microsecond-precision 64-bit timer API, replacement for ets_timer
|
||||
*
|
||||
* Not a public header yet. To be moved into include/ directory when it is made
|
||||
* public.
|
||||
*
|
||||
* esp_timer APIs allow components to receive callbacks when a hardware timer
|
||||
* reaches certain value. The timer provides microsecond accuracy and
|
||||
* up to 64 bit range. Note that while the timer itself provides microsecond
|
456
components/esp32/pm_esp32.c
Normal file
456
components/esp32/pm_esp32.c
Normal file
@ -0,0 +1,456 @@
|
||||
// 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 <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <sys/param.h>
|
||||
|
||||
#include "esp_attr.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_pm.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_crosscore_int.h"
|
||||
|
||||
#include "soc/rtc.h"
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/xtensa_timer.h"
|
||||
#include "xtensa/core-macros.h"
|
||||
|
||||
#include "pm_impl.h"
|
||||
#include "pm_trace.h"
|
||||
#include "esp_timer_impl.h"
|
||||
#include "esp32/pm.h"
|
||||
|
||||
/* CCOMPARE update timeout, in CPU cycles. Any value above ~600 cycles will work
|
||||
* for the purpose of detecting a deadlock.
|
||||
*/
|
||||
#define CCOMPARE_UPDATE_TIMEOUT 1000000
|
||||
|
||||
#ifdef CONFIG_PM_PROFILING
|
||||
#define WITH_PROFILING
|
||||
#endif
|
||||
|
||||
|
||||
static portMUX_TYPE s_switch_lock = portMUX_INITIALIZER_UNLOCKED;
|
||||
/* The following state variables are protected using s_switch_lock: */
|
||||
/* Current sleep mode; When switching, contains old mode until switch is complete */
|
||||
static pm_mode_t s_mode = PM_MODE_CPU_MAX;
|
||||
/* True when switch is in progress */
|
||||
static volatile bool s_is_switching;
|
||||
/* When switch is in progress, this is the mode we are switching into */
|
||||
static pm_mode_t s_new_mode = PM_MODE_CPU_MAX;
|
||||
/* Number of times each mode was locked */
|
||||
static size_t s_mode_lock_counts[PM_MODE_COUNT];
|
||||
/* Bit mask of locked modes. BIT(i) is set iff s_mode_lock_counts[i] > 0. */
|
||||
static uint32_t s_mode_mask;
|
||||
|
||||
/* Divider and multiplier used to adjust (ccompare - ccount) duration.
|
||||
* Only set to non-zero values when switch is in progress.
|
||||
*/
|
||||
static uint32_t s_ccount_div;
|
||||
static uint32_t s_ccount_mul;
|
||||
|
||||
/* Indicates to the ISR hook that CCOMPARE needs to be updated on the given CPU.
|
||||
* Used in conjunction with cross-core interrupt to update CCOMPARE on the other CPU.
|
||||
*/
|
||||
static volatile bool s_need_update_ccompare[portNUM_PROCESSORS];
|
||||
|
||||
/* When no RTOS tasks are active, these locks are released to allow going into
|
||||
* a lower power mode. Used by ISR hook and idle hook.
|
||||
*/
|
||||
static esp_pm_lock_handle_t s_rtos_lock_handle[portNUM_PROCESSORS];
|
||||
|
||||
/* A flag indicating that Idle hook has run on a given CPU;
|
||||
* Next interrupt on the same CPU will take s_rtos_lock_handle.
|
||||
*/
|
||||
static bool s_core_idle[portNUM_PROCESSORS];
|
||||
|
||||
/* g_ticks_us defined in ROM for PRO CPU */
|
||||
extern uint32_t g_ticks_per_us_pro;
|
||||
|
||||
/* Lookup table of CPU frequencies to be used in each mode.
|
||||
* Modified by esp_pm_configure.
|
||||
*/
|
||||
rtc_cpu_freq_t s_cpu_freq_by_mode[PM_MODE_COUNT] = {
|
||||
[PM_MODE_LIGHT_SLEEP] = (rtc_cpu_freq_t) -1, /* unused */
|
||||
[PM_MODE_APB_MIN] = RTC_CPU_FREQ_XTAL,
|
||||
[PM_MODE_APB_MAX] = RTC_CPU_FREQ_80M,
|
||||
[PM_MODE_CPU_MAX] = RTC_CPU_FREQ_80M,
|
||||
};
|
||||
|
||||
/* Lookup table of CPU ticks per microsecond for each RTC_CPU_FREQ_ value.
|
||||
* Essentially the same as returned by rtc_clk_cpu_freq_value(), but without
|
||||
* the function call. Not const because XTAL frequency is only known at run time.
|
||||
*/
|
||||
static uint32_t s_cpu_freq_to_ticks[] = {
|
||||
[RTC_CPU_FREQ_XTAL] = 0, /* This is set by esp_pm_impl_init */
|
||||
[RTC_CPU_FREQ_80M] = 80,
|
||||
[RTC_CPU_FREQ_160M] = 160,
|
||||
[RTC_CPU_FREQ_240M] = 240,
|
||||
[RTC_CPU_FREQ_2M] = 2
|
||||
};
|
||||
|
||||
/* Lookup table of names for each RTC_CPU_FREQ_ value. Used for logging only. */
|
||||
static const char* s_freq_names[] __attribute__((unused)) = {
|
||||
[RTC_CPU_FREQ_XTAL] = "XTAL",
|
||||
[RTC_CPU_FREQ_80M] = "80",
|
||||
[RTC_CPU_FREQ_160M] = "160",
|
||||
[RTC_CPU_FREQ_240M] = "240",
|
||||
[RTC_CPU_FREQ_2M] = "2"
|
||||
};
|
||||
|
||||
/* Whether automatic light sleep is enabled. Currently always false */
|
||||
static bool s_light_sleep_en = false;
|
||||
|
||||
#ifdef WITH_PROFILING
|
||||
/* Time, in microseconds, spent so far in each mode */
|
||||
static pm_time_t s_time_in_mode[PM_MODE_COUNT];
|
||||
/* Timestamp, in microseconds, when the mode switch last happened */
|
||||
static pm_time_t s_last_mode_change_time;
|
||||
/* User-readable mode names, used by esp_pm_impl_dump_stats */
|
||||
static const char* s_mode_names[] = {
|
||||
"SLEEP",
|
||||
"APB_MIN",
|
||||
"APB_MAX",
|
||||
"CPU_MAX"
|
||||
};
|
||||
#endif // WITH_PROFILING
|
||||
|
||||
|
||||
static const char* TAG = "pm_esp32";
|
||||
|
||||
static void update_ccompare();
|
||||
static void do_switch(pm_mode_t new_mode);
|
||||
static void leave_idle();
|
||||
static void on_freq_update(uint32_t old_ticks_per_us, uint32_t ticks_per_us);
|
||||
|
||||
|
||||
pm_mode_t esp_pm_impl_get_mode(esp_pm_lock_type_t type, int arg)
|
||||
{
|
||||
(void) arg;
|
||||
if (type == ESP_PM_CPU_FREQ_MAX) {
|
||||
return PM_MODE_CPU_MAX;
|
||||
} else if (type == ESP_PM_APB_FREQ_MAX) {
|
||||
return PM_MODE_APB_MAX;
|
||||
} else if (type == ESP_PM_NO_LIGHT_SLEEP) {
|
||||
return PM_MODE_APB_MIN;
|
||||
} else {
|
||||
// unsupported mode
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
esp_err_t esp_pm_configure(const void* vconfig)
|
||||
{
|
||||
#ifndef CONFIG_PM_ENABLE
|
||||
return ESP_ERR_NOT_SUPPORTED;
|
||||
#endif
|
||||
|
||||
const esp_pm_config_esp32_t* config = (const esp_pm_config_esp32_t*) vconfig;
|
||||
if (config->light_sleep_enable) {
|
||||
return ESP_ERR_NOT_SUPPORTED;
|
||||
}
|
||||
rtc_cpu_freq_t min_freq = config->min_cpu_freq;
|
||||
rtc_cpu_freq_t max_freq = config->max_cpu_freq;
|
||||
|
||||
rtc_cpu_freq_t apb_max_freq; /* CPU frequency in APB_MAX mode */
|
||||
if (max_freq == RTC_CPU_FREQ_240M) {
|
||||
/* We can't switch between 240 and 80/160 without disabling PLL,
|
||||
* so use 240MHz CPU frequency when 80MHz APB frequency is requested.
|
||||
*/
|
||||
apb_max_freq = RTC_CPU_FREQ_240M;
|
||||
} else {
|
||||
/* Otherwise (max CPU frequency is 80MHz or 160MHz), can use 80MHz
|
||||
* CPU frequency when 80MHz APB frequency is requested.
|
||||
*/
|
||||
apb_max_freq = RTC_CPU_FREQ_80M;
|
||||
}
|
||||
|
||||
apb_max_freq = MAX(apb_max_freq, min_freq);
|
||||
|
||||
ESP_LOGI(TAG, "Frequency switching config: "
|
||||
"CPU_MAX: %s, APB_MAX: %s, APB_MIN: %s, Light sleep: %s",
|
||||
s_freq_names[max_freq],
|
||||
s_freq_names[apb_max_freq],
|
||||
s_freq_names[min_freq],
|
||||
config->light_sleep_enable ? "ENABLED" : "DISABLED");
|
||||
|
||||
portENTER_CRITICAL(&s_switch_lock);
|
||||
s_cpu_freq_by_mode[PM_MODE_CPU_MAX] = max_freq;
|
||||
s_cpu_freq_by_mode[PM_MODE_APB_MAX] = apb_max_freq;
|
||||
s_cpu_freq_by_mode[PM_MODE_APB_MIN] = min_freq;
|
||||
s_light_sleep_en = config->light_sleep_enable;
|
||||
portEXIT_CRITICAL(&s_switch_lock);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static pm_mode_t IRAM_ATTR get_lowest_allowed_mode()
|
||||
{
|
||||
/* TODO: optimize using ffs/clz */
|
||||
if (s_mode_mask >= BIT(PM_MODE_CPU_MAX)) {
|
||||
return PM_MODE_CPU_MAX;
|
||||
} else if (s_mode_mask >= BIT(PM_MODE_APB_MAX)) {
|
||||
return PM_MODE_APB_MAX;
|
||||
} else if (s_mode_mask >= BIT(PM_MODE_APB_MIN) || !s_light_sleep_en) {
|
||||
return PM_MODE_APB_MIN;
|
||||
} else {
|
||||
return PM_MODE_LIGHT_SLEEP;
|
||||
}
|
||||
}
|
||||
|
||||
void IRAM_ATTR esp_pm_impl_switch_mode(pm_mode_t mode,
|
||||
pm_mode_switch_t lock_or_unlock, pm_time_t now)
|
||||
{
|
||||
bool need_switch = false;
|
||||
uint32_t mode_mask = BIT(mode);
|
||||
portENTER_CRITICAL(&s_switch_lock);
|
||||
uint32_t count;
|
||||
if (lock_or_unlock == MODE_LOCK) {
|
||||
count = ++s_mode_lock_counts[mode];
|
||||
} else {
|
||||
count = s_mode_lock_counts[mode]--;
|
||||
}
|
||||
if (count == 1) {
|
||||
if (lock_or_unlock == MODE_LOCK) {
|
||||
s_mode_mask |= mode_mask;
|
||||
} else {
|
||||
s_mode_mask &= ~mode_mask;
|
||||
}
|
||||
need_switch = true;
|
||||
}
|
||||
|
||||
pm_mode_t new_mode = s_mode;
|
||||
if (need_switch) {
|
||||
new_mode = get_lowest_allowed_mode();
|
||||
#ifdef WITH_PROFILING
|
||||
if (s_last_mode_change_time != 0) {
|
||||
pm_time_t diff = now - s_last_mode_change_time;
|
||||
s_time_in_mode[s_mode] += diff;
|
||||
}
|
||||
s_last_mode_change_time = now;
|
||||
#endif // WITH_PROFILING
|
||||
}
|
||||
portEXIT_CRITICAL(&s_switch_lock);
|
||||
if (need_switch && new_mode != s_mode) {
|
||||
do_switch(new_mode);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Update clock dividers in esp_timer and FreeRTOS, and adjust CCOMPARE
|
||||
* values on both CPUs.
|
||||
* @param old_ticks_per_us old CPU frequency
|
||||
* @param ticks_per_us new CPU frequency
|
||||
*/
|
||||
static void IRAM_ATTR on_freq_update(uint32_t old_ticks_per_us, uint32_t ticks_per_us)
|
||||
{
|
||||
uint32_t old_apb_ticks_per_us = MIN(old_ticks_per_us, 80);
|
||||
uint32_t apb_ticks_per_us = MIN(ticks_per_us, 80);
|
||||
/* Update APB frequency value used by the timer */
|
||||
if (old_apb_ticks_per_us != apb_ticks_per_us) {
|
||||
esp_timer_impl_update_apb_freq(apb_ticks_per_us);
|
||||
}
|
||||
|
||||
/* Calculate new tick divisor */
|
||||
_xt_tick_divisor = ticks_per_us * 1000000 / XT_TICK_PER_SEC;
|
||||
|
||||
int core_id = xPortGetCoreID();
|
||||
if (s_rtos_lock_handle[core_id] != NULL) {
|
||||
ESP_PM_TRACE_ENTER(CCOMPARE_UPDATE, core_id);
|
||||
/* ccount_div and ccount_mul are used in esp_pm_impl_update_ccompare
|
||||
* to calculate new CCOMPARE value.
|
||||
*/
|
||||
s_ccount_div = old_ticks_per_us;
|
||||
s_ccount_mul = ticks_per_us;
|
||||
|
||||
/* Update CCOMPARE value on this CPU */
|
||||
update_ccompare();
|
||||
|
||||
#if portNUM_PROCESSORS == 2
|
||||
/* Send interrupt to the other CPU to update CCOMPARE value */
|
||||
int other_core_id = (core_id == 0) ? 1 : 0;
|
||||
|
||||
s_need_update_ccompare[other_core_id] = true;
|
||||
esp_crosscore_int_send_freq_switch(other_core_id);
|
||||
|
||||
int timeout = 0;
|
||||
while (s_need_update_ccompare[other_core_id]) {
|
||||
if (++timeout == CCOMPARE_UPDATE_TIMEOUT) {
|
||||
assert(false && "failed to update CCOMPARE, possible deadlock");
|
||||
}
|
||||
}
|
||||
#endif // portNUM_PROCESSORS == 2
|
||||
|
||||
s_ccount_mul = 0;
|
||||
s_ccount_div = 0;
|
||||
ESP_PM_TRACE_EXIT(CCOMPARE_UPDATE, core_id);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform the switch to new power mode.
|
||||
* Currently only changes the CPU frequency and adjusts clock dividers.
|
||||
* No light sleep yet.
|
||||
* @param new_mode mode to switch to
|
||||
*/
|
||||
static void IRAM_ATTR do_switch(pm_mode_t new_mode)
|
||||
{
|
||||
const int core_id = xPortGetCoreID();
|
||||
|
||||
do {
|
||||
portENTER_CRITICAL_ISR(&s_switch_lock);
|
||||
if (!s_is_switching) {
|
||||
break;
|
||||
}
|
||||
if (s_new_mode <= new_mode) {
|
||||
portEXIT_CRITICAL_ISR(&s_switch_lock);
|
||||
return;
|
||||
}
|
||||
if (s_need_update_ccompare[core_id]) {
|
||||
s_need_update_ccompare[core_id] = false;
|
||||
}
|
||||
portEXIT_CRITICAL_ISR(&s_switch_lock);
|
||||
} while (true);
|
||||
s_new_mode = new_mode;
|
||||
s_is_switching = true;
|
||||
portEXIT_CRITICAL_ISR(&s_switch_lock);
|
||||
|
||||
rtc_cpu_freq_t old_freq = s_cpu_freq_by_mode[s_mode];
|
||||
rtc_cpu_freq_t new_freq = s_cpu_freq_by_mode[new_mode];
|
||||
|
||||
if (new_freq != old_freq) {
|
||||
uint32_t old_ticks_per_us = g_ticks_per_us_pro;
|
||||
uint32_t new_ticks_per_us = s_cpu_freq_to_ticks[new_freq];
|
||||
|
||||
bool switch_down = new_ticks_per_us < old_ticks_per_us;
|
||||
|
||||
ESP_PM_TRACE_ENTER(FREQ_SWITCH, core_id);
|
||||
if (switch_down) {
|
||||
on_freq_update(old_ticks_per_us, new_ticks_per_us);
|
||||
}
|
||||
rtc_clk_cpu_freq_set_fast(new_freq);
|
||||
if (!switch_down) {
|
||||
on_freq_update(old_ticks_per_us, new_ticks_per_us);
|
||||
}
|
||||
ESP_PM_TRACE_EXIT(FREQ_SWITCH, core_id);
|
||||
}
|
||||
|
||||
portENTER_CRITICAL_ISR(&s_switch_lock);
|
||||
s_mode = new_mode;
|
||||
s_is_switching = false;
|
||||
portEXIT_CRITICAL_ISR(&s_switch_lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Calculate new CCOMPARE value based on s_ccount_{mul,div}
|
||||
*
|
||||
* Adjusts CCOMPARE value so that the interrupt happens at the same time as it
|
||||
* would happen without the frequency change.
|
||||
* Assumes that the new_frequency = old_frequency * s_ccount_mul / s_ccount_div.
|
||||
*/
|
||||
static void IRAM_ATTR update_ccompare()
|
||||
{
|
||||
const uint32_t ccompare_min_cycles_in_future = 1000;
|
||||
uint32_t ccount = XTHAL_GET_CCOUNT();
|
||||
uint32_t ccompare = XTHAL_GET_CCOMPARE(XT_TIMER_INDEX);
|
||||
if ((ccompare - ccompare_min_cycles_in_future) - ccount < UINT32_MAX / 2) {
|
||||
uint32_t diff = ccompare - ccount;
|
||||
uint32_t diff_scaled = (diff * s_ccount_mul + s_ccount_div - 1) / s_ccount_div;
|
||||
if (diff_scaled < _xt_tick_divisor) {
|
||||
uint32_t new_ccompare = ccount + diff_scaled;
|
||||
XTHAL_SET_CCOMPARE(XT_TIMER_INDEX, new_ccompare);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void IRAM_ATTR leave_idle()
|
||||
{
|
||||
int core_id = xPortGetCoreID();
|
||||
if (s_core_idle[core_id]) {
|
||||
// TODO: possible optimization: raise frequency here first
|
||||
esp_pm_lock_acquire(s_rtos_lock_handle[core_id]);
|
||||
s_core_idle[core_id] = false;
|
||||
}
|
||||
}
|
||||
|
||||
void esp_pm_impl_idle_hook()
|
||||
{
|
||||
int core_id = xPortGetCoreID();
|
||||
uint32_t state = portENTER_CRITICAL_NESTED();
|
||||
if (!s_core_idle[core_id]) {
|
||||
esp_pm_lock_release(s_rtos_lock_handle[core_id]);
|
||||
s_core_idle[core_id] = true;
|
||||
}
|
||||
portEXIT_CRITICAL_NESTED(state);
|
||||
ESP_PM_TRACE_ENTER(IDLE, core_id);
|
||||
}
|
||||
|
||||
void IRAM_ATTR esp_pm_impl_isr_hook()
|
||||
{
|
||||
int core_id = xPortGetCoreID();
|
||||
ESP_PM_TRACE_ENTER(ISR_HOOK, core_id);
|
||||
#if portNUM_PROCESSORS == 2
|
||||
if (s_need_update_ccompare[core_id]) {
|
||||
update_ccompare();
|
||||
s_need_update_ccompare[core_id] = false;
|
||||
} else {
|
||||
leave_idle();
|
||||
}
|
||||
#else
|
||||
leave_idle();
|
||||
#endif // portNUM_PROCESSORS == 2
|
||||
ESP_PM_TRACE_EXIT(ISR_HOOK, core_id);
|
||||
}
|
||||
|
||||
#ifdef WITH_PROFILING
|
||||
void esp_pm_impl_dump_stats(FILE* out)
|
||||
{
|
||||
pm_time_t time_in_mode[PM_MODE_COUNT];
|
||||
|
||||
portENTER_CRITICAL_ISR(&s_switch_lock);
|
||||
memcpy(time_in_mode, s_time_in_mode, sizeof(time_in_mode));
|
||||
pm_time_t last_mode_change_time = s_last_mode_change_time;
|
||||
pm_mode_t cur_mode = s_mode;
|
||||
pm_time_t now = pm_get_time();
|
||||
portEXIT_CRITICAL_ISR(&s_switch_lock);
|
||||
|
||||
time_in_mode[cur_mode] += now - last_mode_change_time;
|
||||
|
||||
for (int i = 0; i < PM_MODE_COUNT; ++i) {
|
||||
fprintf(out, "%8s %12lld %2d%%\n",
|
||||
s_mode_names[i],
|
||||
time_in_mode[i],
|
||||
(int) (time_in_mode[i] * 100 / now));
|
||||
}
|
||||
}
|
||||
#endif // WITH_PROFILING
|
||||
|
||||
void esp_pm_impl_init()
|
||||
{
|
||||
s_cpu_freq_to_ticks[RTC_CPU_FREQ_XTAL] = rtc_clk_xtal_freq_get();
|
||||
#ifdef CONFIG_PM_TRACE
|
||||
esp_pm_trace_init();
|
||||
#endif
|
||||
ESP_ERROR_CHECK(esp_pm_lock_create(ESP_PM_CPU_FREQ_MAX, 0, "rtos0",
|
||||
&s_rtos_lock_handle[0]));
|
||||
ESP_ERROR_CHECK(esp_pm_lock_acquire(s_rtos_lock_handle[0]));
|
||||
#if portNUM_PROCESSORS == 2
|
||||
ESP_ERROR_CHECK(esp_pm_lock_create(ESP_PM_CPU_FREQ_MAX, 0, "rtos1",
|
||||
&s_rtos_lock_handle[1]));
|
||||
ESP_ERROR_CHECK(esp_pm_lock_acquire(s_rtos_lock_handle[1]));
|
||||
#endif // portNUM_PROCESSORS == 2
|
||||
}
|
117
components/esp32/pm_impl.h
Normal file
117
components/esp32/pm_impl.h
Normal file
@ -0,0 +1,117 @@
|
||||
// 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
|
||||
|
||||
/**
|
||||
* @file pm_impl.h
|
||||
*
|
||||
* This header file defines interface between PM lock functions (pm_locks.c)
|
||||
* and the chip-specific power management (DFS/light sleep) implementation.
|
||||
*/
|
||||
|
||||
#include "soc/rtc.h"
|
||||
#include "esp_pm.h"
|
||||
#include "esp_timer.h"
|
||||
#include "sdkconfig.h"
|
||||
|
||||
|
||||
/**
|
||||
* This is an enum of possible power modes supported by the implementation
|
||||
*/
|
||||
typedef enum {
|
||||
PM_MODE_LIGHT_SLEEP,//!< Light sleep
|
||||
PM_MODE_APB_MIN, //!< Idle (no CPU frequency or APB frequency locks)
|
||||
PM_MODE_APB_MAX, //!< Maximum APB frequency mode
|
||||
PM_MODE_CPU_MAX, //!< Maximum CPU frequency mode
|
||||
PM_MODE_COUNT //!< Number of items
|
||||
} pm_mode_t;
|
||||
|
||||
/**
|
||||
* @brief Get the mode corresponding to a certain lock
|
||||
* @param type lock type
|
||||
* @param arg argument value for this lock (passed to esp_pm_lock_create)
|
||||
* @return lowest power consumption mode which meets the constraints of the lock
|
||||
*/
|
||||
pm_mode_t esp_pm_impl_get_mode(esp_pm_lock_type_t type, int arg);
|
||||
|
||||
/**
|
||||
* If profiling is enabled, this data type will be used to store microsecond
|
||||
* timestamps.
|
||||
*/
|
||||
typedef int64_t pm_time_t;
|
||||
|
||||
/**
|
||||
* See \ref esp_pm_impl_switch_mode
|
||||
*/
|
||||
typedef enum {
|
||||
MODE_LOCK,
|
||||
MODE_UNLOCK
|
||||
} pm_mode_switch_t;
|
||||
|
||||
/**
|
||||
* @brief Switch between power modes when lock is taken or released
|
||||
* @param mode pm_mode_t corresponding to the lock being taken or released,
|
||||
* as returned by \ref esp_pm_impl_get_mode
|
||||
* @param lock_or_unlock
|
||||
* - MODE_LOCK: lock was taken. Implementation needs to make sure
|
||||
* that the constraints of the lock are met by switching to the
|
||||
* given 'mode' or any of the higher power ones.
|
||||
* - MODE_UNLOCK: lock was released. If all the locks for given
|
||||
* mode are released, and no locks for higher power modes are
|
||||
* taken, implementation can switch to one of lower power modes.
|
||||
* @param now timestamp when the lock was taken or released. Passed as
|
||||
* a minor optimization, so that the implementation does not need to
|
||||
* call pm_get_time again.
|
||||
*/
|
||||
void esp_pm_impl_switch_mode(pm_mode_t mode, pm_mode_switch_t lock_or_unlock, pm_time_t now);
|
||||
|
||||
/**
|
||||
* @brief Call once at startup to initialize pm implementation
|
||||
*/
|
||||
void esp_pm_impl_init();
|
||||
|
||||
/**
|
||||
* @brief Hook function for the idle task
|
||||
* Must be called from the IDLE task on each CPU before entering waiti state.
|
||||
*/
|
||||
void esp_pm_impl_idle_hook();
|
||||
|
||||
/**
|
||||
* @brief Hook function for the interrupt dispatcher
|
||||
* Must be called soon after entering the ISR
|
||||
*/
|
||||
void esp_pm_impl_isr_hook();
|
||||
|
||||
/**
|
||||
* @brief Dump the information about time spent in each of the pm modes.
|
||||
*
|
||||
* Prints three columns:
|
||||
* mode name, total time in mode (in microseconds), percentage of time in mode
|
||||
*
|
||||
* @param out stream to dump the information to
|
||||
*/
|
||||
void esp_pm_impl_dump_stats(FILE* out);
|
||||
|
||||
|
||||
#ifdef CONFIG_PM_PROFILING
|
||||
#define WITH_PROFILING
|
||||
#endif
|
||||
|
||||
#ifdef WITH_PROFILING
|
||||
static inline pm_time_t IRAM_ATTR pm_get_time()
|
||||
{
|
||||
return esp_timer_get_time();
|
||||
}
|
||||
#endif // WITH_PROFILING
|
205
components/esp32/pm_locks.c
Normal file
205
components/esp32/pm_locks.c
Normal file
@ -0,0 +1,205 @@
|
||||
// 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;
|
||||
}
|
||||
|
||||
|
51
components/esp32/pm_trace.c
Normal file
51
components/esp32/pm_trace.c
Normal file
@ -0,0 +1,51 @@
|
||||
// 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 "pm_trace.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "soc/gpio_reg.h"
|
||||
|
||||
/* GPIOs to use for tracing of esp_pm events.
|
||||
* Two entries in the array for each type, one for each CPU.
|
||||
* Feel free to change when debugging.
|
||||
*/
|
||||
static const int DRAM_ATTR s_trace_io[] = {
|
||||
BIT(4), BIT(5), // ESP_PM_TRACE_IDLE
|
||||
BIT(16), BIT(17), // ESP_PM_TRACE_TICK
|
||||
BIT(18), BIT(18), // ESP_PM_TRACE_FREQ_SWITCH
|
||||
BIT(19), BIT(19), // ESP_PM_TRACE_CCOMPARE_UPDATE
|
||||
BIT(25), BIT(26), // ESP_PM_TRACE_ISR_HOOK
|
||||
};
|
||||
|
||||
void esp_pm_trace_init()
|
||||
{
|
||||
for (size_t i = 0; i < sizeof(s_trace_io)/sizeof(s_trace_io[0]); ++i) {
|
||||
int io = __builtin_ffs(s_trace_io[i]);
|
||||
if (io == 0) {
|
||||
continue;
|
||||
}
|
||||
gpio_set_direction(io - 1, GPIO_MODE_OUTPUT);
|
||||
}
|
||||
}
|
||||
|
||||
void IRAM_ATTR esp_pm_trace_enter(esp_pm_trace_event_t event, int core_id)
|
||||
{
|
||||
REG_WRITE(GPIO_OUT_W1TS_REG, s_trace_io[2 * event + core_id]);
|
||||
}
|
||||
|
||||
void IRAM_ATTR esp_pm_trace_exit(esp_pm_trace_event_t event, int core_id)
|
||||
{
|
||||
REG_WRITE(GPIO_OUT_W1TC_REG, s_trace_io[2 * event + core_id]);
|
||||
}
|
44
components/esp32/pm_trace.h
Normal file
44
components/esp32/pm_trace.h
Normal file
@ -0,0 +1,44 @@
|
||||
// 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 "sdkconfig.h"
|
||||
|
||||
typedef enum {
|
||||
ESP_PM_TRACE_IDLE,
|
||||
ESP_PM_TRACE_TICK,
|
||||
ESP_PM_TRACE_FREQ_SWITCH,
|
||||
ESP_PM_TRACE_CCOMPARE_UPDATE,
|
||||
ESP_PM_TRACE_ISR_HOOK,
|
||||
ESP_PM_TRACE_TYPE_MAX
|
||||
} esp_pm_trace_event_t;
|
||||
|
||||
void esp_pm_trace_init();
|
||||
void esp_pm_trace_enter(esp_pm_trace_event_t event, int core_id);
|
||||
void esp_pm_trace_exit(esp_pm_trace_event_t event, int core_id);
|
||||
|
||||
#ifdef CONFIG_PM_TRACE
|
||||
|
||||
#define ESP_PM_TRACE_ENTER(event, core_id) \
|
||||
esp_pm_trace_enter(ESP_PM_TRACE_ ## event, core_id)
|
||||
#define ESP_PM_TRACE_EXIT(event, core_id) \
|
||||
esp_pm_trace_exit(ESP_PM_TRACE_ ## event, core_id)
|
||||
|
||||
#else // CONFIG_PM_TRACE
|
||||
|
||||
#define ESP_PM_TRACE_ENTER(type, core_id) do { (void) core_id; } while(0);
|
||||
#define ESP_PM_TRACE_EXIT(type, core_id) do { (void) core_id; } while(0);
|
||||
|
||||
#endif // CONFIG_PM_TRACE
|
@ -3,7 +3,7 @@
|
||||
#include <time.h>
|
||||
#include <sys/time.h>
|
||||
#include "unity.h"
|
||||
#include "../esp_timer.h"
|
||||
#include "esp_timer.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/semphr.h"
|
||||
@ -113,6 +113,7 @@ TEST_CASE("periodic esp_timer produces correct delays", "[esp_timer]")
|
||||
size_t cur_interval;
|
||||
int intervals[NUM_INTERVALS];
|
||||
int64_t t_start;
|
||||
SemaphoreHandle_t done;
|
||||
} test_args_t;
|
||||
|
||||
void timer_func(void* arg)
|
||||
@ -128,6 +129,7 @@ TEST_CASE("periodic esp_timer produces correct delays", "[esp_timer]")
|
||||
if (p_args->cur_interval == NUM_INTERVALS) {
|
||||
printf("done\n");
|
||||
TEST_ESP_OK(esp_timer_stop(p_args->timer));
|
||||
xSemaphoreGive(p_args->done);
|
||||
}
|
||||
}
|
||||
|
||||
@ -137,15 +139,16 @@ TEST_CASE("periodic esp_timer produces correct delays", "[esp_timer]")
|
||||
esp_timer_create_args_t create_args = {
|
||||
.callback = &timer_func,
|
||||
.arg = &args,
|
||||
.name = "timer1"
|
||||
.name = "timer1",
|
||||
};
|
||||
TEST_ESP_OK(esp_timer_create(&create_args, &timer1));
|
||||
ref_clock_init();
|
||||
args.timer = timer1;
|
||||
args.t_start = ref_clock_get();
|
||||
args.done = xSemaphoreCreateBinary();
|
||||
TEST_ESP_OK(esp_timer_start_periodic(timer1, delay_ms * 1000));
|
||||
|
||||
vTaskDelay(delay_ms * (NUM_INTERVALS + 1));
|
||||
TEST_ASSERT(xSemaphoreTake(args.done, delay_ms * NUM_INTERVALS * 2));
|
||||
|
||||
TEST_ASSERT_EQUAL_UINT32(NUM_INTERVALS, args.cur_interval);
|
||||
for (size_t i = 0; i < NUM_INTERVALS; ++i) {
|
||||
@ -155,6 +158,7 @@ TEST_CASE("periodic esp_timer produces correct delays", "[esp_timer]")
|
||||
TEST_ESP_OK( esp_timer_dump(stdout) );
|
||||
|
||||
TEST_ESP_OK( esp_timer_delete(timer1) );
|
||||
vSemaphoreDelete(args.done);
|
||||
#undef NUM_INTERVALS
|
||||
}
|
||||
|
||||
@ -370,3 +374,8 @@ TEST_CASE("esp_timer_get_time returns monotonic values", "[esp_timer][ignore]")
|
||||
vSemaphoreDelete(done_2);
|
||||
ref_clock_deinit();
|
||||
}
|
||||
|
||||
TEST_CASE("Can dump esp_timer stats", "[esp_timer]")
|
||||
{
|
||||
esp_timer_dump(stdout);
|
||||
}
|
||||
|
12
components/esp32/test/test_pm.c
Normal file
12
components/esp32/test/test_pm.c
Normal file
@ -0,0 +1,12 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
#include <sys/time.h>
|
||||
#include "unity.h"
|
||||
#include "esp_pm.h"
|
||||
|
||||
|
||||
TEST_CASE("Can dump power management lock stats", "[pm]")
|
||||
{
|
||||
esp_pm_dump_locks(stdout);
|
||||
}
|
@ -1,9 +1,9 @@
|
||||
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
|
||||
// Copyright 2015-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
|
||||
@ -14,10 +14,43 @@
|
||||
|
||||
#include <esp_event.h>
|
||||
#include <esp_wifi.h>
|
||||
#include "esp_log.h"
|
||||
#include "esp_wifi_internal.h"
|
||||
#include "esp_pm.h"
|
||||
#include "soc/rtc.h"
|
||||
|
||||
#ifdef CONFIG_PM_ENABLE
|
||||
static esp_pm_lock_handle_t s_wifi_modem_sleep_lock;
|
||||
#endif
|
||||
|
||||
esp_err_t esp_wifi_init(const wifi_init_config_t *config)
|
||||
{
|
||||
esp_event_set_default_wifi_handlers();
|
||||
return esp_wifi_init_internal(config);
|
||||
#ifdef CONFIG_PM_ENABLE
|
||||
if (s_wifi_modem_sleep_lock == NULL) {
|
||||
esp_err_t err = esp_pm_lock_create(ESP_PM_APB_FREQ_MAX, 0, "wifi",
|
||||
&s_wifi_modem_sleep_lock);
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
esp_event_set_default_wifi_handlers();
|
||||
return esp_wifi_init_internal(config);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_ENABLE
|
||||
void wifi_apb80m_request(void)
|
||||
{
|
||||
assert(s_wifi_modem_sleep_lock);
|
||||
esp_pm_lock_acquire(s_wifi_modem_sleep_lock);
|
||||
if (rtc_clk_apb_freq_get() != APB_CLK_FREQ) {
|
||||
ESP_LOGE(__func__, "WiFi needs 80MHz APB frequency to work, but got %dHz", rtc_clk_apb_freq_get());
|
||||
}
|
||||
}
|
||||
|
||||
void wifi_apb80m_release(void)
|
||||
{
|
||||
assert(s_wifi_modem_sleep_lock);
|
||||
esp_pm_lock_release(s_wifi_modem_sleep_lock);
|
||||
}
|
||||
#endif //CONFIG_PM_ENABLE
|
||||
|
@ -36,6 +36,7 @@
|
||||
#include "esp_log.h"
|
||||
#include "esp_eth.h"
|
||||
#include "esp_intr_alloc.h"
|
||||
#include "esp_pm.h"
|
||||
|
||||
#include "driver/periph_ctrl.h"
|
||||
|
||||
@ -71,6 +72,9 @@ static SemaphoreHandle_t emac_rx_xMutex = NULL;
|
||||
static SemaphoreHandle_t emac_tx_xMutex = NULL;
|
||||
static const char *TAG = "emac";
|
||||
static bool pause_send = false;
|
||||
#ifdef CONFIG_PM_ENABLE
|
||||
static esp_pm_lock_handle_t s_pm_lock;
|
||||
#endif
|
||||
|
||||
static esp_err_t emac_ioctl(emac_sig_t sig, emac_par_t par);
|
||||
esp_err_t emac_post(emac_sig_t sig, emac_par_t par);
|
||||
@ -804,13 +808,31 @@ esp_err_t esp_eth_enable(void)
|
||||
return open_cmd.err;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_ENABLE
|
||||
esp_err_t err = esp_pm_lock_create(ESP_PM_APB_FREQ_MAX, 0, "ethernet", &s_pm_lock);
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
esp_pm_lock_acquire(s_pm_lock);
|
||||
#endif //CONFIG_PM_ENABLE
|
||||
|
||||
if (emac_config.emac_status != EMAC_RUNTIME_NOT_INIT) {
|
||||
if (emac_ioctl(SIG_EMAC_START, (emac_par_t)(&post_cmd)) != 0) {
|
||||
open_cmd.err = EMAC_CMD_FAIL;
|
||||
goto cleanup;
|
||||
}
|
||||
} else {
|
||||
open_cmd.err = EMAC_CMD_FAIL;
|
||||
goto cleanup;
|
||||
}
|
||||
return EMAC_CMD_OK;
|
||||
|
||||
cleanup:
|
||||
#ifdef CONFIG_PM_ENABLE
|
||||
esp_pm_lock_release(s_pm_lock);
|
||||
esp_pm_lock_delete(s_pm_lock);
|
||||
s_pm_lock = NULL;
|
||||
#endif //CONFIG_PM_ENABLE
|
||||
return open_cmd.err;
|
||||
}
|
||||
|
||||
@ -854,6 +876,12 @@ esp_err_t esp_eth_disable(void)
|
||||
return close_cmd.err;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_ENABLE
|
||||
esp_pm_lock_release(s_pm_lock);
|
||||
esp_pm_lock_delete(s_pm_lock);
|
||||
s_pm_lock = NULL;
|
||||
#endif // CONFIG_PM_ENABLE
|
||||
|
||||
if (emac_config.emac_status == EMAC_RUNTIME_START) {
|
||||
if (emac_ioctl(SIG_EMAC_STOP, (emac_par_t)(&post_cmd)) != 0) {
|
||||
close_cmd.err = EMAC_CMD_FAIL;
|
||||
|
@ -95,8 +95,20 @@
|
||||
#define configNUM_THREAD_LOCAL_STORAGE_POINTERS CONFIG_FREERTOS_THREAD_LOCAL_STORAGE_POINTERS
|
||||
#define configTHREAD_LOCAL_STORAGE_DELETE_CALLBACKS 1
|
||||
|
||||
/* TODO: config freq by menuconfig */
|
||||
#define XT_CLOCK_FREQ (CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ * 1000000)
|
||||
#ifndef __ASSEMBLER__
|
||||
|
||||
/**
|
||||
* This function is defined to provide a deprecation warning whenever
|
||||
* XT_CLOCK_FREQ macro is used.
|
||||
* Update the code to use esp_clk_cpu_freq function instead.
|
||||
* @return current CPU clock frequency, in Hz
|
||||
*/
|
||||
int xt_clock_freq(void) __attribute__((deprecated));
|
||||
|
||||
#define XT_CLOCK_FREQ (xt_clock_freq())
|
||||
|
||||
#endif // __ASSEMBLER__
|
||||
|
||||
|
||||
/* Required for configuration-dependent settings */
|
||||
#include "xtensa_config.h"
|
||||
|
@ -55,7 +55,7 @@ Should be included by all Xtensa generic and RTOS port-specific sources.
|
||||
/*
|
||||
Include any RTOS specific definitions that are needed by this header.
|
||||
*/
|
||||
#include <FreeRTOSConfig.h>
|
||||
#include "FreeRTOSConfig.h"
|
||||
|
||||
/*
|
||||
Convert FreeRTOSConfig definitions to XTENSA definitions.
|
||||
|
@ -49,7 +49,7 @@ and the Xtensa core configuration need not have a timer.
|
||||
|
||||
#include "xtensa_rtos.h" /* in case this wasn't included directly */
|
||||
|
||||
#include <FreeRTOSConfig.h>
|
||||
#include "FreeRTOSConfig.h"
|
||||
|
||||
/*
|
||||
Select timer to use for periodic tick, and determine its interrupt number
|
||||
|
@ -24,6 +24,7 @@
|
||||
*/
|
||||
|
||||
#include "xtensa_rtos.h"
|
||||
#include "sdkconfig.h"
|
||||
|
||||
#define TOPOFSTACK_OFFS 0x00 /* StackType_t *pxTopOfStack */
|
||||
#define CP_TOPOFSTACK_OFFS 0x04 /* xMPU_SETTINGS.coproc_area */
|
||||
@ -269,6 +270,12 @@ _frxt_timer_int:
|
||||
|
||||
ENTRY(16)
|
||||
|
||||
#ifdef CONFIG_PM_TRACE
|
||||
movi a6, 1 /* = ESP_PM_TRACE_TICK */
|
||||
getcoreid a7
|
||||
call4 esp_pm_trace_enter
|
||||
#endif // CONFIG_PM_TRACE
|
||||
|
||||
.L_xt_timer_int_catchup:
|
||||
|
||||
/* Update the timer comparator for the next tick. */
|
||||
@ -308,6 +315,12 @@ _frxt_timer_int:
|
||||
sub a4, a4, a3 /* diff = ccount - old comparator */
|
||||
blt a2, a4, .L_xt_timer_int_catchup /* repeat while diff > divisor */
|
||||
|
||||
#ifdef CONFIG_PM_TRACE
|
||||
movi a6, 1 /* = ESP_PM_TRACE_TICK */
|
||||
getcoreid a7
|
||||
call4 esp_pm_trace_exit
|
||||
#endif // CONFIG_PM_TRACE
|
||||
|
||||
RET(16)
|
||||
|
||||
/*
|
||||
|
@ -34,31 +34,21 @@ that are implemented in C.
|
||||
#endif
|
||||
|
||||
#include "xtensa_rtos.h"
|
||||
#include "esp_clk.h"
|
||||
|
||||
#ifdef XT_RTOS_TIMER_INT
|
||||
|
||||
unsigned _xt_tick_divisor = 0; /* cached number of cycles per tick */
|
||||
|
||||
/*
|
||||
Compute and initialize at run-time the tick divisor (the number of
|
||||
processor clock cycles in an RTOS tick, used to set the tick timer).
|
||||
Called when the processor clock frequency is not known at compile-time.
|
||||
*/
|
||||
void _xt_tick_divisor_init(void)
|
||||
{
|
||||
#ifdef XT_CLOCK_FREQ
|
||||
_xt_tick_divisor = esp_clk_cpu_freq() / XT_TICK_PER_SEC;
|
||||
}
|
||||
|
||||
_xt_tick_divisor = (XT_CLOCK_FREQ / XT_TICK_PER_SEC);
|
||||
|
||||
#else
|
||||
|
||||
#ifdef XT_BOARD
|
||||
_xt_tick_divisor = xtbsp_clock_freq_hz() / XT_TICK_PER_SEC;
|
||||
#else
|
||||
#error "No way to obtain processor clock frequency"
|
||||
#endif /* XT_BOARD */
|
||||
|
||||
#endif /* XT_CLOCK_FREQ */
|
||||
/* Deprecated, to be removed */
|
||||
int xt_clock_freq(void)
|
||||
{
|
||||
return esp_clk_cpu_freq();
|
||||
}
|
||||
|
||||
#endif /* XT_RTOS_TIMER_INT */
|
||||
|
@ -175,7 +175,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
NOTE: For CALL0 ABI, a12-a15 have not yet been saved.
|
||||
|
||||
NOTE: This macro will use registers a0 and a2-a6. The arguments are:
|
||||
NOTE: This macro will use registers a0 and a2-a7. The arguments are:
|
||||
level -- interrupt level
|
||||
mask -- interrupt bitmask for this level
|
||||
--------------------------------------------------------------------------------
|
||||
@ -183,6 +183,12 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
.macro dispatch_c_isr level mask
|
||||
|
||||
#ifdef CONFIG_PM_TRACE
|
||||
movi a6, 0 /* = ESP_PM_TRACE_IDLE */
|
||||
getcoreid a7
|
||||
call4 esp_pm_trace_exit
|
||||
#endif // CONFIG_PM_TRACE
|
||||
|
||||
/* Get mask of pending, enabled interrupts at this level into a2. */
|
||||
|
||||
.L_xt_user_int_&level&:
|
||||
@ -206,6 +212,10 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_PM_ENABLE
|
||||
call4 esp_pm_impl_isr_hook
|
||||
#endif
|
||||
|
||||
#ifdef XT_INTEXC_HOOKS
|
||||
/* Call interrupt hook if present to (pre)handle interrupts. */
|
||||
movi a4, _xt_intexc_hooks
|
||||
|
@ -26,7 +26,7 @@
|
||||
#include "esp_attr.h"
|
||||
#include "esp_intr_alloc.h"
|
||||
#include "esp_clk.h"
|
||||
#include "../esp32/esp_timer.h"
|
||||
#include "esp_timer.h"
|
||||
#include "soc/soc.h"
|
||||
#include "soc/rtc.h"
|
||||
#include "soc/rtc_cntl_reg.h"
|
||||
@ -145,6 +145,15 @@ void esp_set_time_from_rtc()
|
||||
#endif // WITH_FRC1 && WITH_RTC
|
||||
}
|
||||
|
||||
uint64_t esp_clk_rtc_time(void)
|
||||
{
|
||||
#ifdef WITH_RTC
|
||||
return get_rtc_time_us();
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
clock_t IRAM_ATTR _times_r(struct _reent *r, struct tms *ptms)
|
||||
{
|
||||
clock_t t = xTaskGetTickCount() * (portTICK_PERIOD_MS * CLK_TCK / 1000);
|
||||
|
@ -276,6 +276,25 @@ rtc_fast_freq_t rtc_clk_fast_freq_get();
|
||||
*/
|
||||
void rtc_clk_cpu_freq_set(rtc_cpu_freq_t cpu_freq);
|
||||
|
||||
/**
|
||||
* @brief Switch CPU frequency
|
||||
*
|
||||
* This is a faster version of rtc_clk_cpu_freq_set, which can handle some of
|
||||
* the frequency switch paths (XTAL -> PLL, PLL -> XTAL).
|
||||
* When switching from PLL to XTAL, PLL is not disabled (unlike rtc_clk_cpu_freq_set).
|
||||
* When switching back from XTAL to PLL, only the same PLL can be used.
|
||||
* Therefore it is not possible to switch 240 -> XTAL -> (80 or 160) using this
|
||||
* function.
|
||||
*
|
||||
* For unsupported cases, this function falls back to rtc_clk_cpu_freq_set.
|
||||
*
|
||||
* Unlike rtc_clk_cpu_freq_set, this function relies on static data, so it is
|
||||
* less safe to use it e.g. from a panic handler (when memory might be corrupted).
|
||||
*
|
||||
* @param cpu_freq new CPU frequency
|
||||
*/
|
||||
void rtc_clk_cpu_freq_set_fast(rtc_cpu_freq_t cpu_freq);
|
||||
|
||||
/**
|
||||
* @brief Get the currently selected CPU frequency
|
||||
*
|
||||
@ -296,6 +315,14 @@ rtc_cpu_freq_t rtc_clk_cpu_freq_get();
|
||||
*/
|
||||
uint32_t rtc_clk_cpu_freq_value(rtc_cpu_freq_t cpu_freq);
|
||||
|
||||
/**
|
||||
* @brief Get rtc_cpu_freq_t enum value for given CPU frequency
|
||||
* @param cpu_freq_mhz CPU frequency, one of 80, 160, 240, 2, and XTAL frequency
|
||||
* @param[out] out_val output, rtc_cpu_freq_t value corresponding to the frequency
|
||||
* @return true if the given frequency value matches one of enum values
|
||||
*/
|
||||
bool rtc_clk_cpu_freq_from_mhz(int cpu_freq_mhz, rtc_cpu_freq_t* out_val);
|
||||
|
||||
/**
|
||||
* @brief Store new APB frequency value into RTC_APB_FREQ_REG
|
||||
*
|
||||
|
@ -266,6 +266,7 @@
|
||||
#define CPU_CLK_FREQ_ROM APB_CLK_FREQ_ROM
|
||||
#define CPU_CLK_FREQ APB_CLK_FREQ
|
||||
#define APB_CLK_FREQ ( 80*1000000 ) //unit: Hz
|
||||
#define REF_CLK_FREQ ( 1000000 )
|
||||
#define UART_CLK_FREQ APB_CLK_FREQ
|
||||
#define WDT_CLK_FREQ APB_CLK_FREQ
|
||||
#define TIMER_CLK_FREQ (80000000>>4) //80MHz divided by 16
|
||||
|
@ -72,9 +72,9 @@ static const char* TAG = "rtc_clk";
|
||||
* All values are in microseconds.
|
||||
* TODO: some of these are excessive, and should be reduced.
|
||||
*/
|
||||
#define DELAY_CPU_FREQ_SWITCH_TO_XTAL_WITH_150K 80
|
||||
#define DELAY_CPU_FREQ_SWITCH_TO_XTAL_WITH_150K 20
|
||||
#define DELAY_CPU_FREQ_SWITCH_TO_XTAL_WITH_32K 160
|
||||
#define DELAY_CPU_FREQ_SWITCH_TO_PLL 10
|
||||
#define DELAY_CPU_FREQ_SWITCH_TO_PLL 20
|
||||
#define DELAY_PLL_DBIAS_RAISE 3
|
||||
#define DELAY_PLL_ENABLE_WITH_150K 80
|
||||
#define DELAY_PLL_ENABLE_WITH_32K 160
|
||||
@ -87,6 +87,8 @@ static const char* TAG = "rtc_clk";
|
||||
*/
|
||||
#define XTAL_FREQ_EST_CYCLES 10
|
||||
|
||||
static rtc_cpu_freq_t s_cur_freq = RTC_CPU_FREQ_XTAL;
|
||||
static int s_pll_freq = 0;
|
||||
|
||||
static void rtc_clk_32k_enable_internal(int dac, int dres, int dbias)
|
||||
{
|
||||
@ -323,6 +325,70 @@ void rtc_clk_bbpll_set(rtc_xtal_freq_t xtal_freq, rtc_cpu_freq_t cpu_freq)
|
||||
ets_delay_us(delay_pll_en);
|
||||
}
|
||||
|
||||
/**
|
||||
* Switch to XTAL frequency. Does not disable the PLL.
|
||||
*/
|
||||
static void rtc_clk_cpu_freq_to_xtal()
|
||||
{
|
||||
rtc_xtal_freq_t xtal_freq = rtc_clk_xtal_freq_get();
|
||||
ets_update_cpu_frequency(xtal_freq);
|
||||
REG_SET_FIELD(RTC_CNTL_REG, RTC_CNTL_DIG_DBIAS_WAK, RTC_CNTL_DBIAS_1V10);
|
||||
REG_SET_FIELD(APB_CTRL_SYSCLK_CONF_REG, APB_CTRL_PRE_DIV_CNT, 0);
|
||||
REG_SET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_SOC_CLK_SEL, RTC_CNTL_SOC_CLK_SEL_XTL);
|
||||
DPORT_REG_WRITE(DPORT_CPU_PER_CONF_REG, 0); // clear DPORT_CPUPERIOD_SEL
|
||||
|
||||
rtc_clk_apb_freq_update(xtal_freq * MHZ);
|
||||
s_cur_freq = RTC_CPU_FREQ_XTAL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Switch to one of PLL-based frequencies. Current frequency can be XTAL or PLL.
|
||||
* PLL must already be enabled.
|
||||
* If switching between frequencies derived from different PLLs (320M and 480M),
|
||||
* fall back to rtc_clk_cpu_freq_set.
|
||||
* @param cpu_freq new CPU frequency
|
||||
*/
|
||||
static void rtc_clk_cpu_freq_to_pll(rtc_cpu_freq_t cpu_freq)
|
||||
{
|
||||
int freq = 0;
|
||||
if ((cpu_freq == RTC_CPU_FREQ_240M && s_pll_freq == 320) ||
|
||||
(cpu_freq != RTC_CPU_FREQ_240M && s_pll_freq == 240)) {
|
||||
/* need to switch PLLs, fall back to full implementation */
|
||||
rtc_clk_cpu_freq_set(cpu_freq);
|
||||
return;
|
||||
}
|
||||
|
||||
if (cpu_freq == RTC_CPU_FREQ_80M) {
|
||||
DPORT_REG_WRITE(DPORT_CPU_PER_CONF_REG, 0);
|
||||
freq = 80;
|
||||
} else if (cpu_freq == RTC_CPU_FREQ_160M) {
|
||||
DPORT_REG_WRITE(DPORT_CPU_PER_CONF_REG, 1);
|
||||
freq = 160;
|
||||
} else if (cpu_freq == RTC_CPU_FREQ_240M) {
|
||||
REG_SET_FIELD(RTC_CNTL_REG, RTC_CNTL_DIG_DBIAS_WAK, RTC_CNTL_DBIAS_1V25);
|
||||
DPORT_REG_WRITE(DPORT_CPU_PER_CONF_REG, 2);
|
||||
freq = 240;
|
||||
}
|
||||
REG_SET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_SOC_CLK_SEL, RTC_CNTL_SOC_CLK_SEL_PLL);
|
||||
rtc_clk_apb_freq_update(80 * MHZ);
|
||||
ets_update_cpu_frequency(freq);
|
||||
s_cur_freq = cpu_freq;
|
||||
}
|
||||
|
||||
void rtc_clk_cpu_freq_set_fast(rtc_cpu_freq_t cpu_freq)
|
||||
{
|
||||
if (cpu_freq == s_cur_freq) {
|
||||
return;
|
||||
} else if (cpu_freq == RTC_CPU_FREQ_2M || s_cur_freq == RTC_CPU_FREQ_2M) {
|
||||
/* fall back to full implementation if switch to/from 2M is needed */
|
||||
rtc_clk_cpu_freq_set(cpu_freq);
|
||||
} else if (cpu_freq == RTC_CPU_FREQ_XTAL) {
|
||||
rtc_clk_cpu_freq_to_xtal();
|
||||
} else if (cpu_freq > RTC_CPU_FREQ_XTAL) {
|
||||
rtc_clk_cpu_freq_to_pll(cpu_freq);
|
||||
}
|
||||
}
|
||||
|
||||
void rtc_clk_cpu_freq_set(rtc_cpu_freq_t cpu_freq)
|
||||
{
|
||||
rtc_xtal_freq_t xtal_freq = rtc_clk_xtal_freq_get();
|
||||
@ -338,6 +404,7 @@ void rtc_clk_cpu_freq_set(rtc_cpu_freq_t cpu_freq)
|
||||
SET_PERI_REG_MASK(RTC_CNTL_OPTIONS0_REG,
|
||||
RTC_CNTL_BB_I2C_FORCE_PD | RTC_CNTL_BBPLL_FORCE_PD |
|
||||
RTC_CNTL_BBPLL_I2C_FORCE_PD);
|
||||
s_pll_freq = 0;
|
||||
rtc_clk_apb_freq_update(xtal_freq * MHZ);
|
||||
|
||||
/* is APLL under force power down? */
|
||||
@ -365,17 +432,21 @@ void rtc_clk_cpu_freq_set(rtc_cpu_freq_t cpu_freq)
|
||||
if (cpu_freq == RTC_CPU_FREQ_80M) {
|
||||
DPORT_REG_SET_FIELD(DPORT_CPU_PER_CONF_REG, DPORT_CPUPERIOD_SEL, 0);
|
||||
ets_update_cpu_frequency(80);
|
||||
s_pll_freq = 320;
|
||||
} else if (cpu_freq == RTC_CPU_FREQ_160M) {
|
||||
DPORT_REG_SET_FIELD(DPORT_CPU_PER_CONF_REG, DPORT_CPUPERIOD_SEL, 1);
|
||||
ets_update_cpu_frequency(160);
|
||||
s_pll_freq = 320;
|
||||
} else if (cpu_freq == RTC_CPU_FREQ_240M) {
|
||||
DPORT_REG_SET_FIELD(DPORT_CPU_PER_CONF_REG, DPORT_CPUPERIOD_SEL, 2);
|
||||
ets_update_cpu_frequency(240);
|
||||
s_pll_freq = 480;
|
||||
}
|
||||
REG_SET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_SOC_CLK_SEL, RTC_CNTL_SOC_CLK_SEL_PLL);
|
||||
ets_delay_us(DELAY_CPU_FREQ_SWITCH_TO_PLL);
|
||||
rtc_clk_apb_freq_update(80 * MHZ);
|
||||
}
|
||||
s_cur_freq = cpu_freq;
|
||||
}
|
||||
|
||||
rtc_cpu_freq_t rtc_clk_cpu_freq_get()
|
||||
@ -433,6 +504,24 @@ uint32_t rtc_clk_cpu_freq_value(rtc_cpu_freq_t cpu_freq)
|
||||
}
|
||||
}
|
||||
|
||||
bool rtc_clk_cpu_freq_from_mhz(int mhz, rtc_cpu_freq_t* out_val)
|
||||
{
|
||||
if (mhz == 240) {
|
||||
*out_val = RTC_CPU_FREQ_240M;
|
||||
} else if (mhz == 160) {
|
||||
*out_val = RTC_CPU_FREQ_160M;
|
||||
} else if (mhz == 80) {
|
||||
*out_val = RTC_CPU_FREQ_80M;
|
||||
} else if (mhz == (int) rtc_clk_xtal_freq_get()) {
|
||||
*out_val = RTC_CPU_FREQ_XTAL;
|
||||
} else if (mhz == 2) {
|
||||
*out_val = RTC_CPU_FREQ_2M;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Values of RTC_XTAL_FREQ_REG and RTC_APB_FREQ_REG are stored as two copies in
|
||||
* lower and upper 16-bit halves. These are the routines to work with such a
|
||||
* representation.
|
||||
|
@ -49,7 +49,7 @@ static spi_flash_counters_t s_flash_stats;
|
||||
#define COUNTER_STOP(counter) \
|
||||
do{ \
|
||||
s_flash_stats.counter.count++; \
|
||||
s_flash_stats.counter.time += (xthal_get_ccount() - ts_begin) / (XT_CLOCK_FREQ / 1000000); \
|
||||
s_flash_stats.counter.time += (xthal_get_ccount() - ts_begin) / (esp_clk_cpu_freq() / 1000000); \
|
||||
} while(0)
|
||||
|
||||
#define COUNTER_ADD_BYTES(counter, size) \
|
||||
|
@ -146,7 +146,10 @@ INPUT = \
|
||||
##
|
||||
## Application Level Tracing - API Reference
|
||||
##
|
||||
../components/app_trace/include/esp_app_trace.h
|
||||
../components/app_trace/include/esp_app_trace.h \
|
||||
### Power management
|
||||
../components/esp32/include/esp_pm.h \
|
||||
../components/esp32/include/esp32/pm.h
|
||||
|
||||
|
||||
## Get warnings for functions that have no documentation for their parameters or return value
|
||||
|
@ -11,6 +11,7 @@ System API
|
||||
Hooks <hooks>
|
||||
Over The Air Updates (OTA) <ota>
|
||||
Sleep Modes <sleep_modes>
|
||||
Power Management <power_management>
|
||||
Logging <log>
|
||||
Base MAC address <base_mac_address>
|
||||
Application Level Tracing <app_trace>
|
||||
|
123
docs/api-reference/system/power_management.rst
Normal file
123
docs/api-reference/system/power_management.rst
Normal file
@ -0,0 +1,123 @@
|
||||
Power Management
|
||||
================
|
||||
|
||||
Overview
|
||||
--------
|
||||
|
||||
Power management algorithm included in ESP-IDF can adjust APB frequency, CPU frequency, and put the chip into light sleep mode to run the application
|
||||
at smallest possible power consumption, given the requirements of application components.
|
||||
|
||||
Application components can express their requirements by creating and acquiring power management locks.
|
||||
|
||||
For instance, a driver for a peripheral clocked from APB can request the APB frequency to be set to 80 MHz, for the duration while the peripheral is used. Another example is that the RTOS will request the CPU to run at the highest configured frequency while there are tasks ready to run. Yet another example is a peripheral driver which needs interrupts to be enabled. Such driver can request light sleep to be disabled.
|
||||
|
||||
Naturally, requesting higher APB or CPU frequency or disabling light sleep causes higher current consumption. Components should try to limit usage of power management locks to the shortest amount of time possible.
|
||||
|
||||
Configuration
|
||||
-------------
|
||||
|
||||
Power management can be enabled at compile time, using :ref:`CONFIG_PM_ENABLE` option.
|
||||
|
||||
Enabling power management features comes at the cost of increased interrupt latency. Extra latency depends on a number of factors, among which are CPU frequency, single/dual core mode, whether frequency switch needs to be performed or not. Minimal extra latency is 0.2us (when CPU frequency is 240MHz, and frequency scaling is not enabled), maximum extra latency is 40us (when frequency scaling is enabled, and a switch from 40MHz to 80MHz is performed on interrupt entry).
|
||||
|
||||
Dynamic frequency scaling (DFS) can be enabled in the application by calling :cpp:func:`esp_pm_configure` function. Its argument is a structure defining frequency scaling settings (for ESP32, minimum and maximum CPU frequencies). Alternatively, :ref:`CONFIG_PM_DFS_INIT_AUTO` option can be enabled in menuconfig. If enabled, maximal CPU frequency is determined by :ref:`CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ` setting, and minimal CPU frequency is set to the XTAL frequency.
|
||||
|
||||
.. note::
|
||||
|
||||
:cpp:func:`esp_pm_configure` function also has provisions for enabling automatic light sleep mode. However this feature is not fully supported yet, so `esp_pm_configure` will return an `ESP_ERR_NOT_SUPPORTED` if automatic light sleep is requested.
|
||||
|
||||
Power Management Locks
|
||||
----------------------
|
||||
|
||||
As mentioned in the overview, applications can acquire/release locks to control the power management algorithm. When application takes a lock, power management algorithm operation is restricted in a way described below, for each lock. When the lock is released, such restriction is removed.
|
||||
|
||||
Different parts of the application can take the same lock. In this case, lock mush be released the same number of times as it was acquired, in order for power managment algorithm to resume.
|
||||
|
||||
In ESP32, three types of locks are supported:
|
||||
|
||||
``ESP_PM_CPU_FREQ_MAX``
|
||||
Requests CPU frequency to be at the maximal value set via :cpp:func:`esp_pm_configure`. For ESP32, this value can be set to 80, 160, or 240MHz.
|
||||
|
||||
``ESP_PM_APB_FREQ_MAX``
|
||||
Requests APB frequency to be at the maximal supported value. For ESP32, this is 80 MHz.
|
||||
|
||||
``ESP_PM_NO_LIGHT_SLEEP``
|
||||
Prevents automatic light sleep from being used. Note: currently taking this lock has no effect, as automatic light sleep is never used.
|
||||
|
||||
|
||||
Power Management Algorithm for the ESP32
|
||||
----------------------------------------
|
||||
|
||||
When dynamic frequency scaling is enabled, CPU frequency will be switched as follows:
|
||||
|
||||
- If maximal CPU frequency (set using :cpp:func:`esp_pm_configure` or :ref:`CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ`) is 240 MHz:
|
||||
|
||||
1. When ``ESP_PM_CPU_FREQ_MAX`` or ``ESP_PM_APB_FREQ_MAX`` locks are acquired, CPU frequency will be 240 MHz, and APB frequency will be 80 MHz.
|
||||
|
||||
2. Otherwise, frequency will be switched to the minimal value set using :cpp:func:`esp_pm_configure` (usually, XTAL).
|
||||
|
||||
- If maximal CPU frequency is 160 MHz:
|
||||
|
||||
1. When ``ESP_PM_CPU_FREQ_MAX`` is acquired, CPU frequency is set to 160 MHz, and APB frequency to 80 MHz.
|
||||
|
||||
2. When ``ESP_PM_CPU_FREQ_MAX`` is not acquired, but ``ESP_PM_APB_FREQ_MAX`` is, CPU and APB frequencies are set to 80 MHz.
|
||||
|
||||
3. Otherwise, frequency will be switched to the minimal value set using :cpp:func:`esp_pm_configure` (usually, XTAL).
|
||||
|
||||
- If maximal CPU frequency is 80 MHz:
|
||||
|
||||
1. When ``ESP_PM_CPU_FREQ_MAX`` or ``ESP_PM_APB_FREQ_MAX`` locks are acquired, CPU and APB frequencies will be 80 MHz.
|
||||
|
||||
2. Otherwise, frequency will be switched to the minimal value set using :cpp:func:`esp_pm_configure` (usually, XTAL).
|
||||
|
||||
|
||||
Dynamic Frequency Scaling and Peripheral Drivers
|
||||
------------------------------------------------
|
||||
|
||||
When DFS is enabled, APB frequency can be changed several times within a single RTOS tick. Some peripherals can work normally even when APB frequency changes; some can not.
|
||||
|
||||
The following peripherals can work even when APB frequency is changing:
|
||||
|
||||
- UART: if REF_TICK is used as clock source (see `use_ref_tick` member of `uart_config_t`).
|
||||
|
||||
- LEDC: if REF_TICK is used as clock source (see :cpp:func:`ledc_timer_config` function).
|
||||
|
||||
- RMT: if REF_TICK is used as clock source. Currently the driver does not support REF_TICK, but it can be enabled by clearing ``RMT_REF_ALWAYS_ON_CHx`` bit for the respective channel.
|
||||
|
||||
Currently, the following peripheral drivers are aware of DFS and will use ``ESP_PM_APB_FREQ_MAX`` lock for the duration of the transaction:
|
||||
|
||||
- SPI master
|
||||
|
||||
- SDMMC
|
||||
|
||||
The following drivers will hold ``ESP_PM_APB_FREQ_MAX`` lock while the driver is enabled:
|
||||
|
||||
- SPI slave — between calls to :cpp:func:`spi_slave_initialize` and cpp:func:`spi_slave_free`.
|
||||
|
||||
- Ethernet — between calls to :cpp:func:`esp_eth_enable` and :cpp:func:`esp_eth_disable`.
|
||||
|
||||
- WiFi — between calls to :cpp:func:`esp_wifi_start` and :cpp:func:`esp_wifi_stop`. If modem sleep is enabled, lock will be released for thte periods of time when radio is disabled.
|
||||
|
||||
- Bluetooth — between calls to :cpp:func:`esp_bt_controller_enable` and :cpp:func:`esp_bt_controller_disable`.
|
||||
|
||||
The following peripheral drivers are not aware of DFS yet. Applications need to acquire/release locks when necessary:
|
||||
|
||||
- I2C
|
||||
|
||||
- I2S
|
||||
|
||||
- MCPWM
|
||||
|
||||
- PCNT
|
||||
|
||||
- Sigma-delta
|
||||
|
||||
- Timer group
|
||||
|
||||
|
||||
API Reference
|
||||
-------------
|
||||
|
||||
.. include:: /_build/inc/esp_pm.inc
|
||||
.. include:: /_build/inc/pm.inc
|
||||
|
@ -14,7 +14,7 @@ config WIFI_PASSWORD
|
||||
|
||||
choice POWER_SAVE_MODE
|
||||
prompt "power save mode"
|
||||
default POWER_SAVE_NONE
|
||||
default POWER_SAVE_MODEM
|
||||
help
|
||||
Power save mode for the esp32 to use.
|
||||
|
||||
|
@ -17,6 +17,7 @@
|
||||
#include "esp_wifi.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_event_loop.h"
|
||||
#include "esp_pm.h"
|
||||
#include "nvs_flash.h"
|
||||
|
||||
/*set the ssid and password via "make menuconfig"*/
|
||||
@ -89,5 +90,17 @@ void app_main()
|
||||
}
|
||||
ESP_ERROR_CHECK( ret );
|
||||
|
||||
#if CONFIG_PM_ENABLE
|
||||
// Configure dynamic frequency scaling: maximum frequency is set in sdkconfig,
|
||||
// minimum frequency is XTAL.
|
||||
rtc_cpu_freq_t max_freq;
|
||||
rtc_clk_cpu_freq_from_mhz(CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ, &max_freq);
|
||||
esp_pm_config_esp32_t pm_config = {
|
||||
.max_cpu_freq = max_freq,
|
||||
.min_cpu_freq = RTC_CPU_FREQ_XTAL
|
||||
};
|
||||
ESP_ERROR_CHECK( esp_pm_configure(&pm_config) );
|
||||
#endif // CONFIG_PM_ENABLE
|
||||
|
||||
wifi_power_save();
|
||||
}
|
||||
|
6
examples/wifi/power_save/sdkconfig.defaults
Normal file
6
examples/wifi/power_save/sdkconfig.defaults
Normal file
@ -0,0 +1,6 @@
|
||||
# Use lower CPU frequency
|
||||
CONFIG_ESP32_DEFAULT_CPU_FREQ_80=y
|
||||
# Enable support for power management
|
||||
CONFIG_PM_ENABLE=y
|
||||
# Use RTC timer as reference
|
||||
CONFIG_PM_USE_RTC_TIMER_REF=y
|
@ -159,6 +159,8 @@ uint64_t ref_clock_get()
|
||||
uint32_t microseconds = PCNT.cnt_unit[REF_CLOCK_PCNT_UNIT].cnt_val;
|
||||
uint32_t milliseconds = s_milliseconds;
|
||||
if (PCNT.int_st.val & BIT(REF_CLOCK_PCNT_UNIT)) {
|
||||
// refresh counter value, in case the overflow has happened after reading cnt_val
|
||||
microseconds = PCNT.cnt_unit[REF_CLOCK_PCNT_UNIT].cnt_val;
|
||||
milliseconds += REF_CLOCK_PRESCALER_MS;
|
||||
}
|
||||
portEXIT_CRITICAL(&s_lock);
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_clk.h"
|
||||
#include "soc/cpu.h"
|
||||
#include "esp_heap_caps.h"
|
||||
#include "test_utils.h"
|
||||
@ -179,7 +180,7 @@ static void unity_run_single_test_by_index_parse(const char* filter, int index_m
|
||||
unity_run_single_test_by_index(test_index - 1);
|
||||
uint32_t end;
|
||||
RSR(CCOUNT, end);
|
||||
uint32_t ms = (end - start) / (XT_CLOCK_FREQ / 1000);
|
||||
uint32_t ms = (end - start) / (esp_clk_cpu_freq() / 1000);
|
||||
printf("Test ran in %dms\n", ms);
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user