1 /*
2  * SPDX-FileCopyrightText: 2019-2023 Espressif Systems (Shanghai) CO LTD
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 /**
8  * ADC is shared by multiple components, including:
9  * - esp_phy
10  * - esp_wifi
11  * - driver
12  *
13  * However, usages of above components are different.
14  * Therefore, we put the common used parts into `esp_hw_support`, including:
15  * - adc power maintainance
16  * - adc hw calibration settings
17  * - adc locks, to prevent concurrently using adc hw
18  */
19 
20 #include <zephyr/kernel.h>
21 #include <zephyr/logging/log.h>
22 
23 #include <esp_types.h>
24 #include "sdkconfig.h"
25 #include "sys/lock.h"
26 #include "esp_log.h"
27 #include "esp_check.h"
28 #include "hal/adc_types.h"
29 #include "hal/adc_hal.h"
30 #include "hal/adc_hal_common.h"
31 #include "esp_private/adc_share_hw_ctrl.h"
32 #include "esp_private/sar_periph_ctrl.h"
33 //For calibration
34 #if CONFIG_IDF_TARGET_ESP32S2
35 #include "esp_efuse_rtc_table.h"
36 #elif SOC_ADC_CALIBRATION_V1_SUPPORTED
37 #include "esp_efuse_rtc_calib.h"
38 #endif
39 
40 
41 static const char *TAG = "adc_share_hw_ctrl";
42 
43 extern int rtc_spinlock;
44 
45 #define RTC_ENTER_CRITICAL()    do { rtc_spinlock = irq_lock(); } while(0)
46 #define RTC_EXIT_CRITICAL()    irq_unlock(rtc_spinlock);
47 
48 #if SOC_ADC_CALIBRATION_V1_SUPPORTED
49 /*---------------------------------------------------------------
50             ADC Hardware Calibration
51 ---------------------------------------------------------------*/
52 #if CONFIG_IDF_TARGET_ESP32S2
53 #define esp_efuse_rtc_calib_get_ver()     esp_efuse_rtc_table_read_calib_version()
54 
esp_efuse_rtc_calib_get_init_code(int version,uint32_t adc_unit,int atten)55 static inline uint32_t esp_efuse_rtc_calib_get_init_code(int version, uint32_t adc_unit, int atten)
56 {
57     int tag = esp_efuse_rtc_table_get_tag(version, adc_unit, atten, RTCCALIB_V2_PARAM_VINIT);
58     return esp_efuse_rtc_table_get_parsed_efuse_value(tag, false);
59 }
60 #endif
61 
62 static uint32_t s_adc_cali_param[SOC_ADC_PERIPH_NUM][SOC_ADC_ATTEN_NUM] = {};
63 
adc_calc_hw_calibration_code(adc_unit_t adc_n,adc_atten_t atten)64 void adc_calc_hw_calibration_code(adc_unit_t adc_n, adc_atten_t atten)
65 {
66     if (s_adc_cali_param[adc_n][atten]) {
67         ESP_EARLY_LOGV(TAG, "Use calibrated val ADC%d atten=%d: %04X", adc_n + 1, atten, s_adc_cali_param[adc_n][atten]);
68         return ;
69     }
70 
71     // check if we can fetch the values from eFuse.
72     int version = esp_efuse_rtc_calib_get_ver();
73 
74     uint32_t init_code = 0;
75 
76     if ((version >= ESP_EFUSE_ADC_CALIB_VER_MIN) &&
77         (version <= ESP_EFUSE_ADC_CALIB_VER_MAX)) {
78         // Guarantee the calibration version before calling efuse function
79         init_code = esp_efuse_rtc_calib_get_init_code(version, adc_n, atten);
80     }
81 #if SOC_ADC_SELF_HW_CALI_SUPPORTED
82     else {
83         ESP_EARLY_LOGD(TAG, "Calibration eFuse is not configured, use self-calibration for ICode");
84         sar_periph_ctrl_adc_oneshot_power_acquire();
85         RTC_ENTER_CRITICAL();
86         adc_ll_pwdet_set_cct(ADC_LL_PWDET_CCT_DEFAULT);
87         const bool internal_gnd = true;
88         init_code = adc_hal_self_calibration(adc_n, atten, internal_gnd);
89         RTC_EXIT_CRITICAL();
90         sar_periph_ctrl_adc_oneshot_power_release();
91     }
92 #else
93     else {
94         ESP_EARLY_LOGD(TAG, "ICode self-calibration isn't supported");
95     }
96 #endif  //SOC_ADC_SELF_HW_CALI_SUPPORTED
97 
98     s_adc_cali_param[adc_n][atten] = init_code;
99     ESP_EARLY_LOGV(TAG, "Calib(V%d) ADC%d atten=%d: %04X", version, adc_n + 1, atten, init_code);
100 }
101 
adc_set_hw_calibration_code(adc_unit_t adc_n,adc_atten_t atten)102 void IRAM_ATTR adc_set_hw_calibration_code(adc_unit_t adc_n, adc_atten_t atten)
103 {
104     adc_hal_set_calibration_param(adc_n, s_adc_cali_param[adc_n][atten]);
105 }
106 
107 #if SOC_ADC_CALIB_CHAN_COMPENS_SUPPORTED
108 static int s_adc_cali_chan_compens[SOC_ADC_MAX_CHANNEL_NUM][SOC_ADC_ATTEN_NUM] = {};
adc_load_hw_calibration_chan_compens(adc_unit_t adc_n,adc_channel_t chan,adc_atten_t atten)109 void adc_load_hw_calibration_chan_compens(adc_unit_t adc_n, adc_channel_t chan, adc_atten_t atten)
110 {
111     int version = esp_efuse_rtc_calib_get_ver();
112     if ((version >= ESP_EFUSE_ADC_CALIB_VER_MIN) &&
113         (version <= ESP_EFUSE_ADC_CALIB_VER_MAX)) {
114         // Guarantee the calibration version before calling efuse function
115         s_adc_cali_chan_compens[chan][atten] = esp_efuse_rtc_calib_get_chan_compens(version, adc_n, chan, atten);
116     }
117     // No warning when version doesn't match because should has warned in adc_calc_hw_calibration_code
118 }
119 
adc_get_hw_calibration_chan_compens(adc_unit_t adc_n,adc_channel_t chan,adc_atten_t atten)120 int IRAM_ATTR adc_get_hw_calibration_chan_compens(adc_unit_t adc_n, adc_channel_t chan, adc_atten_t atten)
121 {
122     return s_adc_cali_chan_compens[chan][atten];
123 }
124 #endif  // SOC_ADC_CALIB_CHAN_COMPENS_SUPPORTED
125 #endif //#if SOC_ADC_CALIBRATION_V1_SUPPORTED
126 
127 /*---------------------------------------------------------------
128             ADC Hardware Locks
129 ---------------------------------------------------------------*/
130 K_MUTEX_DEFINE(adc1_lock);
131 K_MUTEX_DEFINE(adc2_lock);
132 
133 #define ADC_LOCK_ACQUIRE(lock) do { k_mutex_lock(lock, K_FOREVER); } while(0)
134 #define ADC_LOCK_RELEASE(lock) do { k_mutex_unlock(lock); } while(0)
135 #define ADC_LOCK_TRY_ACQUIRE(lock) k_mutex_lock(lock, K_NO_WAIT)
136 
adc_lock_acquire(adc_unit_t adc_unit)137 esp_err_t adc_lock_acquire(adc_unit_t adc_unit)
138 {
139     if (adc_unit == ADC_UNIT_1) {
140         ADC_LOCK_ACQUIRE(&adc1_lock);
141     }
142 
143     if (adc_unit == ADC_UNIT_2) {
144         ADC_LOCK_ACQUIRE(&adc2_lock);
145     }
146 
147     return ESP_OK;
148 }
149 
adc_lock_release(adc_unit_t adc_unit)150 esp_err_t adc_lock_release(adc_unit_t adc_unit)
151 {
152     if (adc_unit == ADC_UNIT_2) {
153         ESP_RETURN_ON_FALSE((adc2_lock.lock_count != 0), ESP_ERR_INVALID_STATE, TAG, "adc2 lock release without acquiring");
154         ADC_LOCK_RELEASE(&adc2_lock);
155     }
156 
157     if (adc_unit == ADC_UNIT_1) {
158         ESP_RETURN_ON_FALSE((adc1_lock.lock_count != 0), ESP_ERR_INVALID_STATE, TAG, "adc2 lock release without acquiring");
159         ADC_LOCK_RELEASE(&adc1_lock);
160     }
161 
162     return ESP_OK;
163 }
164 
adc_lock_try_acquire(adc_unit_t adc_unit)165 esp_err_t adc_lock_try_acquire(adc_unit_t adc_unit)
166 {
167     if (adc_unit == ADC_UNIT_1) {
168         if (ADC_LOCK_TRY_ACQUIRE(&adc1_lock) == -1) {
169             return ESP_ERR_TIMEOUT;
170         }
171     }
172 
173     if (adc_unit == ADC_UNIT_2) {
174         if (ADC_LOCK_TRY_ACQUIRE(&adc2_lock) == -1) {
175             return ESP_ERR_TIMEOUT;
176         }
177     }
178 
179     return ESP_OK;
180 }
181 
adc2_wifi_acquire(void)182 esp_err_t adc2_wifi_acquire(void)
183 {
184 #if CONFIG_IDF_TARGET_ESP32
185     /* Wi-Fi module will use adc2. Use locks to avoid conflicts. */
186     adc_lock_acquire(ADC_UNIT_2);
187     ESP_LOGD(TAG, "Wi-Fi takes adc2 lock.");
188 #endif
189 
190     return ESP_OK;
191 }
192 
adc2_wifi_release(void)193 esp_err_t adc2_wifi_release(void)
194 {
195 #if CONFIG_IDF_TARGET_ESP32
196     return adc_lock_release(ADC_UNIT_2);
197 #endif
198 
199     return ESP_OK;
200 }
201