mirror of
https://github.com/espressif/esp-idf.git
synced 2025-07-30 18:57:19 +02:00
fix(mcpwm): the wrong pm lock type on esp32 and esp32s3
This commit is contained in:
@ -17,7 +17,7 @@ menu "ESP-Driver:GPTimer Configurations"
|
||||
If enabled, these functions can also be called when cache is disabled.
|
||||
|
||||
config GPTIMER_ISR_CACHE_SAFE
|
||||
bool "Allow GPTimer ISR to execute when cache is disabled"
|
||||
bool "Allow GPTimer ISR to execute when cache is disabled" if !SPI_FLASH_AUTO_SUSPEND
|
||||
select GPTIMER_ISR_HANDLER_IN_IRAM
|
||||
default n
|
||||
help
|
||||
|
@ -1,25 +1,44 @@
|
||||
menu "ESP-Driver:MCPWM Configurations"
|
||||
depends on SOC_MCPWM_SUPPORTED
|
||||
config MCPWM_ISR_IRAM_SAFE
|
||||
bool "Place MCPWM ISR function into IRAM"
|
||||
default n
|
||||
|
||||
config MCPWM_ISR_HANDLER_IN_IRAM
|
||||
bool "Place MCPWM ISR handler into IRAM to reduce latency"
|
||||
default y
|
||||
select MCPWM_OBJ_CACHE_SAFE
|
||||
help
|
||||
This will ensure the MCPWM interrupt handle is IRAM-Safe, allow to avoid flash
|
||||
cache misses, and also be able to run whilst the cache is disabled.
|
||||
(e.g. SPI Flash write)
|
||||
Place MCPWM ISR handler(s) in IRAM to reduce latency caused by cache miss.
|
||||
|
||||
config MCPWM_ISR_CACHE_SAFE
|
||||
bool "Allow MCPWM ISR to execute when cache is disabled" if !SPI_FLASH_AUTO_SUSPEND
|
||||
default n
|
||||
select MCPWM_ISR_HANDLER_IN_IRAM
|
||||
help
|
||||
Enable this option to allow the MCPWM Interrupt Service Routine (ISR)
|
||||
to execute even when the cache is disabled. This can be useful in scenarios where the cache
|
||||
might be turned off, but the MCPWM functionality is still required to operate correctly.
|
||||
|
||||
config MCPWM_CTRL_FUNC_IN_IRAM
|
||||
bool "Place MCPWM control functions into IRAM"
|
||||
bool "Place MCPWM control functions in IRAM"
|
||||
default n
|
||||
select MCPWM_OBJ_CACHE_SAFE
|
||||
help
|
||||
Place MCPWM control functions in IRAM, to reduce latency caused by cache miss.
|
||||
If enabled, these functions can also be called when cache is disabled.
|
||||
|
||||
config MCPWM_OBJ_CACHE_SAFE
|
||||
bool
|
||||
default n
|
||||
help
|
||||
Place MCPWM control functions (like set_compare_value) into IRAM,
|
||||
so that these functions can be IRAM-safe and able to be called in the other IRAM interrupt context.
|
||||
Enabling this option can improve driver performance as well.
|
||||
This will ensure the MCPWM object will not be allocated from a memory region
|
||||
where its cache can be disabled.
|
||||
|
||||
config MCPWM_ENABLE_DEBUG_LOG
|
||||
bool "Enable debug log"
|
||||
bool "Force enable debug log"
|
||||
default n
|
||||
help
|
||||
whether to enable the debug log message for MCPWM driver.
|
||||
Note that, this option only controls the MCPWM driver log, won't affect other drivers.
|
||||
If enabled, MCPWM component will:
|
||||
1. ignore the global logging settings
|
||||
2. compile all log messages into the binary
|
||||
3. set the runtime log level to VERBOSE
|
||||
Please enable this option with caution, as it will increase the binary size.
|
||||
endmenu
|
||||
|
@ -4,3 +4,9 @@ entries:
|
||||
if MCPWM_CTRL_FUNC_IN_IRAM = y:
|
||||
mcpwm_cmpr: mcpwm_comparator_set_compare_value (noflash)
|
||||
mcpwm_timer: mcpwm_timer_set_period (noflash)
|
||||
if MCPWM_ISR_HANDLER_IN_IRAM = y:
|
||||
mcpwm_cap: mcpwm_capture_default_isr (noflash)
|
||||
mcpwm_cmpr: mcpwm_comparator_default_isr (noflash)
|
||||
mcpwm_fault: mcpwm_gpio_fault_default_isr (noflash)
|
||||
mcpwm_oper: mcpwm_operator_default_isr (noflash)
|
||||
mcpwm_timer: mcpwm_timer_default_isr (noflash)
|
||||
|
4
components/esp_driver_mcpwm/sdkconfig.rename
Normal file
4
components/esp_driver_mcpwm/sdkconfig.rename
Normal file
@ -0,0 +1,4 @@
|
||||
# sdkconfig replacement configurations for deprecated options formatted as
|
||||
# CONFIG_DEPRECATED_OPTION CONFIG_NEW_OPTION
|
||||
|
||||
CONFIG_MCPWM_ISR_IRAM_SAFE CONFIG_MCPWM_ISR_CACHE_SAFE
|
@ -1,36 +1,17 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <sys/cdefs.h>
|
||||
#include "sdkconfig.h"
|
||||
#if CONFIG_MCPWM_ENABLE_DEBUG_LOG
|
||||
// The local log level must be defined before including esp_log.h
|
||||
// Set the maximum log level for this source file
|
||||
#define LOG_LOCAL_LEVEL ESP_LOG_DEBUG
|
||||
#endif
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "esp_attr.h"
|
||||
#include "esp_check.h"
|
||||
#include "mcpwm_private.h"
|
||||
#include "esp_private/esp_clk.h"
|
||||
#include "esp_clk_tree.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_memory_utils.h"
|
||||
#include "soc/soc_caps.h"
|
||||
#include "soc/mcpwm_periph.h"
|
||||
#include "hal/mcpwm_ll.h"
|
||||
#include "hal/gpio_hal.h"
|
||||
#include "driver/mcpwm_cap.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "mcpwm_private.h"
|
||||
#include "esp_private/gpio.h"
|
||||
|
||||
static const char *TAG = "mcpwm";
|
||||
#include "hal/gpio_ll.h"
|
||||
|
||||
static void mcpwm_capture_default_isr(void *args);
|
||||
|
||||
@ -85,9 +66,6 @@ static esp_err_t mcpwm_cap_timer_destroy(mcpwm_cap_timer_t *cap_timer)
|
||||
|
||||
esp_err_t mcpwm_new_capture_timer(const mcpwm_capture_timer_config_t *config, mcpwm_cap_timer_handle_t *ret_cap_timer)
|
||||
{
|
||||
#if CONFIG_MCPWM_ENABLE_DEBUG_LOG
|
||||
esp_log_level_set(TAG, ESP_LOG_DEBUG);
|
||||
#endif
|
||||
esp_err_t ret = ESP_OK;
|
||||
mcpwm_cap_timer_t *cap_timer = NULL;
|
||||
ESP_GOTO_ON_FALSE(config && ret_cap_timer, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument");
|
||||
@ -403,7 +381,7 @@ esp_err_t mcpwm_capture_channel_register_event_callbacks(mcpwm_cap_channel_handl
|
||||
int group_id = group->group_id;
|
||||
int cap_chan_id = cap_channel->cap_chan_id;
|
||||
|
||||
#if CONFIG_MCPWM_ISR_IRAM_SAFE
|
||||
#if CONFIG_MCPWM_ISR_CACHE_SAFE
|
||||
if (cbs->on_cap) {
|
||||
ESP_RETURN_ON_FALSE(esp_ptr_in_iram(cbs->on_cap), ESP_ERR_INVALID_ARG, TAG, "on_cap callback not in IRAM");
|
||||
}
|
||||
@ -488,7 +466,7 @@ esp_err_t mcpwm_capture_timer_set_phase_on_sync(mcpwm_cap_timer_handle_t cap_tim
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
IRAM_ATTR static void mcpwm_capture_default_isr(void *args)
|
||||
static void mcpwm_capture_default_isr(void *args)
|
||||
{
|
||||
mcpwm_cap_channel_t *cap_chan = (mcpwm_cap_channel_t *)args;
|
||||
mcpwm_group_t *group = cap_chan->cap_timer->group;
|
||||
|
@ -1,31 +1,12 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <sys/cdefs.h>
|
||||
#include "sdkconfig.h"
|
||||
#if CONFIG_MCPWM_ENABLE_DEBUG_LOG
|
||||
// The local log level must be defined before including esp_log.h
|
||||
// Set the maximum log level for this source file
|
||||
#define LOG_LOCAL_LEVEL ESP_LOG_DEBUG
|
||||
#endif
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "esp_attr.h"
|
||||
#include "esp_check.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_memory_utils.h"
|
||||
#include "soc/soc_caps.h"
|
||||
#include "soc/mcpwm_periph.h"
|
||||
#include "hal/mcpwm_ll.h"
|
||||
#include "driver/mcpwm_cmpr.h"
|
||||
#include "mcpwm_private.h"
|
||||
|
||||
static const char *TAG = "mcpwm";
|
||||
#include "esp_memory_utils.h"
|
||||
#include "driver/mcpwm_cmpr.h"
|
||||
|
||||
static void mcpwm_comparator_default_isr(void *args);
|
||||
|
||||
@ -239,7 +220,7 @@ esp_err_t mcpwm_comparator_register_event_callbacks(mcpwm_cmpr_handle_t cmpr, co
|
||||
int oper_id = oper->oper_id;
|
||||
int cmpr_id = cmpr->cmpr_id;
|
||||
|
||||
#if CONFIG_MCPWM_ISR_IRAM_SAFE
|
||||
#if CONFIG_MCPWM_ISR_CACHE_SAFE
|
||||
if (cbs->on_reach) {
|
||||
ESP_RETURN_ON_FALSE(esp_ptr_in_iram(cbs->on_reach), ESP_ERR_INVALID_ARG, TAG, "on_reach callback not in IRAM");
|
||||
}
|
||||
@ -269,7 +250,7 @@ esp_err_t mcpwm_comparator_register_event_callbacks(mcpwm_cmpr_handle_t cmpr, co
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static void IRAM_ATTR mcpwm_comparator_default_isr(void *args)
|
||||
static void mcpwm_comparator_default_isr(void *args)
|
||||
{
|
||||
mcpwm_oper_cmpr_t *cmpr = (mcpwm_oper_cmpr_t *)args;
|
||||
mcpwm_oper_t *oper = cmpr->base.oper;
|
||||
|
@ -1,26 +1,13 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <sys/lock.h>
|
||||
#include "sdkconfig.h"
|
||||
#if CONFIG_MCPWM_ENABLE_DEBUG_LOG
|
||||
// The local log level must be defined before including esp_log.h
|
||||
// Set the maximum log level for this source file
|
||||
#define LOG_LOCAL_LEVEL ESP_LOG_DEBUG
|
||||
#endif
|
||||
#include "esp_log.h"
|
||||
#include "esp_check.h"
|
||||
#include "mcpwm_private.h"
|
||||
#include "esp_clk_tree.h"
|
||||
#include "esp_private/esp_clk_tree_common.h"
|
||||
#include "esp_private/periph_ctrl.h"
|
||||
#include "soc/mcpwm_periph.h"
|
||||
#include "soc/soc_caps.h"
|
||||
#include "hal/mcpwm_ll.h"
|
||||
#include "mcpwm_private.h"
|
||||
#include "esp_private/rtc_clk.h"
|
||||
|
||||
#if SOC_PERIPH_CLK_CTRL_SHARED
|
||||
@ -39,8 +26,6 @@
|
||||
static esp_err_t mcpwm_create_sleep_retention_link_cb(void *arg);
|
||||
#endif
|
||||
|
||||
static const char *TAG = "mcpwm";
|
||||
|
||||
typedef struct {
|
||||
_lock_t mutex; // platform level mutex lock
|
||||
mcpwm_group_t *groups[SOC_MCPWM_GROUPS]; // array of MCPWM group instances
|
||||
@ -187,6 +172,7 @@ esp_err_t mcpwm_select_periph_clock(mcpwm_group_t *group, soc_module_clk_t clk_s
|
||||
esp_err_t ret = ESP_OK;
|
||||
bool clock_selection_conflict = false;
|
||||
bool do_clock_init = false;
|
||||
int group_id = group->group_id;
|
||||
// check if we need to update the group clock source, group clock source is shared by all mcpwm modules
|
||||
portENTER_CRITICAL(&group->spinlock);
|
||||
if (group->clk_src == 0) {
|
||||
@ -202,15 +188,20 @@ esp_err_t mcpwm_select_periph_clock(mcpwm_group_t *group, soc_module_clk_t clk_s
|
||||
if (do_clock_init) {
|
||||
|
||||
#if CONFIG_PM_ENABLE
|
||||
sprintf(group->pm_lock_name, "mcpwm_%d", group->group_id); // e.g. mcpwm_0
|
||||
ret = esp_pm_lock_create(ESP_PM_NO_LIGHT_SLEEP, 0, group->pm_lock_name, &group->pm_lock);
|
||||
// to make the mcpwm works reliable, the source clock must stay alive and unchanged
|
||||
esp_pm_lock_type_t pm_lock_type = ESP_PM_NO_LIGHT_SLEEP;
|
||||
#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S3
|
||||
// on ESP32 and ESP32S3, MCPWM's clock source (PLL_160M) frequency is automatically reduced during DFS, resulting in an inaccurate time base
|
||||
// thus we want to use the APB_MAX lock
|
||||
pm_lock_type = ESP_PM_APB_FREQ_MAX;
|
||||
#endif
|
||||
ret = esp_pm_lock_create(pm_lock_type, 0, mcpwm_periph_signals.groups[group_id].module_name, &group->pm_lock);
|
||||
ESP_RETURN_ON_ERROR(ret, TAG, "create pm lock failed");
|
||||
ESP_LOGD(TAG, "install NO_LIGHT_SLEEP lock for MCPWM group(%d)", group->group_id);
|
||||
#endif // CONFIG_PM_ENABLE
|
||||
|
||||
esp_clk_tree_enable_src((soc_module_clk_t)clk_src, true);
|
||||
MCPWM_CLOCK_SRC_ATOMIC() {
|
||||
mcpwm_ll_group_set_clock_source(group->group_id, clk_src);
|
||||
mcpwm_ll_group_set_clock_source(group_id, clk_src);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
@ -314,3 +305,11 @@ void mcpwm_create_retention_module(mcpwm_group_t *group)
|
||||
_lock_release(&s_platform.mutex);
|
||||
}
|
||||
#endif // MCPWM_USE_RETENTION_LINK
|
||||
|
||||
#if CONFIG_MCPWM_ENABLE_DEBUG_LOG
|
||||
__attribute__((constructor))
|
||||
static void mcpwm_override_default_log_level(void)
|
||||
{
|
||||
esp_log_level_set(TAG, ESP_LOG_VERBOSE);
|
||||
}
|
||||
#endif
|
||||
|
@ -1,27 +1,13 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <sys/cdefs.h>
|
||||
#include "sdkconfig.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "esp_attr.h"
|
||||
#include "esp_check.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_log.h"
|
||||
#include "soc/soc_caps.h"
|
||||
#include "soc/mcpwm_periph.h"
|
||||
#include "hal/mcpwm_ll.h"
|
||||
#include "driver/mcpwm_etm.h"
|
||||
#include "mcpwm_private.h"
|
||||
#include "driver/mcpwm_etm.h"
|
||||
#include "esp_private/etm_interface.h"
|
||||
|
||||
static const char *TAG = "mcpwm-etm";
|
||||
|
||||
typedef struct {
|
||||
esp_etm_event_t base;
|
||||
mcpwm_cmpr_handle_t cmpr;
|
||||
|
@ -1,35 +1,16 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <sys/cdefs.h>
|
||||
#include "sdkconfig.h"
|
||||
#if CONFIG_MCPWM_ENABLE_DEBUG_LOG
|
||||
// The local log level must be defined before including esp_log.h
|
||||
// Set the maximum log level for this source file
|
||||
#define LOG_LOCAL_LEVEL ESP_LOG_DEBUG
|
||||
#endif
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "esp_attr.h"
|
||||
#include "esp_check.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_log.h"
|
||||
#include "mcpwm_private.h"
|
||||
#include "esp_memory_utils.h"
|
||||
#include "soc/soc_caps.h"
|
||||
#include "soc/mcpwm_periph.h"
|
||||
#include "hal/mcpwm_ll.h"
|
||||
#include "hal/gpio_hal.h"
|
||||
#include "driver/mcpwm_fault.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "mcpwm_private.h"
|
||||
#include "esp_private/gpio.h"
|
||||
|
||||
static const char *TAG = "mcpwm";
|
||||
|
||||
static void mcpwm_gpio_fault_default_isr(void *args);
|
||||
static esp_err_t mcpwm_del_gpio_fault(mcpwm_fault_handle_t fault);
|
||||
static esp_err_t mcpwm_del_soft_fault(mcpwm_fault_handle_t fault);
|
||||
@ -87,9 +68,6 @@ static esp_err_t mcpwm_gpio_fault_destroy(mcpwm_gpio_fault_t *fault)
|
||||
|
||||
esp_err_t mcpwm_new_gpio_fault(const mcpwm_gpio_fault_config_t *config, mcpwm_fault_handle_t *ret_fault)
|
||||
{
|
||||
#if CONFIG_MCPWM_ENABLE_DEBUG_LOG
|
||||
esp_log_level_set(TAG, ESP_LOG_DEBUG);
|
||||
#endif
|
||||
esp_err_t ret = ESP_OK;
|
||||
mcpwm_gpio_fault_t *fault = NULL;
|
||||
ESP_GOTO_ON_FALSE(config && ret_fault, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument");
|
||||
@ -253,7 +231,7 @@ esp_err_t mcpwm_fault_register_event_callbacks(mcpwm_fault_handle_t fault, const
|
||||
mcpwm_hal_context_t *hal = &group->hal;
|
||||
int fault_id = gpio_fault->fault_id;
|
||||
|
||||
#if CONFIG_MCPWM_ISR_IRAM_SAFE
|
||||
#if CONFIG_MCPWM_ISR_CACHE_SAFE
|
||||
if (cbs->on_fault_enter) {
|
||||
ESP_RETURN_ON_FALSE(esp_ptr_in_iram(cbs->on_fault_enter), ESP_ERR_INVALID_ARG, TAG, "on_fault_enter callback not in IRAM");
|
||||
}
|
||||
@ -287,7 +265,7 @@ esp_err_t mcpwm_fault_register_event_callbacks(mcpwm_fault_handle_t fault, const
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static void IRAM_ATTR mcpwm_gpio_fault_default_isr(void *args)
|
||||
static void mcpwm_gpio_fault_default_isr(void *args)
|
||||
{
|
||||
mcpwm_gpio_fault_t *fault = (mcpwm_gpio_fault_t *)args;
|
||||
mcpwm_group_t *group = fault->base.group;
|
||||
|
@ -1,35 +1,16 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <sys/cdefs.h>
|
||||
#include "sdkconfig.h"
|
||||
#if CONFIG_MCPWM_ENABLE_DEBUG_LOG
|
||||
// The local log level must be defined before including esp_log.h
|
||||
// Set the maximum log level for this source file
|
||||
#define LOG_LOCAL_LEVEL ESP_LOG_DEBUG
|
||||
#endif
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "esp_attr.h"
|
||||
#include "esp_check.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_log.h"
|
||||
#include "soc/soc_caps.h"
|
||||
#include "soc/mcpwm_periph.h"
|
||||
#include "hal/mcpwm_ll.h"
|
||||
#include "mcpwm_private.h"
|
||||
#include "hal/gpio_hal.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "driver/mcpwm_gen.h"
|
||||
#include "mcpwm_private.h"
|
||||
#include "esp_private/esp_gpio_reserve.h"
|
||||
#include "esp_private/gpio.h"
|
||||
|
||||
static const char *TAG = "mcpwm";
|
||||
|
||||
static esp_err_t mcpwm_generator_register_to_operator(mcpwm_gen_t *gen, mcpwm_oper_t *oper)
|
||||
{
|
||||
int gen_id = -1;
|
||||
|
@ -1,31 +1,12 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <sys/cdefs.h>
|
||||
#include "sdkconfig.h"
|
||||
#if CONFIG_MCPWM_ENABLE_DEBUG_LOG
|
||||
// The local log level must be defined before including esp_log.h
|
||||
// Set the maximum log level for this source file
|
||||
#define LOG_LOCAL_LEVEL ESP_LOG_DEBUG
|
||||
#endif
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "esp_attr.h"
|
||||
#include "esp_check.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_memory_utils.h"
|
||||
#include "soc/soc_caps.h"
|
||||
#include "soc/mcpwm_periph.h"
|
||||
#include "hal/mcpwm_ll.h"
|
||||
#include "driver/mcpwm_oper.h"
|
||||
#include "mcpwm_private.h"
|
||||
|
||||
static const char *TAG = "mcpwm";
|
||||
#include "esp_memory_utils.h"
|
||||
#include "driver/mcpwm_oper.h"
|
||||
|
||||
static void mcpwm_operator_default_isr(void *args);
|
||||
|
||||
@ -82,9 +63,6 @@ static esp_err_t mcpwm_operator_destroy(mcpwm_oper_t *oper)
|
||||
|
||||
esp_err_t mcpwm_new_operator(const mcpwm_operator_config_t *config, mcpwm_oper_handle_t *ret_oper)
|
||||
{
|
||||
#if CONFIG_MCPWM_ENABLE_DEBUG_LOG
|
||||
esp_log_level_set(TAG, ESP_LOG_DEBUG);
|
||||
#endif
|
||||
esp_err_t ret = ESP_OK;
|
||||
mcpwm_oper_t *oper = NULL;
|
||||
ESP_GOTO_ON_FALSE(config && ret_oper, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument");
|
||||
@ -240,7 +218,7 @@ esp_err_t mcpwm_operator_register_event_callbacks(mcpwm_oper_handle_t oper, cons
|
||||
int group_id = group->group_id;
|
||||
int oper_id = oper->oper_id;
|
||||
|
||||
#if CONFIG_MCPWM_ISR_IRAM_SAFE
|
||||
#if CONFIG_MCPWM_ISR_CACHE_SAFE
|
||||
if (cbs->on_brake_cbc) {
|
||||
ESP_RETURN_ON_FALSE(esp_ptr_in_iram(cbs->on_brake_cbc), ESP_ERR_INVALID_ARG, TAG, "on_brake_cbc callback not in IRAM");
|
||||
}
|
||||
@ -348,7 +326,7 @@ esp_err_t mcpwm_operator_recover_from_fault(mcpwm_oper_handle_t oper, mcpwm_faul
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static void IRAM_ATTR mcpwm_operator_default_isr(void *args)
|
||||
static void mcpwm_operator_default_isr(void *args)
|
||||
{
|
||||
mcpwm_oper_t *oper = (mcpwm_oper_t *)args;
|
||||
mcpwm_group_t *group = oper->group;
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@ -7,14 +7,28 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <sys/cdefs.h>
|
||||
#include <sys/lock.h>
|
||||
#include "sdkconfig.h"
|
||||
#if CONFIG_MCPWM_ENABLE_DEBUG_LOG
|
||||
// The local log level must be defined before including esp_log.h
|
||||
// Set the maximum log level for this source file
|
||||
#define LOG_LOCAL_LEVEL ESP_LOG_DEBUG
|
||||
#endif
|
||||
#include "soc/soc_caps.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_check.h"
|
||||
#include "esp_intr_alloc.h"
|
||||
#include "esp_heap_caps.h"
|
||||
#include "esp_pm.h"
|
||||
#include "soc/soc_caps.h"
|
||||
#include "soc/io_mux_reg.h"
|
||||
#include "soc/mcpwm_periph.h"
|
||||
#include "hal/mcpwm_hal.h"
|
||||
#include "hal/mcpwm_ll.h"
|
||||
#include "hal/mcpwm_types.h"
|
||||
#include "driver/mcpwm_types.h"
|
||||
#include "esp_private/sleep_retention.h"
|
||||
@ -23,13 +37,13 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#if CONFIG_MCPWM_ISR_IRAM_SAFE || CONFIG_MCPWM_CTRL_FUNC_IN_IRAM
|
||||
#if CONFIG_MCPWM_OBJ_CACHE_SAFE
|
||||
#define MCPWM_MEM_ALLOC_CAPS (MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT)
|
||||
#else
|
||||
#define MCPWM_MEM_ALLOC_CAPS MALLOC_CAP_DEFAULT
|
||||
#endif
|
||||
|
||||
#if CONFIG_MCPWM_ISR_IRAM_SAFE
|
||||
#if CONFIG_MCPWM_ISR_CACHE_SAFE
|
||||
#define MCPWM_INTR_ALLOC_FLAG (ESP_INTR_FLAG_SHARED | ESP_INTR_FLAG_INTRDISABLED | ESP_INTR_FLAG_IRAM)
|
||||
#else
|
||||
#define MCPWM_INTR_ALLOC_FLAG (ESP_INTR_FLAG_SHARED | ESP_INTR_FLAG_INTRDISABLED)
|
||||
@ -41,7 +55,9 @@ extern "C" {
|
||||
#define MCPWM_ALLOW_INTR_PRIORITY_MASK ESP_INTR_FLAG_LOWMED
|
||||
|
||||
#define MCPWM_GROUP_CLOCK_DEFAULT_PRESCALE 2
|
||||
#define MCPWM_PM_LOCK_NAME_LEN_MAX 16
|
||||
|
||||
///!< Logging settings
|
||||
#define TAG "mcpwm"
|
||||
|
||||
typedef struct mcpwm_group_t mcpwm_group_t;
|
||||
typedef struct mcpwm_timer_t mcpwm_timer_t;
|
||||
@ -74,9 +90,6 @@ struct mcpwm_group_t {
|
||||
mcpwm_oper_t *operators[SOC_MCPWM_OPERATORS_PER_GROUP]; // mcpwm operator array
|
||||
mcpwm_gpio_fault_t *gpio_faults[SOC_MCPWM_GPIO_FAULTS_PER_GROUP]; // mcpwm fault detectors array
|
||||
mcpwm_gpio_sync_src_t *gpio_sync_srcs[SOC_MCPWM_GPIO_SYNCHROS_PER_GROUP]; // mcpwm gpio sync array
|
||||
#if CONFIG_PM_ENABLE
|
||||
char pm_lock_name[MCPWM_PM_LOCK_NAME_LEN_MAX]; // pm lock name
|
||||
#endif
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
|
@ -4,32 +4,12 @@
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <sys/cdefs.h>
|
||||
#include "sdkconfig.h"
|
||||
#if CONFIG_MCPWM_ENABLE_DEBUG_LOG
|
||||
// The local log level must be defined before including esp_log.h
|
||||
// Set the maximum log level for this source file
|
||||
#define LOG_LOCAL_LEVEL ESP_LOG_DEBUG
|
||||
#endif
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "esp_attr.h"
|
||||
#include "esp_check.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_memory_utils.h"
|
||||
#include "soc/soc_caps.h"
|
||||
#include "soc/mcpwm_periph.h"
|
||||
#include "hal/mcpwm_ll.h"
|
||||
#include "mcpwm_private.h"
|
||||
#include "hal/gpio_hal.h"
|
||||
#include "driver/mcpwm_sync.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "mcpwm_private.h"
|
||||
#include "esp_private/gpio.h"
|
||||
|
||||
static const char *TAG = "mcpwm";
|
||||
|
||||
static esp_err_t mcpwm_del_timer_sync_src(mcpwm_sync_t *sync_src);
|
||||
static esp_err_t mcpwm_del_gpio_sync_src(mcpwm_sync_t *sync_src);
|
||||
static esp_err_t mcpwm_del_soft_sync_src(mcpwm_sync_t *sync_src);
|
||||
@ -176,9 +156,6 @@ static esp_err_t mcpwm_gpio_sync_src_destroy(mcpwm_gpio_sync_src_t *gpio_sync_sr
|
||||
|
||||
esp_err_t mcpwm_new_gpio_sync_src(const mcpwm_gpio_sync_src_config_t *config, mcpwm_sync_handle_t *ret_sync)
|
||||
{
|
||||
#if CONFIG_MCPWM_ENABLE_DEBUG_LOG
|
||||
esp_log_level_set(TAG, ESP_LOG_DEBUG);
|
||||
#endif
|
||||
esp_err_t ret = ESP_OK;
|
||||
mcpwm_gpio_sync_src_t *gpio_sync_src = NULL;
|
||||
ESP_GOTO_ON_FALSE(config && ret_sync, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument");
|
||||
|
@ -1,32 +1,14 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <sys/cdefs.h>
|
||||
#include "sdkconfig.h"
|
||||
#if CONFIG_MCPWM_ENABLE_DEBUG_LOG
|
||||
// The local log level must be defined before including esp_log.h
|
||||
// Set the maximum log level for this source file
|
||||
#define LOG_LOCAL_LEVEL ESP_LOG_DEBUG
|
||||
#endif
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "esp_attr.h"
|
||||
#include "esp_check.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_log.h"
|
||||
#include "mcpwm_private.h"
|
||||
#include "esp_memory_utils.h"
|
||||
#include "soc/soc_caps.h"
|
||||
#include "soc/mcpwm_periph.h"
|
||||
#include "hal/mcpwm_ll.h"
|
||||
#include "driver/mcpwm_timer.h"
|
||||
#include "esp_private/mcpwm.h"
|
||||
#include "mcpwm_private.h"
|
||||
|
||||
static const char *TAG = "mcpwm";
|
||||
|
||||
static void mcpwm_timer_default_isr(void *args);
|
||||
|
||||
@ -83,9 +65,6 @@ static esp_err_t mcpwm_timer_destroy(mcpwm_timer_t *timer)
|
||||
|
||||
esp_err_t mcpwm_new_timer(const mcpwm_timer_config_t *config, mcpwm_timer_handle_t *ret_timer)
|
||||
{
|
||||
#if CONFIG_MCPWM_ENABLE_DEBUG_LOG
|
||||
esp_log_level_set(TAG, ESP_LOG_DEBUG);
|
||||
#endif
|
||||
esp_err_t ret = ESP_OK;
|
||||
mcpwm_timer_t *timer = NULL;
|
||||
ESP_GOTO_ON_FALSE(config && ret_timer, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument");
|
||||
@ -211,7 +190,7 @@ esp_err_t mcpwm_timer_register_event_callbacks(mcpwm_timer_handle_t timer, const
|
||||
int timer_id = timer->timer_id;
|
||||
mcpwm_hal_context_t *hal = &group->hal;
|
||||
|
||||
#if CONFIG_MCPWM_ISR_IRAM_SAFE
|
||||
#if CONFIG_MCPWM_ISR_CACHE_SAFE
|
||||
if (cbs->on_empty) {
|
||||
ESP_RETURN_ON_FALSE(esp_ptr_in_iram(cbs->on_empty), ESP_ERR_INVALID_ARG, TAG, "on_empty callback not in IRAM");
|
||||
}
|
||||
@ -369,7 +348,7 @@ esp_err_t mcpwm_timer_set_phase_on_sync(mcpwm_timer_handle_t timer, const mcpwm_
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static void IRAM_ATTR mcpwm_timer_default_isr(void *args)
|
||||
static void mcpwm_timer_default_isr(void *args)
|
||||
{
|
||||
mcpwm_timer_t *timer = (mcpwm_timer_t *)args;
|
||||
mcpwm_group_t *group = timer->group;
|
||||
|
@ -9,7 +9,7 @@ set(srcs "test_app_main.c"
|
||||
"test_mcpwm_common.c"
|
||||
"test_mcpwm_utils.c")
|
||||
|
||||
if(CONFIG_MCPWM_ISR_IRAM_SAFE)
|
||||
if(CONFIG_MCPWM_ISR_CACHE_SAFE)
|
||||
list(APPEND srcs "test_mcpwm_iram.c")
|
||||
endif()
|
||||
|
||||
|
@ -34,11 +34,11 @@ extern "C" {
|
||||
#define TEST_SYNC_GPIO 13
|
||||
#endif
|
||||
|
||||
#if CONFIG_MCPWM_ISR_IRAM_SAFE
|
||||
#if CONFIG_MCPWM_ISR_CACHE_SAFE
|
||||
#define TEST_MCPWM_CALLBACK_ATTR IRAM_ATTR
|
||||
#else
|
||||
#define TEST_MCPWM_CALLBACK_ATTR
|
||||
#endif // CONFIG_MCPWM_ISR_IRAM_SAFE
|
||||
#endif // CONFIG_MCPWM_ISR_CACHE_SAFE
|
||||
|
||||
void check_mcpwm_timer_phase(mcpwm_timer_handle_t *timers, size_t num_timers,
|
||||
uint32_t expected_count, mcpwm_timer_direction_t expected_direction);
|
||||
|
@ -10,7 +10,7 @@ from pytest_embedded_idf.utils import idf_parametrize
|
||||
'config',
|
||||
[
|
||||
'release',
|
||||
'iram_safe',
|
||||
'cache_safe',
|
||||
],
|
||||
indirect=True,
|
||||
)
|
||||
|
@ -1,5 +1,5 @@
|
||||
CONFIG_COMPILER_DUMP_RTL_FILES=y
|
||||
CONFIG_MCPWM_ISR_IRAM_SAFE=y
|
||||
CONFIG_MCPWM_ISR_CACHE_SAFE=y
|
||||
CONFIG_MCPWM_CTRL_FUNC_IN_IRAM=y
|
||||
CONFIG_GPIO_CTRL_FUNC_IN_IRAM=y
|
||||
CONFIG_COMPILER_OPTIMIZATION_NONE=y
|
@ -1,6 +1,7 @@
|
||||
CONFIG_PM_ENABLE=y
|
||||
CONFIG_FREERTOS_USE_TICKLESS_IDLE=y
|
||||
CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP=y
|
||||
CONFIG_PM_DFS_INIT_AUTO=y
|
||||
CONFIG_COMPILER_OPTIMIZATION_SIZE=y
|
||||
CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_SIZE=y
|
||||
CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT=y
|
||||
|
@ -1,5 +1,6 @@
|
||||
CONFIG_PM_ENABLE=y
|
||||
CONFIG_FREERTOS_USE_TICKLESS_IDLE=y
|
||||
CONFIG_PM_DFS_INIT_AUTO=y
|
||||
CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP=y
|
||||
CONFIG_COMPILER_OPTIMIZATION_SIZE=y
|
||||
CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_SIZE=y
|
||||
|
@ -11,7 +11,7 @@
|
||||
const mcpwm_signal_conn_t mcpwm_periph_signals = {
|
||||
.groups = {
|
||||
[0] = {
|
||||
.module = PERIPH_PWM0_MODULE,
|
||||
.module_name = "MCPWM0",
|
||||
.irq_id = ETS_PWM0_INTR_SOURCE,
|
||||
.operators = {
|
||||
[0] = {
|
||||
@ -80,7 +80,7 @@ const mcpwm_signal_conn_t mcpwm_periph_signals = {
|
||||
}
|
||||
},
|
||||
[1] = {
|
||||
.module = PERIPH_PWM1_MODULE,
|
||||
.module_name = "MCPWM1",
|
||||
.irq_id = ETS_PWM1_INTR_SOURCE,
|
||||
.operators = {
|
||||
[0] = {
|
||||
|
@ -12,7 +12,7 @@
|
||||
const mcpwm_signal_conn_t mcpwm_periph_signals = {
|
||||
.groups = {
|
||||
[0] = {
|
||||
.module = PERIPH_MCPWM0_MODULE,
|
||||
.module_name = "MCPWM0",
|
||||
.irq_id = ETS_MCPWM0_INTR_SOURCE,
|
||||
.operators = {
|
||||
[0] = {
|
||||
|
@ -12,7 +12,7 @@
|
||||
const mcpwm_signal_conn_t mcpwm_periph_signals = {
|
||||
.groups = {
|
||||
[0] = {
|
||||
.module = PERIPH_MCPWM0_MODULE,
|
||||
.module_name = "MCPWM0",
|
||||
.irq_id = ETS_MCPWM0_INTR_SOURCE,
|
||||
.operators = {
|
||||
[0] = {
|
||||
|
@ -12,7 +12,7 @@
|
||||
const mcpwm_signal_conn_t mcpwm_periph_signals = {
|
||||
.groups = {
|
||||
[0] = {
|
||||
.module = PERIPH_MCPWM0_MODULE,
|
||||
.module_name = "MCPWM0",
|
||||
.irq_id = ETS_MCPWM0_INTR_SOURCE,
|
||||
.operators = {
|
||||
[0] = {
|
||||
|
@ -12,7 +12,7 @@
|
||||
const mcpwm_signal_conn_t mcpwm_periph_signals = {
|
||||
.groups = {
|
||||
[0] = {
|
||||
.module = PERIPH_MCPWM0_MODULE,
|
||||
.module_name = "MCPWM0",
|
||||
.irq_id = ETS_PWM0_INTR_SOURCE,
|
||||
.operators = {
|
||||
[0] = {
|
||||
@ -81,7 +81,7 @@ const mcpwm_signal_conn_t mcpwm_periph_signals = {
|
||||
}
|
||||
},
|
||||
[1] = {
|
||||
.module = PERIPH_MCPWM1_MODULE,
|
||||
.module_name = "MCPWM1",
|
||||
.irq_id = ETS_PWM1_INTR_SOURCE,
|
||||
.operators = {
|
||||
[0] = {
|
||||
|
@ -11,7 +11,7 @@
|
||||
const mcpwm_signal_conn_t mcpwm_periph_signals = {
|
||||
.groups = {
|
||||
[0] = {
|
||||
.module = PERIPH_PWM0_MODULE,
|
||||
.module_name = "MCPWM0",
|
||||
.irq_id = ETS_PWM0_INTR_SOURCE,
|
||||
.operators = {
|
||||
[0] = {
|
||||
@ -80,7 +80,7 @@ const mcpwm_signal_conn_t mcpwm_periph_signals = {
|
||||
}
|
||||
},
|
||||
[1] = {
|
||||
.module = PERIPH_PWM1_MODULE,
|
||||
.module_name = "MCPWM1",
|
||||
.irq_id = ETS_PWM1_INTR_SOURCE,
|
||||
.operators = {
|
||||
[0] = {
|
||||
|
@ -21,7 +21,7 @@ extern "C" {
|
||||
#if SOC_MCPWM_SUPPORTED
|
||||
typedef struct {
|
||||
struct {
|
||||
const periph_module_t module; // Peripheral module
|
||||
const char *module_name;
|
||||
const int irq_id;
|
||||
struct {
|
||||
struct {
|
||||
|
@ -989,7 +989,7 @@ IRAM Safe
|
||||
|
||||
By default, the MCPWM interrupt will be deferred when the Cache is disabled for reasons like writing/erasing Flash. Thus the event callback functions will not get executed in time, which is not expected in a real-time application.
|
||||
|
||||
There is a Kconfig option :ref:`CONFIG_MCPWM_ISR_IRAM_SAFE` that:
|
||||
There is a Kconfig option :ref:`CONFIG_MCPWM_ISR_CACHE_SAFE` that:
|
||||
|
||||
* enables the interrupt to be serviced even when the cache is disabled
|
||||
* places all functions used by the ISR into IRAM [2]_
|
||||
@ -1023,7 +1023,7 @@ Other functions that are not related to `Resource Allocation and Initialization
|
||||
Kconfig Options
|
||||
^^^^^^^^^^^^^^^
|
||||
|
||||
- :ref:`CONFIG_MCPWM_ISR_IRAM_SAFE` controls whether the default ISR handler can work when the cache is disabled, see :ref:`mcpwm-iram-safe` for more information.
|
||||
- :ref:`CONFIG_MCPWM_ISR_CACHE_SAFE` controls whether the default ISR handler can work when the cache is disabled, see :ref:`mcpwm-iram-safe` for more information.
|
||||
- :ref:`CONFIG_MCPWM_CTRL_FUNC_IN_IRAM` controls where to place the MCPWM control functions (IRAM or flash), see :ref:`mcpwm-iram-safe` for more information.
|
||||
- :ref:`CONFIG_MCPWM_ENABLE_DEBUG_LOG` is used to enable the debug log output. Enabling this option will increase the firmware binary size.
|
||||
|
||||
|
@ -989,7 +989,7 @@ IRAM 安全
|
||||
|
||||
默认情况下,禁用 cache 时,写入/擦除 flash 等原因将导致 MCPWM 中断延迟,事件回调函数也将延迟执行。在实时应用程序中,应避免此类情况。
|
||||
|
||||
因此,可以启用 Kconfig 选项 :ref:`CONFIG_MCPWM_ISR_IRAM_SAFE`,该选项:
|
||||
因此,可以启用 Kconfig 选项 :ref:`CONFIG_MCPWM_ISR_CACHE_SAFE`,该选项:
|
||||
|
||||
* 支持在禁用 cache 时启用所需中断
|
||||
* 支持将 ISR 使用的所有函数存放在 IRAM 中 [2]_
|
||||
@ -1023,7 +1023,7 @@ IRAM 安全
|
||||
Kconfig 选项
|
||||
^^^^^^^^^^^^^^^
|
||||
|
||||
- :ref:`CONFIG_MCPWM_ISR_IRAM_SAFE` 控制默认 ISR 处理程序能否在禁用 cache 的情况下工作。更多信息请参见 :ref:`mcpwm-iram-safe`。
|
||||
- :ref:`CONFIG_MCPWM_ISR_CACHE_SAFE` 控制默认 ISR 处理程序能否在禁用 cache 的情况下工作。更多信息请参见 :ref:`mcpwm-iram-safe`。
|
||||
- :ref:`CONFIG_MCPWM_CTRL_FUNC_IN_IRAM` 控制 MCPWM 控制函数的存放位置(IRAM 或 flash)。更多信息请参见 :ref:`mcpwm-iram-safe`。
|
||||
- :ref:`CONFIG_MCPWM_ENABLE_DEBUG_LOG` 用于启用调试日志输出。启用此选项将增加固件的二进制文件大小。
|
||||
|
||||
|
Reference in New Issue
Block a user