rmt_common.c 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268
  1. /*
  2. * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
  3. *
  4. * SPDX-License-Identifier: Apache-2.0
  5. */
  6. #include <sys/lock.h>
  7. #include "sdkconfig.h"
  8. #if CONFIG_RMT_ENABLE_DEBUG_LOG
  9. // The local log level must be defined before including esp_log.h
  10. // Set the maximum log level for this source file
  11. #define LOG_LOCAL_LEVEL ESP_LOG_DEBUG
  12. #endif
  13. #include "esp_log.h"
  14. #include "esp_check.h"
  15. #include "rmt_private.h"
  16. #include "clk_ctrl_os.h"
  17. #include "soc/rtc.h"
  18. #include "soc/rmt_periph.h"
  19. #include "hal/rmt_ll.h"
  20. #include "driver/gpio.h"
  21. #include "esp_clk_tree.h"
  22. #include "esp_private/periph_ctrl.h"
  23. static const char *TAG = "rmt";
  24. #if SOC_PERIPH_CLK_CTRL_SHARED
  25. #define RMT_CLOCK_SRC_ATOMIC() PERIPH_RCC_ATOMIC()
  26. #else
  27. #define RMT_CLOCK_SRC_ATOMIC()
  28. #endif
  29. #if !SOC_RCC_IS_INDEPENDENT
  30. #define RMT_RCC_ATOMIC() PERIPH_RCC_ATOMIC()
  31. #else
  32. #define RMT_RCC_ATOMIC()
  33. #endif
  34. typedef struct rmt_platform_t {
  35. _lock_t mutex; // platform level mutex lock
  36. rmt_group_t *groups[SOC_RMT_GROUPS]; // array of RMT group instances
  37. int group_ref_counts[SOC_RMT_GROUPS]; // reference count used to protect group install/uninstall
  38. } rmt_platform_t;
  39. static rmt_platform_t s_platform; // singleton platform
  40. rmt_group_t *rmt_acquire_group_handle(int group_id)
  41. {
  42. bool new_group = false;
  43. rmt_group_t *group = NULL;
  44. // prevent install rmt group concurrently
  45. _lock_acquire(&s_platform.mutex);
  46. if (!s_platform.groups[group_id]) {
  47. group = heap_caps_calloc(1, sizeof(rmt_group_t), RMT_MEM_ALLOC_CAPS);
  48. if (group) {
  49. new_group = true;
  50. s_platform.groups[group_id] = group;
  51. group->group_id = group_id;
  52. group->spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED;
  53. // initial occupy_mask: 1111...100...0
  54. group->occupy_mask = UINT32_MAX & ~((1 << SOC_RMT_CHANNELS_PER_GROUP) - 1);
  55. // group clock won't be configured at this stage, it will be set when allocate the first channel
  56. group->clk_src = 0;
  57. // group interrupt priority is shared between all channels, it will be set when allocate the first channel
  58. group->intr_priority = RMT_GROUP_INTR_PRIORITY_UNINITIALIZED;
  59. // enable the bus clock for the RMT peripheral
  60. RMT_RCC_ATOMIC() {
  61. rmt_ll_enable_bus_clock(group_id, true);
  62. rmt_ll_reset_register(group_id);
  63. }
  64. // hal layer initialize
  65. rmt_hal_init(&group->hal);
  66. }
  67. } else { // group already install
  68. group = s_platform.groups[group_id];
  69. }
  70. if (group) {
  71. // someone acquired the group handle means we have a new object that refer to this group
  72. s_platform.group_ref_counts[group_id]++;
  73. }
  74. _lock_release(&s_platform.mutex);
  75. if (new_group) {
  76. ESP_LOGD(TAG, "new group(%d) at %p, occupy=%"PRIx32, group_id, group, group->occupy_mask);
  77. }
  78. return group;
  79. }
  80. void rmt_release_group_handle(rmt_group_t *group)
  81. {
  82. int group_id = group->group_id;
  83. rmt_clock_source_t clk_src = group->clk_src;
  84. bool do_deinitialize = false;
  85. rmt_hal_context_t *hal = &group->hal;
  86. _lock_acquire(&s_platform.mutex);
  87. s_platform.group_ref_counts[group_id]--;
  88. if (s_platform.group_ref_counts[group_id] == 0) {
  89. do_deinitialize = true;
  90. s_platform.groups[group_id] = NULL;
  91. // disable core clock
  92. RMT_CLOCK_SRC_ATOMIC() {
  93. rmt_ll_enable_group_clock(hal->regs, false);
  94. }
  95. // hal layer deinitialize
  96. rmt_hal_deinit(hal);
  97. // disable bus clock
  98. RMT_RCC_ATOMIC() {
  99. rmt_ll_enable_bus_clock(group_id, false);
  100. }
  101. free(group);
  102. }
  103. _lock_release(&s_platform.mutex);
  104. switch (clk_src) {
  105. #if SOC_RMT_SUPPORT_RC_FAST
  106. case RMT_CLK_SRC_RC_FAST:
  107. periph_rtc_dig_clk8m_disable();
  108. break;
  109. #endif // SOC_RMT_SUPPORT_RC_FAST
  110. default:
  111. break;
  112. }
  113. if (do_deinitialize) {
  114. ESP_LOGD(TAG, "del group(%d)", group_id);
  115. }
  116. }
  117. esp_err_t rmt_select_periph_clock(rmt_channel_handle_t chan, rmt_clock_source_t clk_src)
  118. {
  119. esp_err_t ret = ESP_OK;
  120. rmt_group_t *group = chan->group;
  121. int channel_id = chan->channel_id;
  122. uint32_t periph_src_clk_hz = 0;
  123. bool clock_selection_conflict = false;
  124. // check if we need to update the group clock source, group clock source is shared by all channels
  125. portENTER_CRITICAL(&group->spinlock);
  126. if (group->clk_src == 0) {
  127. group->clk_src = clk_src;
  128. } else {
  129. clock_selection_conflict = (group->clk_src != clk_src);
  130. }
  131. portEXIT_CRITICAL(&group->spinlock);
  132. ESP_RETURN_ON_FALSE(!clock_selection_conflict, ESP_ERR_INVALID_STATE, TAG,
  133. "group clock conflict, already is %d but attempt to %d", group->clk_src, clk_src);
  134. // TODO: [clk_tree] to use a generic clock enable/disable or acquire/release function for all clock source
  135. #if SOC_RMT_SUPPORT_RC_FAST
  136. if (clk_src == RMT_CLK_SRC_RC_FAST) {
  137. // RC_FAST clock is not enabled automatically on start up, we enable it here manually.
  138. // Note there's a ref count in the enable/disable function, we must call them in pair in the driver.
  139. periph_rtc_dig_clk8m_enable();
  140. }
  141. #endif // SOC_RMT_SUPPORT_RC_FAST
  142. // get clock source frequency
  143. 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),
  144. TAG, "get clock source frequency failed");
  145. #if CONFIG_PM_ENABLE
  146. // if DMA is not used, we're using CPU to push the data to the RMT FIFO
  147. // if the CPU frequency goes down, the transfer+encoding scheme could be unstable because CPU can't fill the data in time
  148. // so, choose ESP_PM_CPU_FREQ_MAX lock for non-dma mode
  149. // otherwise, chose lock type based on the clock source
  150. esp_pm_lock_type_t pm_lock_type = chan->dma_chan ? ESP_PM_NO_LIGHT_SLEEP : ESP_PM_CPU_FREQ_MAX;
  151. #if SOC_RMT_SUPPORT_APB
  152. if (clk_src == RMT_CLK_SRC_APB) {
  153. // APB clock frequency can be changed during DFS
  154. pm_lock_type = ESP_PM_APB_FREQ_MAX;
  155. }
  156. #endif // SOC_RMT_SUPPORT_APB
  157. sprintf(chan->pm_lock_name, "rmt_%d_%d", group->group_id, channel_id); // e.g. rmt_0_0
  158. ret = esp_pm_lock_create(pm_lock_type, 0, chan->pm_lock_name, &chan->pm_lock);
  159. ESP_RETURN_ON_ERROR(ret, TAG, "create pm lock failed");
  160. #endif // CONFIG_PM_ENABLE
  161. // no division for group clock source, to achieve highest resolution
  162. RMT_CLOCK_SRC_ATOMIC() {
  163. rmt_ll_set_group_clock_src(group->hal.regs, channel_id, clk_src, 1, 1, 0);
  164. rmt_ll_enable_group_clock(group->hal.regs, true);
  165. }
  166. group->resolution_hz = periph_src_clk_hz;
  167. ESP_LOGD(TAG, "group clock resolution:%"PRIu32, group->resolution_hz);
  168. return ret;
  169. }
  170. esp_err_t rmt_get_channel_id(rmt_channel_handle_t channel, int *ret_id)
  171. {
  172. ESP_RETURN_ON_FALSE(channel && ret_id, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
  173. *ret_id = channel->channel_id;
  174. return ESP_OK;
  175. }
  176. esp_err_t rmt_apply_carrier(rmt_channel_handle_t channel, const rmt_carrier_config_t *config)
  177. {
  178. // specially, we allow config to be NULL, means to disable the carrier submodule
  179. ESP_RETURN_ON_FALSE(channel, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
  180. return channel->set_carrier_action(channel, config);
  181. }
  182. esp_err_t rmt_del_channel(rmt_channel_handle_t channel)
  183. {
  184. ESP_RETURN_ON_FALSE(channel, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
  185. gpio_reset_pin(channel->gpio_num);
  186. return channel->del(channel);
  187. }
  188. esp_err_t rmt_enable(rmt_channel_handle_t channel)
  189. {
  190. ESP_RETURN_ON_FALSE(channel, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
  191. return channel->enable(channel);
  192. }
  193. esp_err_t rmt_disable(rmt_channel_handle_t channel)
  194. {
  195. ESP_RETURN_ON_FALSE(channel, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
  196. return channel->disable(channel);
  197. }
  198. bool rmt_set_intr_priority_to_group(rmt_group_t *group, int intr_priority)
  199. {
  200. bool priority_conflict = false;
  201. portENTER_CRITICAL(&group->spinlock);
  202. if (group->intr_priority == RMT_GROUP_INTR_PRIORITY_UNINITIALIZED) {
  203. // intr_priority never allocated, accept user's value unconditionally
  204. // intr_priority could only be set once here
  205. group->intr_priority = intr_priority;
  206. } else {
  207. // group intr_priority already specified
  208. // If interrupt priority specified before, it CANNOT BE CHANGED until `rmt_release_group_handle()` called
  209. // So we have to check if the new priority specified conflicts with the old one
  210. if (intr_priority) {
  211. // User specified intr_priority, check if conflict or not
  212. // Even though the `group->intr_priority` is 0, an intr_priority must have been specified automatically too,
  213. // although we do not know it exactly now, so specifying the intr_priority again might also cause conflict.
  214. // So no matter if `group->intr_priority` is 0 or not, we have to check.
  215. // Value `0` of `group->intr_priority` means "unknown", NOT "unspecified"!
  216. if (intr_priority != (group->intr_priority)) {
  217. // intr_priority conflicts!
  218. priority_conflict = true;
  219. }
  220. }
  221. // else do nothing
  222. // user did not specify intr_priority, then keep the old priority
  223. // We'll use the `RMT_INTR_ALLOC_FLAG | RMT_ALLOW_INTR_PRIORITY_MASK`, which should always success
  224. }
  225. // The `group->intr_priority` will not change any longer, even though another task tries to modify it.
  226. // So we could exit critical here safely.
  227. portEXIT_CRITICAL(&group->spinlock);
  228. return priority_conflict;
  229. }
  230. int rmt_get_isr_flags(rmt_group_t *group)
  231. {
  232. int isr_flags = RMT_INTR_ALLOC_FLAG;
  233. if (group->intr_priority) {
  234. // Use user-specified priority bit
  235. isr_flags |= (1 << (group->intr_priority));
  236. } else {
  237. // Allow all LOWMED priority bits
  238. isr_flags |= RMT_ALLOW_INTR_PRIORITY_MASK;
  239. }
  240. return isr_flags;
  241. }