1
6
7
8
9
10
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
77
78
93
94
95
96
99
103
104
105
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
127
128
129
130
133
134
135
136
145
146
159
160
161
162
163
164
165
166
167
168
169
170
175
176
177
178
179
180
181
187
188
189
190
191
192
193
194
195
196
197
198
199
200
205
206
207
208
209
210
211
212
213
217
218
219
220
221
222
228
229
235
236
242
243
248
249
254
255
260
261
262
263
264
265
270
271
272
273
274
275
276
277
278
279
283
284
285
286
287
288
289
290
291
292
293
294
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
329
330
331
/* ... */
#include <sys/lock.h>
#include "sdkconfig.h"
#if CONFIG_RMT_ENABLE_DEBUG_LOG
#define LOG_LOCAL_LEVEL ESP_LOG_DEBUG/* ... */
#endif
#include "esp_log.h"
#include "esp_check.h"
#include "rmt_private.h"
#include "clk_ctrl_os.h"
#include "soc/rtc.h"
#include "soc/soc_caps.h"
#include "soc/rmt_periph.h"
#include "hal/rmt_ll.h"
#include "driver/gpio.h"
#include "esp_private/esp_clk_tree_common.h"
#include "esp_private/periph_ctrl.h"11 includes
static const char *TAG = "rmt";
#if SOC_PERIPH_CLK_CTRL_SHARED
#define RMT_CLOCK_SRC_ATOMIC() PERIPH_RCC_ATOMIC()
#else
#define RMT_CLOCK_SRC_ATOMIC()
#endif
#if !SOC_RCC_IS_INDEPENDENT
#define RMT_RCC_ATOMIC() PERIPH_RCC_ATOMIC()
#else
#define RMT_RCC_ATOMIC()
#endif
typedef struct rmt_platform_t {
_lock_t mutex;
rmt_group_t *groups[SOC_RMT_GROUPS];
int group_ref_counts[SOC_RMT_GROUPS];
}{ ... } rmt_platform_t;
static rmt_platform_t s_platform;
#if RMT_USE_RETENTION_LINK
static esp_err_t rmt_create_sleep_retention_link_cb(void *arg);
#endif
rmt_group_t *rmt_acquire_group_handle(int group_id)
{
bool new_group = false;
rmt_group_t *group = NULL;
_lock_acquire(&s_platform.mutex);
if (!s_platform.groups[group_id]) {
group = heap_caps_calloc(1, sizeof(rmt_group_t), RMT_MEM_ALLOC_CAPS);
if (group) {
new_group = true;
s_platform.groups[group_id] = group;
group->group_id = group_id;
group->spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED;
group->occupy_mask = UINT32_MAX & ~((1 << SOC_RMT_CHANNELS_PER_GROUP) - 1);
group->clk_src = 0;
group->intr_priority = RMT_GROUP_INTR_PRIORITY_UNINITIALIZED;
RMT_RCC_ATOMIC() {
rmt_ll_enable_bus_clock(group_id, true);
rmt_ll_reset_register(group_id);
}{...}
#if RMT_USE_RETENTION_LINK
sleep_retention_module_t module = rmt_reg_retention_info[group_id].module;
sleep_retention_module_init_param_t init_param = {
.cbs = {
.create = {
.handle = rmt_create_sleep_retention_link_cb,
.arg = group,
}{...},
}{...},
.depends = RETENTION_MODULE_BITMAP_INIT(CLOCK_SYSTEM)
}{...};
if (sleep_retention_module_init(module, &init_param) != ESP_OK) {
ESP_LOGW(TAG, "init sleep retention failed %d, power domain may be turned off during sleep", group_id);
}{...}
#endif/* ... */
rmt_hal_init(&group->hal);
}{...}
}{...} else {
group = s_platform.groups[group_id];
}{...}
if (group) {
s_platform.group_ref_counts[group_id]++;
}{...}
_lock_release(&s_platform.mutex);
if (new_group) {
ESP_LOGD(TAG, "new group(%d) at %p, occupy=%"PRIx32, group_id, group, group->occupy_mask);
}{...}
return group;
}{ ... }
void rmt_release_group_handle(rmt_group_t *group)
{
int group_id = group->group_id;
rmt_clock_source_t clk_src = group->clk_src;
bool do_deinitialize = false;
rmt_hal_context_t *hal = &group->hal;
_lock_acquire(&s_platform.mutex);
s_platform.group_ref_counts[group_id]--;
if (s_platform.group_ref_counts[group_id] == 0) {
do_deinitialize = true;
s_platform.groups[group_id] = NULL;
RMT_CLOCK_SRC_ATOMIC() {
rmt_ll_enable_group_clock(hal->regs, false);
}{...}
rmt_hal_deinit(hal);
RMT_RCC_ATOMIC() {
rmt_ll_enable_bus_clock(group_id, false);
}{...}
}{...}
_lock_release(&s_platform.mutex);
switch (clk_src) {
#if SOC_RMT_SUPPORT_RC_FAST
case RMT_CLK_SRC_RC_FAST:
periph_rtc_dig_clk8m_disable();
break;/* ... */
#endif
default:
break;...
}{...}
if (do_deinitialize) {
#if RMT_USE_RETENTION_LINK
sleep_retention_module_t module = rmt_reg_retention_info[group_id].module;
if (sleep_retention_is_module_created(module)) {
sleep_retention_module_free(module);
}{...}
if (sleep_retention_is_module_inited(module)) {
sleep_retention_module_deinit(module);
}{...}
#endif/* ... */
free(group);
ESP_LOGD(TAG, "del group(%d)", group_id);
}{...}
}{ ... }
esp_err_t rmt_select_periph_clock(rmt_channel_handle_t chan, rmt_clock_source_t clk_src)
{
esp_err_t ret = ESP_OK;
rmt_group_t *group = chan->group;
int channel_id = chan->channel_id;
uint32_t periph_src_clk_hz = 0;
bool clock_selection_conflict = false;
portENTER_CRITICAL(&group->spinlock);
if (group->clk_src == 0) {
group->clk_src = clk_src;
}{...} else {
clock_selection_conflict = (group->clk_src != clk_src);
}{...}
portEXIT_CRITICAL(&group->spinlock);
ESP_RETURN_ON_FALSE(!clock_selection_conflict, ESP_ERR_INVALID_STATE, TAG,
"group clock conflict, already is %d but attempt to %d", group->clk_src, clk_src);
#if SOC_RMT_SUPPORT_RC_FAST
if (clk_src == RMT_CLK_SRC_RC_FAST) {
periph_rtc_dig_clk8m_enable();
}{...}
#endif/* ... */
ESP_RETURN_ON_ERROR(esp_clk_tree_src_get_freq_hz((soc_module_clk_t)clk_src, ESP_CLK_TREE_SRC_FREQ_PRECISION_CACHED, &periph_src_clk_hz),
TAG, "get clock source frequency failed");
#if CONFIG_PM_ENABLE
esp_pm_lock_type_t pm_lock_type = chan->dma_chan ? ESP_PM_NO_LIGHT_SLEEP : ESP_PM_CPU_FREQ_MAX;
#if SOC_RMT_SUPPORT_APB
if (clk_src == RMT_CLK_SRC_APB) {
pm_lock_type = ESP_PM_APB_FREQ_MAX;
}{...}
#endif/* ... */
sprintf(chan->pm_lock_name, "rmt_%d_%d", group->group_id, channel_id);
ret = esp_pm_lock_create(pm_lock_type, 0, chan->pm_lock_name, &chan->pm_lock);
ESP_RETURN_ON_ERROR(ret, TAG, "create pm lock failed");/* ... */
#endif
esp_clk_tree_enable_src((soc_module_clk_t)clk_src, true);
RMT_CLOCK_SRC_ATOMIC() {
rmt_ll_set_group_clock_src(group->hal.regs, channel_id, clk_src, 1, 1, 0);
rmt_ll_enable_group_clock(group->hal.regs, true);
}{...}
group->resolution_hz = periph_src_clk_hz;
ESP_LOGD(TAG, "group clock resolution:%"PRIu32, group->resolution_hz);
return ret;
}{ ... }
esp_err_t rmt_get_channel_id(rmt_channel_handle_t channel, int *ret_id)
{
ESP_RETURN_ON_FALSE(channel && ret_id, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
*ret_id = channel->channel_id;
return ESP_OK;
}{ ... }
esp_err_t rmt_get_channel_resolution(rmt_channel_handle_t channel, uint32_t *ret_resolution_hz)
{
ESP_RETURN_ON_FALSE(channel && ret_resolution_hz, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
*ret_resolution_hz = channel->resolution_hz;
return ESP_OK;
}{ ... }
esp_err_t rmt_apply_carrier(rmt_channel_handle_t channel, const rmt_carrier_config_t *config)
{
ESP_RETURN_ON_FALSE(channel, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
return channel->set_carrier_action(channel, config);
}{ ... }
esp_err_t rmt_del_channel(rmt_channel_handle_t channel)
{
ESP_RETURN_ON_FALSE(channel, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
return channel->del(channel);
}{ ... }
esp_err_t rmt_enable(rmt_channel_handle_t channel)
{
ESP_RETURN_ON_FALSE(channel, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
return channel->enable(channel);
}{ ... }
esp_err_t rmt_disable(rmt_channel_handle_t channel)
{
ESP_RETURN_ON_FALSE(channel, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
return channel->disable(channel);
}{ ... }
bool rmt_set_intr_priority_to_group(rmt_group_t *group, int intr_priority)
{
bool priority_conflict = false;
portENTER_CRITICAL(&group->spinlock);
if (group->intr_priority == RMT_GROUP_INTR_PRIORITY_UNINITIALIZED) {
group->intr_priority = intr_priority;
}{...} else {
if (intr_priority) {
if (intr_priority != (group->intr_priority)) {
priority_conflict = true;
}{...}
}{...}
}{...}
portEXIT_CRITICAL(&group->spinlock);
return priority_conflict;
}{ ... }
int rmt_get_isr_flags(rmt_group_t *group)
{
int isr_flags = RMT_INTR_ALLOC_FLAG;
if (group->intr_priority) {
isr_flags |= (1 << (group->intr_priority));
}{...} else {
isr_flags |= RMT_ALLOW_INTR_PRIORITY_MASK;
}{...}
return isr_flags;
}{ ... }
#if RMT_USE_RETENTION_LINK
static esp_err_t rmt_create_sleep_retention_link_cb(void *arg)
{
rmt_group_t *group = (rmt_group_t *)arg;
int group_id = group->group_id;
esp_err_t err = sleep_retention_entries_create(rmt_reg_retention_info[group_id].regdma_entry_array,
rmt_reg_retention_info[group_id].array_size,
REGDMA_LINK_PRI_RMT, rmt_reg_retention_info[group_id].module);
return err;
}{...}
void rmt_create_retention_module(rmt_group_t *group)
{
int group_id = group->group_id;
sleep_retention_module_t module = rmt_reg_retention_info[group_id].module;
_lock_acquire(&s_platform.mutex);
if (sleep_retention_is_module_inited(module) && !sleep_retention_is_module_created(module)) {
if (sleep_retention_module_allocate(module) != ESP_OK) {
ESP_LOGW(TAG, "create retention link failed, power domain won't be turned off during sleep");
}{...}
}{...}
_lock_release(&s_platform.mutex);
}{...}
/* ... */#endif