1
6
7
8
9
10
11
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
35
36
37
38
39
40
41
42
47
48
49
50
51
52
53
54
55
56
57
58
59
60
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
109
110
111
117
118
125
126
129
130
135
138
139
142
143
144
145
146
150
151
152
153
154
155
156
157
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
206
209
212
213
218
219
225
226
227
228
229
230
239
240
243
244
245
246
247
248
249
250
251
252
253
254
255
256
261
262
263
264
265
266
267
273
274
275
276
277
278
279
280
281
282
283
284
285
290
291
292
297
298
302
303
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
327
328
329
330
331
332
333
334
335
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
358
359
360
361
362
363
364
369
370
371
372
374
375
376
377
378
379
384
385
386
387
389
390
391
392
393
394
395
396
397
398
399
400
403
404
408
409
410
411
412
413
414
415
416
423
424
425
426
427
428
430
431
432
433
434
435
/* ... */
#include <string.h>
#include <stdio.h>
#include "sdkconfig.h"
#include "esp_types.h"
#if CONFIG_I2C_ENABLE_DEBUG_LOG
#define LOG_LOCAL_LEVEL ESP_LOG_DEBUG/* ... */
#endif
#include "esp_log.h"
#include "esp_check.h"
#include "esp_pm.h"
#include "freertos/FreeRTOS.h"
#include "hal/i2c_hal.h"
#include "hal/gpio_hal.h"
#include "esp_private/periph_ctrl.h"
#include "esp_rom_gpio.h"
#include "i2c_private.h"
#include "driver/gpio.h"
#include "soc/clk_tree_defs.h"
#include "soc/i2c_periph.h"
#include "esp_clk_tree.h"
#include "clk_ctrl_os.h"
#include "esp_private/gpio.h"15 includes
#if SOC_LP_I2C_SUPPORTED
#include "hal/rtc_io_ll.h"
#include "driver/rtc_io.h"
#include "soc/rtc_io_channel.h"
#include "driver/lp_io.h"/* ... */
#endif
#if I2C_USE_RETENTION_LINK
#include "esp_private/sleep_retention.h"
#endif
static const char *TAG = "i2c.common";
typedef struct i2c_platform_t {
_lock_t mutex;
i2c_bus_handle_t buses[SOC_I2C_NUM];
uint32_t count[SOC_I2C_NUM];
}{ ... } i2c_platform_t;
static i2c_platform_t s_i2c_platform = {};
#if I2C_USE_RETENTION_LINK
static esp_err_t s_i2c_sleep_retention_init(void *arg)
{
i2c_bus_t *bus = (i2c_bus_t *)arg;
i2c_port_num_t port_num = bus->port_num;
esp_err_t ret = sleep_retention_entries_create(i2c_regs_retention[port_num].link_list, i2c_regs_retention[port_num].link_num, REGDMA_LINK_PRI_I2C, i2c_regs_retention[port_num].module_id);
ESP_RETURN_ON_ERROR(ret, TAG, "failed to allocate mem for sleep retention");
return ret;
}{...}
void i2c_create_retention_module(i2c_bus_handle_t handle)
{
i2c_port_num_t port_num = handle->port_num;
_lock_acquire(&s_i2c_platform.mutex);
if (handle->retention_link_created == false) {
if (sleep_retention_module_allocate(i2c_regs_retention[port_num].module_id) != ESP_OK) {
ESP_LOGW(TAG, "create retention module failed, power domain can't turn off");
}{...} else {
handle->retention_link_created = true;
}{...}
}{...}
_lock_release(&s_i2c_platform.mutex);
}{...}
/* ... */#endif
static esp_err_t s_i2c_bus_handle_acquire(i2c_port_num_t port_num, i2c_bus_handle_t *i2c_new_bus, i2c_bus_mode_t mode)
{
#if CONFIG_I2C_ENABLE_DEBUG_LOG
esp_log_level_set(TAG, ESP_LOG_DEBUG);
#endif
bool new_bus = false;
i2c_bus_t *bus = NULL;
esp_err_t ret = ESP_OK;
if (!s_i2c_platform.buses[port_num]) {
new_bus = true;
bus = heap_caps_calloc(1, sizeof(i2c_bus_t), I2C_MEM_ALLOC_CAPS);
if (bus) {
s_i2c_platform.buses[port_num] = bus;
bus->port_num = port_num;
bus->spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED;
bus->bus_mode = mode;
bus->is_lp_i2c = (bus->port_num < SOC_HP_I2C_NUM) ? false : true;
#if I2C_USE_RETENTION_LINK
if (bus->is_lp_i2c == false) {
sleep_retention_module_init_param_t init_param = {
.cbs = { .create = { .handle = s_i2c_sleep_retention_init, .arg = (void *)bus } }
}{...};
esp_err_t err = sleep_retention_module_init(i2c_regs_retention[port_num].module_id, &init_param);
if (err != ESP_OK) {
ESP_LOGW(TAG, "init sleep retention failed on bus %d, power domain may be turned off during sleep", port_num);
}{...}
}{...} else {
ESP_LOGW(TAG, "Detected PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP is enabled while LP_I2C is used. Sleep retention is not supported on LP I2C. Please use it properly");
}{...}
#endif/* ... */
if (!bus->is_lp_i2c) {
I2C_RCC_ATOMIC() {
i2c_ll_enable_bus_clock(bus->port_num, true);
i2c_ll_reset_register(bus->port_num);
}{...}
}{...}
#if SOC_LP_I2C_SUPPORTED
else {
LP_I2C_BUS_CLK_ATOMIC() {
lp_i2c_ll_enable_bus_clock(bus->port_num - SOC_HP_I2C_NUM, true);
lp_i2c_ll_reset_register(bus->port_num - SOC_HP_I2C_NUM);
}{...}
}{...}
#endif/* ... */
I2C_CLOCK_SRC_ATOMIC() {
i2c_hal_init(&bus->hal, port_num);
}{...}
}{...}
}{...} else {
ESP_LOGE(TAG, "I2C bus id(%d) has already been acquired", port_num);
bus = s_i2c_platform.buses[port_num];
ret = ESP_ERR_INVALID_STATE;
}{...}
if (bus) {
s_i2c_platform.count[port_num]++;
}{...}
if (new_bus) {
ESP_LOGD(TAG, "new bus(%d) at %p", port_num, bus);
}{...}
*i2c_new_bus = bus;
return ret;
}{ ... }
bool i2c_bus_occupied(i2c_port_num_t port_num)
{
return s_i2c_platform.buses[port_num] != NULL;
}{ ... }
esp_err_t i2c_acquire_bus_handle(i2c_port_num_t port_num, i2c_bus_handle_t *i2c_new_bus, i2c_bus_mode_t mode)
{
bool bus_occupied = false;
bool bus_found = false;
esp_err_t ret = ESP_OK;
_lock_acquire(&s_i2c_platform.mutex);
if (port_num == -1) {
for (int i = 0; i < SOC_HP_I2C_NUM; i++) {
bus_occupied = i2c_bus_occupied(i);
if (bus_occupied == false) {
ret = s_i2c_bus_handle_acquire(i, i2c_new_bus, mode);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "acquire bus failed");
_lock_release(&s_i2c_platform.mutex);
return ret;
}{...}
bus_found = true;
port_num = i;
break;
}{...}
}{...}
if (bus_found == false) {
ESP_LOGE(TAG, "acquire bus failed, no free bus");
_lock_release(&s_i2c_platform.mutex);
return ESP_ERR_NOT_FOUND;
}{...}
}{...} else {
ret = s_i2c_bus_handle_acquire(port_num, i2c_new_bus, mode);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "acquire bus failed");
}{...}
}{...}
_lock_release(&s_i2c_platform.mutex);
return ret;
}{ ... }
esp_err_t i2c_release_bus_handle(i2c_bus_handle_t i2c_bus)
{
int port_num = i2c_bus->port_num;
i2c_clock_source_t clk_src = i2c_bus->clk_src;
bool do_deinitialize = false;
_lock_acquire(&s_i2c_platform.mutex);
if (s_i2c_platform.buses[port_num]) {
s_i2c_platform.count[port_num]--;
if (s_i2c_platform.count[port_num] == 0) {
do_deinitialize = true;
s_i2c_platform.buses[port_num] = NULL;
#if I2C_USE_RETENTION_LINK
if (i2c_bus->is_lp_i2c == false) {
if (i2c_bus->retention_link_created) {
sleep_retention_module_free(i2c_regs_retention[port_num].module_id);
}{...}
sleep_retention_module_deinit(i2c_regs_retention[port_num].module_id);
}{...}
#endif/* ... */
if (i2c_bus->intr_handle) {
ESP_RETURN_ON_ERROR(esp_intr_free(i2c_bus->intr_handle), TAG, "delete interrupt service failed");
}{...}
if (i2c_bus->pm_lock) {
ESP_RETURN_ON_ERROR(esp_pm_lock_delete(i2c_bus->pm_lock), TAG, "delete pm_lock failed");
}{...}
if (!i2c_bus->is_lp_i2c) {
I2C_RCC_ATOMIC() {
i2c_ll_enable_bus_clock(port_num, false);
}{...}
}{...}
#if SOC_LP_I2C_SUPPORTED
else {
LP_I2C_BUS_CLK_ATOMIC() {
lp_i2c_ll_enable_bus_clock(port_num - SOC_HP_I2C_NUM, false);
}{...}
}{...}
#endif/* ... */
free(i2c_bus);
}{...}
}{...}
_lock_release(&s_i2c_platform.mutex);
switch (clk_src) {
#if SOC_I2C_SUPPORT_RTC
case I2C_CLK_SRC_RC_FAST:
periph_rtc_dig_clk8m_disable();
break;/* ... */
#endif
default:
break;...
}{...}
if (do_deinitialize) {
ESP_LOGD(TAG, "delete bus %d", port_num);
}{...}
ESP_RETURN_ON_FALSE(s_i2c_platform.count[port_num] == 0, ESP_ERR_INVALID_STATE, TAG, "Bus not freed entirely");
return ESP_OK;
}{ ... }
esp_err_t i2c_select_periph_clock(i2c_bus_handle_t handle, soc_module_clk_t clk_src)
{
esp_err_t ret = ESP_OK;
ESP_RETURN_ON_FALSE(handle, ESP_ERR_INVALID_ARG, TAG, "I2C empty controller handle");
uint32_t periph_src_clk_hz = 0;
bool clock_selection_conflict = 0;
portENTER_CRITICAL(&handle->spinlock);
if (handle->clk_src == 0) {
handle->clk_src = clk_src;
}{...} else {
clock_selection_conflict = (handle->clk_src != clk_src);
}{...}
portEXIT_CRITICAL(&handle->spinlock);
ESP_RETURN_ON_FALSE(!clock_selection_conflict, ESP_ERR_INVALID_STATE, TAG,
"group clock conflict, already is %d but attempt to %d", handle->clk_src, clk_src);
#if SOC_I2C_SUPPORT_RTC
if (clk_src == (soc_module_clk_t)I2C_CLK_SRC_RC_FAST) {
periph_rtc_dig_clk8m_enable();
}{...}
#endif/* ... */
ESP_RETURN_ON_ERROR(esp_clk_tree_src_get_freq_hz(clk_src, ESP_CLK_TREE_SRC_FREQ_PRECISION_APPROX, &periph_src_clk_hz), TAG, "i2c get clock frequency error");
handle->clk_src_freq_hz = periph_src_clk_hz;
#if CONFIG_PM_ENABLE
bool need_pm_lock = true;
esp_pm_lock_type_t pm_lock_type = ESP_PM_NO_LIGHT_SLEEP;
#if SOC_I2C_SUPPORT_RTC
if (clk_src == (soc_module_clk_t)I2C_CLK_SRC_RC_FAST) {
pm_lock_type = ESP_PM_NO_LIGHT_SLEEP;
}{...}
#endif/* ... */
#if SOC_I2C_SUPPORT_APB
if (clk_src == (soc_module_clk_t)I2C_CLK_SRC_APB) {
pm_lock_type = ESP_PM_APB_FREQ_MAX;
}{...}
#endif/* ... */
if (handle->is_lp_i2c) {
pm_lock_type = ESP_PM_NO_LIGHT_SLEEP;
}{...}
if (need_pm_lock) {
sprintf(handle->pm_lock_name, "I2C_%d", handle->port_num);
ret = esp_pm_lock_create(pm_lock_type, 0, handle->pm_lock_name, &handle->pm_lock);
ESP_RETURN_ON_ERROR(ret, TAG, "create pm lock failed");
}{...}
#endif/* ... */
ESP_LOGD(TAG, "bus clock source frequency: %"PRIu32"hz", periph_src_clk_hz);
return ret;
}{ ... }
static esp_err_t s_hp_i2c_pins_config(i2c_bus_handle_t handle)
{
int port_id = handle->port_num;
ESP_RETURN_ON_ERROR(gpio_set_level(handle->sda_num, 1), TAG, "i2c sda pin set level failed");
gpio_input_enable(handle->sda_num);
gpio_od_enable(handle->sda_num);
if (handle->pull_up_enable) {
gpio_pullup_en(handle->sda_num);
}{...} else {
gpio_pullup_dis(handle->sda_num);
}{...}
gpio_func_sel(handle->sda_num, PIN_FUNC_GPIO);
esp_rom_gpio_connect_out_signal(handle->sda_num, i2c_periph_signal[port_id].sda_out_sig, 0, 0);
esp_rom_gpio_connect_in_signal(handle->sda_num, i2c_periph_signal[port_id].sda_in_sig, 0);
ESP_RETURN_ON_ERROR(gpio_set_level(handle->scl_num, 1), TAG, "i2c scl pin set level failed");
gpio_input_enable(handle->scl_num);
gpio_od_enable(handle->scl_num);
if (handle->pull_up_enable) {
gpio_pullup_en(handle->scl_num);
}{...} else {
gpio_pullup_dis(handle->scl_num);
}{...}
gpio_func_sel(handle->scl_num, PIN_FUNC_GPIO);
esp_rom_gpio_connect_out_signal(handle->scl_num, i2c_periph_signal[port_id].scl_out_sig, 0, 0);
esp_rom_gpio_connect_in_signal(handle->scl_num, i2c_periph_signal[port_id].scl_in_sig, 0);
return ESP_OK;
}{ ... }
#if SOC_LP_I2C_SUPPORTED
static esp_err_t s_lp_i2c_pins_config(i2c_bus_handle_t handle)
{
ESP_RETURN_ON_ERROR(!rtc_gpio_is_valid_gpio(handle->sda_num), TAG, "LP I2C SDA GPIO invalid");
ESP_RETURN_ON_ERROR(!rtc_gpio_is_valid_gpio(handle->scl_num), TAG, "LP I2C SCL GPIO invalid");
#if !SOC_LP_GPIO_MATRIX_SUPPORTED
ESP_RETURN_ON_FALSE((handle->sda_num == LP_I2C_SDA_IOMUX_PAD), ESP_ERR_INVALID_ARG, TAG, LP_I2C_SDA_PIN_ERR_LOG);
ESP_RETURN_ON_FALSE((handle->scl_num == LP_I2C_SCL_IOMUX_PAD), ESP_ERR_INVALID_ARG, TAG, LP_I2C_SCL_PIN_ERR_LOG);/* ... */
#endif
int port_id = handle->port_num;
rtc_gpio_init(handle->sda_num);
rtc_gpio_set_direction(handle->sda_num, RTC_GPIO_MODE_INPUT_OUTPUT_OD);
rtc_gpio_pulldown_dis(handle->sda_num);
if (handle->pull_up_enable) {
rtc_gpio_pullup_en(handle->sda_num);
}{...} else {
rtc_gpio_pullup_dis(handle->sda_num);
}{...}
#if !SOC_LP_GPIO_MATRIX_SUPPORTED
rtc_gpio_iomux_func_sel(handle->sda_num, i2c_periph_signal[port_id].iomux_func);
#else
lp_gpio_connect_out_signal(handle->sda_num, i2c_periph_signal[port_id].sda_out_sig, 0, 0);
lp_gpio_connect_in_signal(handle->sda_num, i2c_periph_signal[port_id].sda_in_sig, 0);/* ... */
#endif
rtc_gpio_init(handle->scl_num);
rtc_gpio_set_direction(handle->scl_num, RTC_GPIO_MODE_INPUT_OUTPUT_OD);
rtc_gpio_pulldown_dis(handle->scl_num);
if (handle->pull_up_enable) {
rtc_gpio_pullup_en(handle->scl_num);
}{...} else {
rtc_gpio_pullup_dis(handle->scl_num);
}{...}
#if !SOC_LP_GPIO_MATRIX_SUPPORTED
rtc_gpio_iomux_func_sel(handle->scl_num, i2c_periph_signal[port_id].iomux_func);
#else
lp_gpio_connect_out_signal(handle->scl_num, i2c_periph_signal[port_id].scl_out_sig, 0, 0);
lp_gpio_connect_in_signal(handle->scl_num, i2c_periph_signal[port_id].scl_in_sig, 0);/* ... */
#endif
return ESP_OK;
}{...}
/* ... */
#endif
esp_err_t i2c_common_set_pins(i2c_bus_handle_t handle)
{
esp_err_t ret = ESP_OK;
if (handle->is_lp_i2c == false) {
ESP_RETURN_ON_ERROR(s_hp_i2c_pins_config(handle), TAG, "config i2c pins failed");
}{...}
#if SOC_LP_I2C_SUPPORTED
else {
ESP_RETURN_ON_ERROR(s_lp_i2c_pins_config(handle), TAG, "config i2c lp pins failed");
}{...}
#endif/* ... */
return ret;
}{ ... }
esp_err_t i2c_common_deinit_pins(i2c_bus_handle_t handle)
{
int port_id = handle->port_num;
if (handle->is_lp_i2c == false) {
ESP_RETURN_ON_ERROR(gpio_output_disable(handle->sda_num), TAG, "disable i2c pins failed");
esp_rom_gpio_connect_in_signal(GPIO_MATRIX_CONST_ZERO_INPUT, i2c_periph_signal[port_id].sda_in_sig, 0);
ESP_RETURN_ON_ERROR(gpio_output_disable(handle->scl_num), TAG, "disable i2c pins failed");
esp_rom_gpio_connect_in_signal(GPIO_MATRIX_CONST_ZERO_INPUT, i2c_periph_signal[port_id].scl_in_sig, 0);
}{...}
#if SOC_LP_I2C_SUPPORTED
else {
ESP_RETURN_ON_ERROR(rtc_gpio_deinit(handle->sda_num), TAG, "deinit rtc gpio failed");
ESP_RETURN_ON_ERROR(rtc_gpio_deinit(handle->scl_num), TAG, "deinit rtc gpio failed");
#if SOC_LP_GPIO_MATRIX_SUPPORTED
lp_gpio_connect_in_signal(GPIO_MATRIX_CONST_ZERO_INPUT, i2c_periph_signal[port_id].scl_in_sig, 0);
lp_gpio_connect_in_signal(GPIO_MATRIX_CONST_ZERO_INPUT, i2c_periph_signal[port_id].sda_in_sig, 0);/* ... */
#endif
}{...}
#endif/* ... */
return ESP_OK;
}{ ... }