2023-09-04 05:20:36 -04:00
/*
2024-04-12 04:50:26 -04:00
* SPDX - FileCopyrightText : 2022 - 2024 Espressif Systems ( Shanghai ) CO LTD
2023-09-04 05:20:36 -04:00
*
* SPDX - License - Identifier : Apache - 2.0
*/
2023-10-19 10:21:53 -04:00
# include <sys/queue.h>
2023-09-04 05:20:36 -04:00
# include "sdkconfig.h"
2023-10-26 06:01:30 -04:00
# include "freertos/FreeRTOS.h"
2023-09-04 05:20:36 -04:00
# include "driver/gpio.h"
# include "esp_clock_output.h"
# include "esp_check.h"
# include "esp_rom_gpio.h"
2024-04-15 02:12:47 -04:00
# include "soc/clkout_channel.h"
2023-09-04 05:20:36 -04:00
# include "hal/gpio_hal.h"
2024-04-15 02:12:47 -04:00
# include "hal/clk_tree_hal.h"
2023-12-14 05:12:09 -05:00
# include "hal/clk_tree_ll.h"
2023-09-04 05:20:36 -04:00
# include "soc/soc_caps.h"
# include "soc/io_mux_reg.h"
2023-10-19 10:21:53 -04:00
typedef struct clkout_channel_handle {
2023-09-04 05:20:36 -04:00
bool is_mapped ;
2023-10-19 10:21:53 -04:00
soc_clkout_sig_id_t mapped_clock ;
2024-01-22 08:48:12 -05:00
clock_out_channel_t channel_id ;
2023-09-04 05:20:36 -04:00
uint8_t ref_cnt ;
2023-10-19 10:21:53 -04:00
uint64_t mapped_io_bmap ;
portMUX_TYPE clkout_channel_lock ;
} clkout_channel_handle_t ;
typedef struct esp_clock_output_mapping {
2023-09-04 05:20:36 -04:00
gpio_num_t mapped_io ;
2023-10-19 10:21:53 -04:00
clkout_channel_handle_t * clkout_channel_hdl ;
uint8_t ref_cnt ;
portMUX_TYPE clkout_mapping_lock ;
SLIST_ENTRY ( esp_clock_output_mapping ) next ;
} esp_clock_output_mapping_t ;
2023-09-04 05:20:36 -04:00
2023-10-19 10:21:53 -04:00
static const char * TAG = " esp_clock_output " ;
2023-09-04 05:20:36 -04:00
2023-10-19 10:21:53 -04:00
static SLIST_HEAD ( esp_clock_output_mapping_head , esp_clock_output_mapping ) s_mapping_list = SLIST_HEAD_INITIALIZER ( s_mapping_list_head ) ;
static portMUX_TYPE s_mapping_list_lock = portMUX_INITIALIZER_UNLOCKED ;
static portMUX_TYPE s_clkout_lock = portMUX_INITIALIZER_UNLOCKED ;
2023-09-04 05:20:36 -04:00
2023-10-19 10:21:53 -04:00
static clkout_channel_handle_t s_clkout_handle [ CLKOUT_CHANNEL_MAX ] = {
2023-09-04 05:20:36 -04:00
[ 0 . . . CLKOUT_CHANNEL_MAX - 1 ] = {
. is_mapped = false ,
. ref_cnt = 0 ,
2023-10-19 10:21:53 -04:00
. mapped_io_bmap = 0 ,
. clkout_channel_lock = portMUX_INITIALIZER_UNLOCKED ,
2023-09-04 05:20:36 -04:00
}
} ;
2023-10-19 10:21:53 -04:00
static clkout_channel_handle_t * clkout_channel_alloc ( soc_clkout_sig_id_t clk_sig , gpio_num_t gpio_num )
2023-09-04 05:20:36 -04:00
{
2023-10-19 10:21:53 -04:00
clkout_channel_handle_t * allocated_channel = NULL ;
2023-09-04 05:20:36 -04:00
# if SOC_GPIO_CLOCKOUT_BY_IO_MUX
2023-10-19 10:21:53 -04:00
portENTER_CRITICAL ( & s_clkout_handle [ IONUM_TO_CLKOUT_CHANNEL ( gpio_num ) ] . clkout_channel_lock ) ;
if ( ! s_clkout_handle [ IONUM_TO_CLKOUT_CHANNEL ( gpio_num ) ] . is_mapped ) {
s_clkout_handle [ IONUM_TO_CLKOUT_CHANNEL ( gpio_num ) ] . is_mapped = true ;
allocated_channel = & s_clkout_handle [ IONUM_TO_CLKOUT_CHANNEL ( gpio_num ) ] ;
} else if ( ( s_clkout_handle [ IONUM_TO_CLKOUT_CHANNEL ( gpio_num ) ] . mapped_io_bmap & BIT ( gpio_num ) ) & &
2023-10-26 06:01:30 -04:00
( s_clkout_handle [ IONUM_TO_CLKOUT_CHANNEL ( gpio_num ) ] . mapped_clock = = clk_sig ) ) {
2023-10-19 10:21:53 -04:00
allocated_channel = & s_clkout_handle [ IONUM_TO_CLKOUT_CHANNEL ( gpio_num ) ] ;
2023-09-04 05:20:36 -04:00
}
2023-10-19 10:21:53 -04:00
allocated_channel - > channel_id = ( clock_out_channel_t ) IONUM_TO_CLKOUT_CHANNEL ( gpio_num ) ;
portEXIT_CRITICAL ( & s_clkout_handle [ IONUM_TO_CLKOUT_CHANNEL ( gpio_num ) ] . clkout_channel_lock ) ;
2023-09-04 05:20:36 -04:00
# elif SOC_GPIO_CLOCKOUT_BY_GPIO_MATRIX
2023-10-26 06:01:30 -04:00
for ( uint32_t channel = 0 ; channel < CLKOUT_CHANNEL_MAX ; channel + + ) {
2023-10-19 10:21:53 -04:00
portENTER_CRITICAL ( & s_clkout_handle [ channel ] . clkout_channel_lock ) ;
if ( ! s_clkout_handle [ channel ] . is_mapped ) {
s_clkout_handle [ channel ] . is_mapped = true ;
allocated_channel = & s_clkout_handle [ channel ] ;
allocated_channel - > channel_id = ( clock_out_channel_t ) channel ;
portEXIT_CRITICAL ( & s_clkout_handle [ channel ] . clkout_channel_lock ) ;
2023-09-04 05:20:36 -04:00
break ;
2023-10-19 10:21:53 -04:00
} else if ( s_clkout_handle [ channel ] . mapped_clock = = clk_sig ) {
allocated_channel = & s_clkout_handle [ channel ] ;
portEXIT_CRITICAL ( & s_clkout_handle [ channel ] . clkout_channel_lock ) ;
2023-09-04 05:20:36 -04:00
break ;
}
2023-10-19 10:21:53 -04:00
portEXIT_CRITICAL ( & s_clkout_handle [ channel ] . clkout_channel_lock ) ;
2023-09-04 05:20:36 -04:00
}
# endif
2023-10-19 10:21:53 -04:00
if ( allocated_channel ! = NULL ) {
portENTER_CRITICAL ( & allocated_channel - > clkout_channel_lock ) ;
allocated_channel - > mapped_io_bmap | = BIT ( gpio_num ) ;
allocated_channel - > mapped_clock = clk_sig ;
allocated_channel - > ref_cnt + + ;
if ( allocated_channel - > ref_cnt = = 1 ) {
portENTER_CRITICAL ( & s_clkout_lock ) ;
2023-12-14 05:12:09 -05:00
# if SOC_CLOCKOUT_HAS_SOURCE_GATE
clk_ll_enable_clkout_source ( clk_sig , true ) ;
# endif
2024-04-15 02:12:47 -04:00
clk_hal_clock_output_setup ( clk_sig , allocated_channel - > channel_id ) ;
2023-10-19 10:21:53 -04:00
portEXIT_CRITICAL ( & s_clkout_lock ) ;
}
portEXIT_CRITICAL ( & allocated_channel - > clkout_channel_lock ) ;
2023-09-04 05:20:36 -04:00
}
2023-10-19 10:21:53 -04:00
return allocated_channel ;
2023-09-04 05:20:36 -04:00
}
2023-10-19 10:21:53 -04:00
static esp_clock_output_mapping_t * clkout_mapping_alloc ( clkout_channel_handle_t * channel_hdl , gpio_num_t gpio_num )
2023-09-04 05:20:36 -04:00
{
2023-10-19 10:21:53 -04:00
esp_clock_output_mapping_t * allocated_mapping = NULL ;
portENTER_CRITICAL ( & s_mapping_list_lock ) ;
esp_clock_output_mapping_t * hdl ;
SLIST_FOREACH ( hdl , & s_mapping_list , next ) {
if ( ( hdl - > clkout_channel_hdl = = channel_hdl ) & & ( hdl - > mapped_io = = gpio_num ) ) {
allocated_mapping = hdl ;
}
}
portEXIT_CRITICAL ( & s_mapping_list_lock ) ;
if ( allocated_mapping = = NULL ) {
allocated_mapping = ( esp_clock_output_mapping_t * ) malloc ( sizeof ( esp_clock_output_mapping_t ) ) ;
allocated_mapping - > mapped_io = gpio_num ;
allocated_mapping - > clkout_channel_hdl = channel_hdl ;
allocated_mapping - > ref_cnt = 0 ;
portMUX_INITIALIZE ( & allocated_mapping - > clkout_mapping_lock ) ;
portENTER_CRITICAL ( & s_mapping_list_lock ) ;
SLIST_INSERT_HEAD ( & s_mapping_list , allocated_mapping , next ) ;
portEXIT_CRITICAL ( & s_mapping_list_lock ) ;
}
portENTER_CRITICAL ( & allocated_mapping - > clkout_mapping_lock ) ;
allocated_mapping - > ref_cnt + + ;
if ( allocated_mapping - > ref_cnt = = 1 ) {
# if SOC_GPIO_CLOCKOUT_BY_IO_MUX
gpio_hal_iomux_func_sel ( GPIO_PIN_MUX_REG [ gpio_num ] , CLKOUT_CHANNEL_TO_IOMUX_FUNC ( allocated_mapping - > clkout_channel_hdl - > channel_id ) ) ;
# elif SOC_GPIO_CLOCKOUT_BY_GPIO_MATRIX
gpio_set_pull_mode ( gpio_num , GPIO_FLOATING ) ;
gpio_hal_iomux_func_sel ( GPIO_PIN_MUX_REG [ gpio_num ] , PIN_FUNC_GPIO ) ;
gpio_set_direction ( gpio_num , GPIO_MODE_OUTPUT ) ;
esp_rom_gpio_connect_out_signal ( gpio_num , CLKOUT_CHANNEL_TO_GPIO_SIG_ID ( allocated_mapping - > clkout_channel_hdl - > channel_id ) , false , false ) ;
# endif
2023-09-04 05:20:36 -04:00
}
2023-10-19 10:21:53 -04:00
portEXIT_CRITICAL ( & allocated_mapping - > clkout_mapping_lock ) ;
return allocated_mapping ;
2023-09-04 05:20:36 -04:00
}
2023-10-19 10:21:53 -04:00
static void clkout_channel_free ( clkout_channel_handle_t * channel_hdl )
2023-09-04 05:20:36 -04:00
{
2023-10-19 10:21:53 -04:00
portENTER_CRITICAL ( & channel_hdl - > clkout_channel_lock ) ;
if ( - - channel_hdl - > ref_cnt = = 0 ) {
portENTER_CRITICAL ( & s_clkout_lock ) ;
2023-12-14 05:12:09 -05:00
# if SOC_CLOCKOUT_HAS_SOURCE_GATE
clk_ll_enable_clkout_source ( channel_hdl - > mapped_clock , false ) ;
# endif
2024-04-15 02:12:47 -04:00
clk_hal_clock_output_teardown ( channel_hdl - > channel_id ) ;
2023-10-19 10:21:53 -04:00
portEXIT_CRITICAL ( & s_clkout_lock ) ;
2023-12-14 05:12:09 -05:00
channel_hdl - > mapped_clock = CLKOUT_SIG_INVALID ;
2023-10-19 10:21:53 -04:00
channel_hdl - > is_mapped = false ;
}
portEXIT_CRITICAL ( & channel_hdl - > clkout_channel_lock ) ;
}
2023-09-04 05:20:36 -04:00
2023-10-19 10:21:53 -04:00
static void clkout_mapping_free ( esp_clock_output_mapping_t * mapping_hdl )
{
portENTER_CRITICAL ( & mapping_hdl - > clkout_mapping_lock ) ;
clkout_channel_free ( mapping_hdl - > clkout_channel_hdl ) ;
bool do_free_mapping_hdl = false ;
if ( - - mapping_hdl - > ref_cnt = = 0 ) {
gpio_hal_iomux_func_sel ( GPIO_PIN_MUX_REG [ mapping_hdl - > mapped_io ] , PIN_FUNC_GPIO ) ;
esp_rom_gpio_connect_out_signal ( mapping_hdl - > mapped_io , SIG_GPIO_OUT_IDX , false , false ) ;
gpio_set_direction ( mapping_hdl - > mapped_io , GPIO_MODE_DISABLE ) ;
portENTER_CRITICAL ( & mapping_hdl - > clkout_channel_hdl - > clkout_channel_lock ) ;
mapping_hdl - > clkout_channel_hdl - > mapped_io_bmap & = ~ BIT ( mapping_hdl - > mapped_io ) ;
portEXIT_CRITICAL ( & mapping_hdl - > clkout_channel_hdl - > clkout_channel_lock ) ;
portENTER_CRITICAL ( & s_mapping_list_lock ) ;
SLIST_REMOVE ( & s_mapping_list , mapping_hdl , esp_clock_output_mapping , next ) ;
portEXIT_CRITICAL ( & s_mapping_list_lock ) ;
do_free_mapping_hdl = true ;
}
portEXIT_CRITICAL ( & mapping_hdl - > clkout_mapping_lock ) ;
2023-09-04 05:20:36 -04:00
2023-10-19 10:21:53 -04:00
if ( do_free_mapping_hdl ) {
free ( mapping_hdl ) ;
}
}
2023-09-04 05:20:36 -04:00
2023-10-19 10:21:53 -04:00
esp_err_t esp_clock_output_start ( soc_clkout_sig_id_t clk_sig , gpio_num_t gpio_num , esp_clock_output_mapping_handle_t * clkout_mapping_ret_hdl )
{
ESP_RETURN_ON_FALSE ( ( clkout_mapping_ret_hdl ! = NULL ) , ESP_ERR_INVALID_ARG , TAG , " Clock out mapping handle passed in is invalid " ) ;
2024-04-15 02:12:47 -04:00
# if SOC_GPIO_CLOCKOUT_BY_GPIO_MATRIX
ESP_RETURN_ON_FALSE ( GPIO_IS_VALID_GPIO ( gpio_num ) , ESP_ERR_INVALID_ARG , TAG , " %s " , " Output GPIO number error " ) ;
# else
2023-10-19 10:21:53 -04:00
ESP_RETURN_ON_FALSE ( IS_VALID_CLKOUT_IO ( gpio_num ) , ESP_ERR_INVALID_ARG , TAG , " %s " , " Output GPIO number error " ) ;
2024-04-15 02:12:47 -04:00
# endif
2023-09-04 05:20:36 -04:00
2023-10-19 10:21:53 -04:00
esp_clock_output_mapping_t * hdl ;
SLIST_FOREACH ( hdl , & s_mapping_list , next ) {
ESP_RETURN_ON_FALSE ( ! ( ( hdl - > mapped_io = = gpio_num ) & & ( hdl - > clkout_channel_hdl - > mapped_clock ! = clk_sig ) ) , ESP_ERR_INVALID_ARG , TAG , " Selected io is already mapped by another signal " ) ;
}
2023-09-04 05:20:36 -04:00
2023-10-19 10:21:53 -04:00
clkout_channel_handle_t * channel_hdl = clkout_channel_alloc ( clk_sig , gpio_num ) ;
2023-09-04 05:20:36 -04:00
# if SOC_GPIO_CLOCKOUT_BY_IO_MUX
2023-10-19 10:21:53 -04:00
ESP_RETURN_ON_FALSE ( ( channel_hdl ! = NULL ) , ESP_ERR_INVALID_ARG , TAG , " Selected clock out IO is already mapped to other internal clock source " ) ;
2023-09-04 05:20:36 -04:00
# elif SOC_GPIO_CLOCKOUT_BY_GPIO_MATRIX
2023-10-19 10:21:53 -04:00
ESP_RETURN_ON_FALSE ( ( channel_hdl ! = NULL ) , ESP_FAIL , TAG , " Maximum support for %d output clock signals, no available clock_out channel for assignment " , CLKOUT_CHANNEL_MAX ) ;
2023-09-04 05:20:36 -04:00
# endif
2023-10-19 10:21:53 -04:00
* clkout_mapping_ret_hdl = clkout_mapping_alloc ( channel_hdl , gpio_num ) ;
2023-09-04 05:20:36 -04:00
return ESP_OK ;
}
2023-10-19 10:21:53 -04:00
esp_err_t esp_clock_output_stop ( esp_clock_output_mapping_handle_t clkout_mapping_hdl )
2023-09-04 05:20:36 -04:00
{
2023-10-19 10:21:53 -04:00
ESP_RETURN_ON_FALSE ( ( clkout_mapping_hdl ! = NULL ) , ESP_ERR_INVALID_ARG , TAG , " Clock out mapping handle passed in is invalid " ) ;
ESP_RETURN_ON_FALSE ( clkout_mapping_hdl - > ref_cnt > 0 , ESP_ERR_INVALID_STATE , TAG , " %s " , " Clock outputting is already disabled " ) ;
clkout_mapping_free ( clkout_mapping_hdl ) ;
2023-09-04 05:20:36 -04:00
return ESP_OK ;
}
2024-04-12 04:50:26 -04:00
# if SOC_CLOCKOUT_SUPPORT_CHANNEL_DIVIDER
esp_err_t esp_clock_output_set_divider ( esp_clock_output_mapping_handle_t clkout_mapping_hdl , uint32_t div_num )
{
ESP_RETURN_ON_FALSE ( ( ( div_num > 0 ) & & ( div_num < = 256 ) ) , ESP_ERR_INVALID_ARG , TAG , " Divider number must be in the range of [1, 256] " ) ;
ESP_RETURN_ON_FALSE ( ( clkout_mapping_hdl ! = NULL ) , ESP_ERR_INVALID_ARG , TAG , " Clock out mapping handle passed in is invalid " ) ;
portENTER_CRITICAL ( & clkout_mapping_hdl - > clkout_mapping_lock ) ;
clk_hal_clock_output_set_divider ( clkout_mapping_hdl - > clkout_channel_hdl - > channel_id , div_num ) ;
portEXIT_CRITICAL ( & clkout_mapping_hdl - > clkout_mapping_lock ) ;
return ESP_OK ;
}
# endif
2023-09-04 05:20:36 -04:00
# if CONFIG_IDF_TARGET_ESP32
// Due to a hardware bug, PIN_CTRL cannot select 0xf output, whereas 0xf is the default value.
__attribute__ ( ( constructor ) )
static void esp_clock_output_pin_ctrl_init ( void )
{
gpio_ll_set_pin_ctrl ( 0 , CLK_OUT1 , CLK_OUT1_S ) ;
gpio_ll_set_pin_ctrl ( 0 , CLK_OUT2 , CLK_OUT2_S ) ;
gpio_ll_set_pin_ctrl ( 0 , CLK_OUT3 , CLK_OUT3_S ) ;
}
# endif