clk_ctrl_os.c 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  1. /*
  2. * SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD
  3. *
  4. * SPDX-License-Identifier: Apache-2.0
  5. */
  6. #include <freertos/FreeRTOS.h>
  7. #include "clk_ctrl_os.h"
  8. #include "soc/rtc.h"
  9. #include "esp_private/esp_clk_tree_common.h"
  10. #include "esp_check.h"
  11. static portMUX_TYPE periph_spinlock = portMUX_INITIALIZER_UNLOCKED;
  12. static uint8_t s_periph_ref_counts = 0;
  13. static uint32_t s_rc_fast_freq = 0; // Frequency of the RC_FAST clock in Hz
  14. #if SOC_CLK_APLL_SUPPORTED
  15. static const char *TAG = "clk_ctrl_os";
  16. // Current APLL frequency, in HZ. Zero if APLL is not enabled.
  17. static uint32_t s_cur_apll_freq = 0;
  18. static int s_apll_ref_cnt = 0;
  19. #endif
  20. bool periph_rtc_dig_clk8m_enable(void)
  21. {
  22. portENTER_CRITICAL(&periph_spinlock);
  23. if (s_periph_ref_counts == 0) {
  24. rtc_dig_clk8m_enable();
  25. #if SOC_CLK_RC_FAST_SUPPORT_CALIBRATION
  26. s_rc_fast_freq = esp_clk_tree_rc_fast_get_freq_hz(ESP_CLK_TREE_SRC_FREQ_PRECISION_EXACT);
  27. if (s_rc_fast_freq == 0) {
  28. rtc_dig_clk8m_disable();
  29. portEXIT_CRITICAL(&periph_spinlock);
  30. return false;
  31. }
  32. #endif //SOC_CLK_RC_FAST_SUPPORT_CALIBRATION
  33. }
  34. s_periph_ref_counts++;
  35. portEXIT_CRITICAL(&periph_spinlock);
  36. return true;
  37. }
  38. uint32_t periph_rtc_dig_clk8m_get_freq(void)
  39. {
  40. #if !SOC_CLK_RC_FAST_SUPPORT_CALIBRATION
  41. /* Workaround: CLK8M calibration cannot be performed, we can only return its theoretic value */
  42. return SOC_CLK_RC_FAST_FREQ_APPROX;
  43. #else
  44. return s_rc_fast_freq;
  45. #endif
  46. }
  47. void periph_rtc_dig_clk8m_disable(void)
  48. {
  49. portENTER_CRITICAL(&periph_spinlock);
  50. assert(s_periph_ref_counts > 0);
  51. s_periph_ref_counts--;
  52. if (s_periph_ref_counts == 0) {
  53. s_rc_fast_freq = 0;
  54. rtc_dig_clk8m_disable();
  55. }
  56. portEXIT_CRITICAL(&periph_spinlock);
  57. }
  58. #if SOC_CLK_APLL_SUPPORTED
  59. void periph_rtc_apll_acquire(void)
  60. {
  61. portENTER_CRITICAL(&periph_spinlock);
  62. s_apll_ref_cnt++;
  63. if (s_apll_ref_cnt == 1) {
  64. // For the first time enable APLL, need to set power up
  65. rtc_clk_apll_enable(true);
  66. }
  67. portEXIT_CRITICAL(&periph_spinlock);
  68. }
  69. void periph_rtc_apll_release(void)
  70. {
  71. portENTER_CRITICAL(&periph_spinlock);
  72. assert(s_apll_ref_cnt > 0);
  73. s_apll_ref_cnt--;
  74. if (s_apll_ref_cnt == 0) {
  75. // If there is no peripheral using APLL, shut down the power
  76. s_cur_apll_freq = 0;
  77. rtc_clk_apll_enable(false);
  78. }
  79. portEXIT_CRITICAL(&periph_spinlock);
  80. }
  81. esp_err_t periph_rtc_apll_freq_set(uint32_t expt_freq, uint32_t *real_freq)
  82. {
  83. uint32_t o_div = 0;
  84. uint32_t sdm0 = 0;
  85. uint32_t sdm1 = 0;
  86. uint32_t sdm2 = 0;
  87. // Guarantee 'periph_rtc_apll_acquire' has been called before set apll freq
  88. assert(s_apll_ref_cnt > 0);
  89. uint32_t apll_freq = rtc_clk_apll_coeff_calc(expt_freq, &o_div, &sdm0, &sdm1, &sdm2);
  90. ESP_RETURN_ON_FALSE(apll_freq, ESP_ERR_INVALID_ARG, TAG, "APLL coefficients calculate failed");
  91. bool need_config = true;
  92. portENTER_CRITICAL(&periph_spinlock);
  93. /* If APLL is not in use or only one peripheral in use, its frequency can be changed as will
  94. * But when more than one peripheral refers APLL, its frequency is not allowed to change once it is set */
  95. if (s_cur_apll_freq == 0 || s_apll_ref_cnt < 2) {
  96. s_cur_apll_freq = apll_freq;
  97. } else {
  98. apll_freq = s_cur_apll_freq;
  99. need_config = false;
  100. }
  101. portEXIT_CRITICAL(&periph_spinlock);
  102. *real_freq = apll_freq;
  103. if (need_config) {
  104. ESP_LOGD(TAG, "APLL will working at %"PRIu32" Hz with coefficients [sdm0] %"PRIu32" [sdm1] %"PRIu32" [sdm2] %"PRIu32" [o_div] %"PRIu32"",
  105. apll_freq, sdm0, sdm1, sdm2, o_div);
  106. /* Set coefficients for APLL, notice that it doesn't mean APLL will start */
  107. rtc_clk_apll_coeff_set(o_div, sdm0, sdm1, sdm2);
  108. } else {
  109. return ESP_ERR_INVALID_STATE;
  110. }
  111. return ESP_OK;
  112. }
  113. #endif // SOC_CLK_APLL_SUPPORTED