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