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