clk_ctrl_os.c 4.1 KB

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