1 /*
2  * SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include "esp_attr.h"
8 #include "freertos/portmacro.h"
9 #include "esp_phy_init.h"
10 #include "esp_private/phy.h"
11 #include "esp_timer.h"
12 
13 #if SOC_MODEM_CLOCK_IS_INDEPENDENT
14 #include "esp_private/esp_modem_clock.h"
15 #endif
16 
17 #define PHY_ENABLE_VERSION_PRINT 1
18 
19 static DRAM_ATTR portMUX_TYPE s_phy_int_mux = portMUX_INITIALIZER_UNLOCKED;
20 
21 extern void phy_version_print(void);
22 static _lock_t s_phy_access_lock;
23 
24 /* Reference count of enabling PHY */
25 static bool s_phy_is_enabled = false;
26 
27 #if CONFIG_ESP_PHY_RECORD_USED_TIME
28 #define ESP_PHY_MODEM_COUNT_MAX         (__builtin_ffs(PHY_MODEM_MAX - 1))
29 #define ESP_PHY_IS_VALID_MODEM(modem)   (__builtin_popcount(modem) == 1 && __builtin_ctz(modem) < ESP_PHY_MODEM_COUNT_MAX)
30 
31 static DRAM_ATTR struct {
32     uint64_t used_time;
33     uint64_t enabled_time;
34     uint64_t disabled_time;
35 } s_phy_rf_used_info[ESP_PHY_MODEM_COUNT_MAX];
36 
phy_record_time(bool enabled,esp_phy_modem_t modem)37 static IRAM_ATTR void phy_record_time(bool enabled, esp_phy_modem_t modem) {
38     uint8_t index = __builtin_ctz(modem);
39     if (enabled) {
40         s_phy_rf_used_info[index].enabled_time = esp_timer_get_time();
41     } else {
42         s_phy_rf_used_info[index].disabled_time = esp_timer_get_time();
43         s_phy_rf_used_info[index].used_time += s_phy_rf_used_info[index].disabled_time - s_phy_rf_used_info[index].enabled_time;
44     }
45 }
46 
phy_query_used_time(uint64_t * used_time,esp_phy_modem_t modem)47 esp_err_t phy_query_used_time(uint64_t *used_time, esp_phy_modem_t modem) {
48     if (!ESP_PHY_IS_VALID_MODEM(modem)) {
49         return ESP_ERR_INVALID_ARG;
50     }
51     uint8_t index = __builtin_ctz(modem);
52     _lock_acquire(&s_phy_access_lock);
53     *used_time = s_phy_rf_used_info[index].used_time;
54     if (s_phy_rf_used_info[index].disabled_time < s_phy_rf_used_info[index].enabled_time) {
55         // phy is being used
56         *used_time += esp_timer_get_time() - s_phy_rf_used_info[index].enabled_time;
57     }
58     _lock_release(&s_phy_access_lock);
59     return ESP_OK;
60 }
61 
phy_clear_used_time(esp_phy_modem_t modem)62 esp_err_t phy_clear_used_time(esp_phy_modem_t modem) {
63     if (!ESP_PHY_IS_VALID_MODEM(modem)) {
64         return ESP_ERR_INVALID_ARG;
65     }
66     uint8_t index = __builtin_ctz(modem);
67     _lock_acquire(&s_phy_access_lock);
68     if (s_phy_rf_used_info[index].enabled_time > s_phy_rf_used_info[index].disabled_time) {
69         // phy is being used
70         s_phy_rf_used_info[index].enabled_time = esp_timer_get_time();
71     } else {
72         s_phy_rf_used_info[index].enabled_time = s_phy_rf_used_info[index].disabled_time;
73     }
74     s_phy_rf_used_info[index].used_time = 0;
75     _lock_release(&s_phy_access_lock);
76     return ESP_OK;
77 }
78 #endif
79 
phy_enter_critical(void)80 uint32_t IRAM_ATTR phy_enter_critical(void)
81 {
82     if (xPortInIsrContext()) {
83         portENTER_CRITICAL_ISR(&s_phy_int_mux);
84 
85     } else {
86         portENTER_CRITICAL(&s_phy_int_mux);
87     }
88     // Interrupt level will be stored in current tcb, so always return zero.
89     return 0;
90 }
91 
phy_exit_critical(uint32_t level)92 void IRAM_ATTR phy_exit_critical(uint32_t level)
93 {
94     // Param level don't need any more, ignore it.
95     if (xPortInIsrContext()) {
96         portEXIT_CRITICAL_ISR(&s_phy_int_mux);
97     } else {
98         portEXIT_CRITICAL(&s_phy_int_mux);
99     }
100 }
101 
esp_phy_enable(esp_phy_modem_t modem)102 void esp_phy_enable(esp_phy_modem_t modem)
103 {
104     _lock_acquire(&s_phy_access_lock);
105     if (phy_get_modem_flag() == 0) {
106 #if SOC_MODEM_CLOCK_IS_INDEPENDENT
107         modem_clock_module_enable(PERIPH_PHY_MODULE);
108 #endif
109         if (!s_phy_is_enabled) {
110             register_chipv7_phy(NULL, NULL, PHY_RF_CAL_FULL);
111             phy_version_print();
112             s_phy_is_enabled = true;
113         } else {
114             phy_wakeup_init();
115         }
116         phy_track_pll_init();
117     }
118     phy_set_modem_flag(modem);
119     // Immediately track pll when phy enabled.
120     phy_track_pll();
121 #if CONFIG_ESP_PHY_RECORD_USED_TIME
122     phy_record_time(true, modem);
123 #endif
124     _lock_release(&s_phy_access_lock);
125 }
126 
esp_phy_disable(esp_phy_modem_t modem)127 void esp_phy_disable(esp_phy_modem_t modem)
128 {
129     _lock_acquire(&s_phy_access_lock);
130 #if CONFIG_ESP_PHY_RECORD_USED_TIME
131     phy_record_time(false, modem);
132 #endif
133     phy_clr_modem_flag(modem);
134     if (phy_get_modem_flag() == 0) {
135 
136         phy_track_pll_deinit();
137         phy_close_rf();
138         phy_xpd_tsens();
139 #if SOC_MODEM_CLOCK_IS_INDEPENDENT
140         modem_clock_module_disable(PERIPH_PHY_MODULE);
141 #endif
142     }
143     _lock_release(&s_phy_access_lock);
144 }
145 
phy_get_lock(void)146 _lock_t phy_get_lock(void)
147 {
148     return s_phy_access_lock;
149 }
150