55 */
66#include <string.h>
77#include <stdatomic.h>
8+ #include "soc/soc_caps.h"
89#include "esp_log.h"
910#include "esp_heap_caps.h"
1011#include "driver/bitscrambler.h"
@@ -20,6 +21,13 @@ static const char *TAG = "bitscrambler";
2021#define BITSCRAMBLER_MEM_ALLOC_CAPS MALLOC_CAP_DEFAULT
2122#endif
2223
24+ #if !SOC_RCC_IS_INDEPENDENT
25+ // Reset and Clock Control registers are mixing with other peripherals, so we need to use a critical section
26+ #define BS_RCC_ATOMIC () PERIPH_RCC_ATOMIC()
27+ #else
28+ #define BS_RCC_ATOMIC ()
29+ #endif
30+
2331#define BITSCRAMBLER_BINARY_VER 1 //max version we're compatible with
2432#define BITSCRAMBLER_HW_REV 0
2533
@@ -54,95 +62,92 @@ typedef struct {
5462atomic_flag tx_in_use = ATOMIC_FLAG_INIT ;
5563atomic_flag rx_in_use = ATOMIC_FLAG_INIT ;
5664
57- // Claim both TX and RX channels for loopback use
58- // Returns true on success, false if any of the two directions already is claimed.
59- static bool claim_channel_loopback (void )
60- {
61- bool old_val_tx = atomic_flag_test_and_set (& tx_in_use );
62- if (old_val_tx ) {
63- return false;
64- }
65- bool old_val_rx = atomic_flag_test_and_set (& rx_in_use );
66- if (old_val_rx ) {
67- atomic_flag_clear (& tx_in_use );
68- return false;
69- }
70- return true;
71- }
65+ // This is a reference count for the BitScrambler module. It is used to keep track of how many clients are using the module.
66+ atomic_int group_ref_count = 0 ;
7267
7368// Claim a channel using the direction it indicated.
7469// Returns true on success, false if the direction already is claimed
7570static bool claim_channel (bitscrambler_direction_t dir )
7671{
72+ int old_use_count = atomic_fetch_add (& group_ref_count , 1 );
73+ if (old_use_count == 0 ) {
74+ BS_RCC_ATOMIC () {
75+ // This is the first client using the module, so we need to enable the sys clock
76+ bitscrambler_ll_set_bus_clock_sys_enable (true);
77+ bitscrambler_ll_reset_sys ();
78+ // also power on the memory
79+ bitscrambler_ll_mem_power_by_pmu ();
80+ }
81+ }
7782 if (dir == BITSCRAMBLER_DIR_TX ) {
7883 bool old_val = atomic_flag_test_and_set (& tx_in_use );
7984 if (old_val ) {
80- return false;
85+ goto err ;
86+ } else {
87+ BS_RCC_ATOMIC () {
88+ bitscrambler_ll_set_bus_clock_tx_enable (true);
89+ bitscrambler_ll_reset_tx ();
90+ }
8191 }
82- } else if ( dir == BITSCRAMBLER_DIR_RX ) {
92+ } else {
8393 bool old_val = atomic_flag_test_and_set (& rx_in_use );
8494 if (old_val ) {
85- return false;
95+ goto err ;
96+ } else {
97+ BS_RCC_ATOMIC () {
98+ bitscrambler_ll_set_bus_clock_rx_enable (true);
99+ bitscrambler_ll_reset_rx ();
100+ }
86101 }
87102 }
88103 return true;
104+ err :
105+ atomic_fetch_sub (& group_ref_count , 1 );
106+ return false;
89107}
90108
91- //Initialize the BitScrambler object and hardware using the given config.
92- static esp_err_t init_from_config (bitscrambler_t * bs , const bitscrambler_config_t * config )
93- {
94- bs -> cfg = * config ; //Copy config over
95- bs -> hw = BITSCRAMBLER_LL_GET_HW (0 ); //there's only one as of now; if there's more, we need to handle them as a pool.
96- return ESP_OK ;
97- }
98-
99- static void enable_clocks (bitscrambler_t * bs )
109+ // Release the channel using the direction it indicated.
110+ static void release_channel (bitscrambler_direction_t dir )
100111{
101- PERIPH_RCC_ACQUIRE_ATOMIC (PERIPH_BITSCRAMBLER_MODULE , ref_count ) {
102- if (ref_count == 0 ) { //we're the first to enable the BitScrambler module
103- bitscrambler_ll_set_bus_clock_sys_enable (1 );
104- bitscrambler_ll_reset_sys ();
105- bitscrambler_ll_mem_power_by_pmu ();
106- }
107- if (bs -> cfg .dir == BITSCRAMBLER_DIR_RX || bs -> loopback ) {
108- bitscrambler_ll_set_bus_clock_rx_enable (1 );
109- bitscrambler_ll_reset_rx ();
110- }
111- if (bs -> cfg .dir == BITSCRAMBLER_DIR_TX || bs -> loopback ) {
112- bitscrambler_ll_set_bus_clock_tx_enable (1 );
113- bitscrambler_ll_reset_tx ();
112+ if (dir == BITSCRAMBLER_DIR_TX ) {
113+ atomic_flag_clear (& tx_in_use );
114+ } else if (dir == BITSCRAMBLER_DIR_RX ) {
115+ atomic_flag_clear (& rx_in_use );
116+ }
117+ int old_use_count = atomic_fetch_sub (& group_ref_count , 1 );
118+ if (old_use_count == 1 ) {
119+ // This is the last client using the module, so we need to disable the sys clock
120+ BS_RCC_ATOMIC () {
121+ bitscrambler_ll_set_bus_clock_sys_enable (false);
122+ bitscrambler_ll_mem_force_power_off ();
114123 }
115124 }
116125}
117126
118- static void disable_clocks (bitscrambler_t * bs )
127+ //Initialize the BitScrambler object and hardware using the given config.
128+ static esp_err_t init_from_config (bitscrambler_t * bs , const bitscrambler_config_t * config )
119129{
120- PERIPH_RCC_RELEASE_ATOMIC (PERIPH_BITSCRAMBLER_MODULE , ref_count ) {
121- if (bs -> cfg .dir == BITSCRAMBLER_DIR_RX || bs -> loopback ) {
122- bitscrambler_ll_set_bus_clock_rx_enable (0 );
123- }
124- if (bs -> cfg .dir == BITSCRAMBLER_DIR_TX || bs -> loopback ) {
125- bitscrambler_ll_set_bus_clock_tx_enable (0 );
126- }
127- if (ref_count == 0 ) { //we're the last to disable the BitScrambler module
128- bitscrambler_ll_set_bus_clock_sys_enable (0 );
129- bitscrambler_ll_mem_force_power_off ();
130- }
131- }
130+ bs -> cfg = * config ; //Copy config over
131+ bs -> hw = BITSCRAMBLER_LL_GET_HW (0 ); //there's only one as of now; if there's more, we need to handle them as a pool.
132+ return ESP_OK ;
132133}
133134
134- //Private function: init an existing BitScrambler object as a loopback BitScrambler.
135+ // init an existing BitScrambler object as a loopback BitScrambler, only used by the bitscrambler loopback driver
135136esp_err_t bitscrambler_init_loopback (bitscrambler_handle_t handle , const bitscrambler_config_t * config )
136137{
137- if (!claim_channel_loopback ()) {
138+ // claim the TX channel first
139+ if (!claim_channel (BITSCRAMBLER_DIR_TX )) {
140+ return ESP_ERR_NOT_FOUND ;
141+ }
142+ // claim the RX channel, if it fails, release the TX channel
143+ if (!claim_channel (BITSCRAMBLER_DIR_RX )) {
144+ release_channel (BITSCRAMBLER_DIR_TX );
138145 return ESP_ERR_NOT_FOUND ;
139146 }
140147
141- assert ( config -> dir == BITSCRAMBLER_DIR_TX );
148+ // mark the BitScrambler object as a loopback BitScrambler
142149 handle -> loopback = true;
143- enable_clocks (handle );
144- esp_err_t r = init_from_config (handle , config );
145- return r ;
150+ return init_from_config (handle , config );
146151}
147152
148153esp_err_t bitscrambler_new (const bitscrambler_config_t * config , bitscrambler_handle_t * handle )
@@ -172,8 +177,6 @@ esp_err_t bitscrambler_new(const bitscrambler_config_t *config, bitscrambler_han
172177 return r ;
173178 }
174179
175- enable_clocks (bs );
176-
177180 // Return the handle
178181 * handle = bs ;
179182 return ESP_OK ;
@@ -304,14 +307,11 @@ void bitscrambler_free(bitscrambler_handle_t handle)
304307 if (!handle ) {
305308 return ;
306309 }
307- disable_clocks (handle );
308310 if (handle -> loopback ) {
309- atomic_flag_clear (& tx_in_use );
310- atomic_flag_clear (& rx_in_use );
311- } else if (handle -> cfg .dir == BITSCRAMBLER_DIR_TX ) {
312- atomic_flag_clear (& tx_in_use );
313- } else if (handle -> cfg .dir == BITSCRAMBLER_DIR_RX ) {
314- atomic_flag_clear (& rx_in_use );
311+ release_channel (BITSCRAMBLER_DIR_TX );
312+ release_channel (BITSCRAMBLER_DIR_RX );
313+ } else {
314+ release_channel (handle -> cfg .dir );
315315 }
316316 if (handle -> extra_clean_up ) {
317317 handle -> extra_clean_up (handle , handle -> clean_up_user_ctx );
0 commit comments