1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2025-12-16 18:13:49 +01:00
RIOT/cpu/esp32/periph/i2c_hw.c
2025-05-28 17:50:42 +02:00

758 lines
24 KiB
C

/*
* Copyright (C) 2018 Gunar Schorcht
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup cpu_esp32
* @ingroup drivers_periph_i2c
* @{
*
* @file
* @brief Low-level I2C driver implementation for ESP32 SDK
*
* @note The hardware implementation seems to be very poor and faulty.
* I2C commands in the I2C command pipeline are not executed
* sporadically. A number of ACK errors and timeouts caused by
* protocol errors are the result. You should use the hardware
* implementation only if they can be tolerated.
*
* @author Gunar Schorcht <gunar@schorcht.net>
*
* @}
*/
/*
* The I2C peripheral driver uses a mixture of the high level functions and
* the HAL driver. While complex functions like the configuration of the I2C
* interface use the high level interface, the HAL interface is used by data
* transfer functions for better performance and better control.
*/
#include <errno.h>
#include "gpio_arch.h"
#include "irq_arch.h"
#include "log.h"
#include "macros/units.h"
#include "mutex.h"
#include "periph_conf.h"
#include "periph/i2c.h"
#include "time_units.h"
#include "ztimer.h"
#include "esp_attr.h"
#include "esp_clk_tree.h"
#include "esp_cpu.h"
#include "esp_driver_i2c/i2c_private.h"
#include "esp_rom_gpio.h"
#include "esp_rom_sys.h"
#include "hal/i2c_hal.h"
#include "rom/ets_sys.h"
#include "soc/clk_tree_defs.h"
#include "soc/i2c_reg.h"
#define ENABLE_DEBUG 0
#include "debug.h"
/* typical values according to the ESP-IDF doc */
#define I2C_GLITCH_IGNORE_CNT (7)
#define I2C_CLR_BUS_TIMEOUT_MS (50)
/* Ensure that the I2Cn_* symbols define I2C_DEV(n) */
#if !defined(I2C0_SPEED) && defined(I2C1_SPEED)
#error "I2C_DEV(1) is used but I2C_DEV(0) is not defined"
#endif
typedef struct {
i2c_speed_t clk_freq; /* clock freuency in Hz according to bus speed */
uint8_t cmd; /* command index of a transfer*/
uint8_t cmd_op; /* main command op code of a transfer */
uint8_t len; /* data length of a transfer */
uint32_t status; /* results of a transfer */
mutex_t cmd_lock; /* command execution locking */
mutex_t dev_lock; /* device locking */
} _i2c_bus_t;
static i2c_hal_context_t _i2c_hw[] = {
#if defined(I2C0_SPEED)
{ .dev = I2C_LL_GET_HW(0) },
#endif
#if defined(I2C1_SPEED)
{ .dev = I2C_LL_GET_HW(1) },
#endif
};
_Static_assert(I2C_NUMOF == ARRAY_SIZE(_i2c_hw),
"Size of bus descriptor table doesn't match I2C_NUMOF");
_Static_assert(I2C_NUMOF <= I2C_NUMOF_MAX,
"Number of defined I2C devices is greater than the number of supported devices");
static _i2c_bus_t _i2c_bus[I2C_NUMOF] = {
#if defined(I2C0_SPEED)
{
.cmd_lock = MUTEX_INIT_LOCKED,
.dev_lock = MUTEX_INIT,
},
#endif
#if defined(I2C1_SPEED)
{
.cmd_lock = MUTEX_INIT_LOCKED,
.dev_lock = MUTEX_INIT,
},
#endif
};
/* forward declaration of internal functions */
static void _i2c_start_cmd(i2c_t dev);
static void _i2c_stop_cmd(i2c_t dev);
static void _i2c_end_cmd(i2c_t dev);
static void _i2c_write_cmd(i2c_t dev, const uint8_t* data, uint8_t len);
static void _i2c_read_cmd(i2c_t dev, uint8_t len, bool last);
static void _i2c_transfer(i2c_t dev);
static void _i2c_intr_handler(void *arg);
static int _i2c_status_to_errno(i2c_t dev);
static void _i2c_configure_gpios(i2c_t dev);
static void _i2c_reset_and_configure(i2c_t dev);
static void _i2c_reset_fsm(i2c_t dev);
static void _i2c_clear_bus(i2c_t dev);
void i2c_init(i2c_t dev)
{
_Static_assert(I2C_NUMOF <= I2C_NUMOF_MAX, "Too many I2C devices defined");
_Static_assert(I2C_NUMOF == ARRAY_SIZE(_i2c_hw),
"Number of hardware descriptors doesn't match the I2C_NUMOF");
assert(dev < I2C_NUMOF);
/* According to the Technical Reference Manual, only FAST mode is supported,
* but FAST PLUS mode seems to work also. */
assert(i2c_config[dev].speed <= I2C_SPEED_FAST_PLUS);
/* GPIOs for SCL and SDA signals must not already be used for peripherals */
if (((gpio_get_pin_usage(i2c_config[dev].scl) != _I2C) &&
(gpio_get_pin_usage(i2c_config[dev].scl) != _GPIO)) ||
((gpio_get_pin_usage(i2c_config[dev].sda) != _I2C) &&
(gpio_get_pin_usage(i2c_config[dev].sda) != _GPIO))) {
LOG_TAG_ERROR("i2c", "GPIO%u and/or GPIO%u are used for %s/%s and "
"cannot be used as I2C interface\n",
i2c_config[dev].scl, i2c_config[dev].sda,
gpio_get_pin_usage_str(i2c_config[dev].scl),
gpio_get_pin_usage_str(i2c_config[dev].sda));
assert(0);
}
_i2c_bus[dev].cmd = 0;
_i2c_bus[dev].len = 0;
i2c_acquire(dev);
uint32_t clk_speed;
switch (i2c_config[dev].speed) {
case I2C_SPEED_LOW:
clk_speed = 10 * KHZ(1);
break;
case I2C_SPEED_NORMAL:
clk_speed = 100 * KHZ(1);
break;
case I2C_SPEED_FAST:
clk_speed = 400 * KHZ(1);
break;
case I2C_SPEED_FAST_PLUS:
clk_speed = 1000 * KHZ(1);
break;
case I2C_SPEED_HIGH:
clk_speed = 3400 * KHZ(1);
break;
default:
LOG_TAG_ERROR("i2c", "Invalid speed value in %s\n", __func__);
assert(0);
}
_i2c_bus[dev].clk_freq = clk_speed;
/* configures the GPIOs */
_i2c_configure_gpios(dev);
/* enable clocks, set the bus timing and enable the periphery */
_i2c_reset_and_configure(dev);
/* route all I2C interrupt sources to same the CPU interrupt */
intr_matrix_set(PRO_CPU_NUM, i2c_periph_signal[dev].irq, CPU_INUM_I2C);
/* set interrupt handler and enable the CPU interrupt */
esp_cpu_intr_set_handler(CPU_INUM_I2C, _i2c_intr_handler, NULL);
esp_cpu_intr_enable(BIT(CPU_INUM_I2C));
i2c_release(dev);
}
void i2c_acquire(i2c_t dev)
{
DEBUG ("%s\n", __func__);
assert(dev < I2C_NUMOF);
mutex_lock(&_i2c_bus[dev].dev_lock);
}
void i2c_release(i2c_t dev)
{
DEBUG ("%s\n", __func__);
assert(dev < I2C_NUMOF);
mutex_unlock(&_i2c_bus[dev].dev_lock);
}
int i2c_read_bytes(i2c_t dev, uint16_t addr, void *data, size_t len, uint8_t flags)
{
DEBUG ("%s dev=%u addr=%02x data=%p len=%d flags=%01x\n",
__func__, dev, addr, data, len, flags);
assert(dev < I2C_NUMOF);
assert(len > 0);
assert(data != NULL);
int res;
_i2c_bus[dev].cmd_op = I2C_LL_CMD_READ;
_i2c_bus[dev].cmd = 0;
/* reset TX/RX FIFO queue */
i2c_ll_txfifo_rst(_i2c_hw[dev].dev);
i2c_ll_rxfifo_rst(_i2c_hw[dev].dev);
/* if I2C_NOSTART is not set, START condition and ADDR is used */
if (!(flags & I2C_NOSTART)) {
/* send START condition */
_i2c_start_cmd(dev);
/* address handling */
if (flags & I2C_ADDR10) {
/* prepare 10 bit address bytes */
uint8_t addr10[2];
addr10[0] = 0xf0 | (addr & 0x0300) >> 7 | I2C_MASTER_READ;
addr10[1] = addr & 0xff;
/* send ADDR with read flag */
_i2c_write_cmd(dev, addr10, 2);
}
else {
/* send ADDR with read flag */
uint8_t addr7 = (addr << 1 | I2C_READ);
_i2c_write_cmd(dev, &addr7, 1);
}
}
/* read data bytes in blocks of SOC_I2C_FIFO_LEN bytes */
uint32_t off = 0;
/* if len > SOC_I2C_FIFO_LEN read SOC_I2C_FIFO_LEN bytes at a time */
while (len > SOC_I2C_FIFO_LEN) {
/* read one block of data bytes command */
_i2c_bus[dev].len = SOC_I2C_FIFO_LEN;
_i2c_read_cmd(dev, SOC_I2C_FIFO_LEN, false);
_i2c_end_cmd(dev);
_i2c_transfer(dev);
res = _i2c_status_to_errno(dev);
if (res) {
return res;
}
/* if transfer was successful, fetch the data from I2C RAM */
i2c_ll_read_rxfifo(_i2c_hw[dev].dev, data + off, len);
/* reset RX FIFO queue */
i2c_ll_rxfifo_rst(_i2c_hw[dev].dev);
len -= SOC_I2C_FIFO_LEN;
off += SOC_I2C_FIFO_LEN;
}
/* read remaining data bytes command with a final NAK */
_i2c_bus[dev].len = len;
_i2c_read_cmd(dev, len, true);
/* if I2C_NOSTOP flag is not set, send STOP condition is used */
if (!(flags & I2C_NOSTOP)) {
/* send STOP condition */
_i2c_stop_cmd(dev);
}
else {
/* otherwise place end command in pipeline */
_i2c_end_cmd(dev);
}
/* finish operation by executing the command pipeline */
_i2c_transfer(dev);
if ((res = _i2c_status_to_errno(dev))) {
return res;
}
/* fetch the data from RX FIFO */
i2c_ll_read_rxfifo(_i2c_hw[dev].dev, data + off, len);
/* return 0 on success */
return 0;
}
int i2c_write_bytes(i2c_t dev, uint16_t addr, const void *data, size_t len, uint8_t flags)
{
DEBUG ("%s dev=%u addr=%02x data=%p len=%d flags=%01x\n",
__func__, dev, addr, data, len, flags);
assert(dev < I2C_NUMOF);
assert(len > 0);
assert(data != NULL);
int res;
_i2c_bus[dev].cmd_op = I2C_LL_CMD_WRITE;
_i2c_bus[dev].cmd = 0;
/* reset TX FIFO queue */
i2c_ll_txfifo_rst(_i2c_hw[dev].dev);
/* if I2C_NOSTART is not set, START condition and ADDR is used */
if (!(flags & I2C_NOSTART)) {
/* send START condition */
_i2c_start_cmd(dev);
/* address handling */
if (flags & I2C_ADDR10) {
/* prepare 10 bit address bytes */
uint8_t addr10[2];
addr10[0] = 0xf0 | (addr & 0x0300) >> 7 | I2C_MASTER_WRITE;
addr10[1] = addr & 0xff;
/* send ADDR without read flag */
_i2c_write_cmd(dev, addr10, 2);
}
else {
/* send ADDR without read flag */
uint8_t addr7 = addr << 1;
_i2c_write_cmd(dev, &addr7, 1);
}
}
/* send data bytes in blocks of SOC_I2C_FIFO_LEN bytes */
uint32_t off = 0;
uint32_t tx_fifo_free;
/* get available TX FIFO space */
i2c_ll_get_txfifo_len(_i2c_hw[dev].dev, &tx_fifo_free);
/* if len > SOC_I2C_FIFO_LEN write SOC_I2C_FIFO_LEN bytes at a time */
while (len > tx_fifo_free) {
/* send one block of data bytes */
_i2c_bus[dev].len = tx_fifo_free;
_i2c_write_cmd(dev, (uint8_t*)data + off, tx_fifo_free);
_i2c_end_cmd(dev);
_i2c_transfer(dev);
res = _i2c_status_to_errno(dev);
if (res) {
return res;
}
len -= tx_fifo_free;
off += tx_fifo_free;
/* reset TX FIFO queue */
i2c_ll_txfifo_rst(_i2c_hw[dev].dev);
/* update available TX FIFO space */
i2c_ll_get_txfifo_len(_i2c_hw[dev].dev, &tx_fifo_free);
}
/* write remaining data bytes command */
_i2c_bus[dev].len = len;
_i2c_write_cmd(dev, (uint8_t*)data + off, len);
/* if I2C_NOSTOP flag is not set, send STOP condition is used */
if (!(flags & I2C_NOSTOP)) {
/* send STOP condition */
_i2c_stop_cmd(dev);
}
else {
/* otherwise place end command in pipeline */
_i2c_end_cmd(dev);
}
/* finish operation by executing the command pipeline */
_i2c_transfer(dev);
return _i2c_status_to_errno(dev);
}
#if defined(CPU_FAM_ESP32)
#define I2C_NACK_INT_ENA_M I2C_ACK_ERR_INT_ENA_M
#endif
/*
* @note:
* It is a known problem that the ESP32x I2C hardware implementation checks
* the ACK when the next command in the pipeline is executed. That is,
* for a segmented write operation of type `START ADDR DATA0 STOP` the
* `I2C_END_DETECT_INT` and the `I2C_NACK_INT` are triggered simultaneously
* in case of acknowledge errors. In this case it is not clear from the
* interrupt status whether the acknolegdement error has occurred for the
* ADDR or the DATA0 byte. The only way to find out is to check the remaining
* TX FIFO length. If the number of bytes in the TX-FIFO is still higher
* (for two-byte addresses) or equal (for one-byte addresses) to the length of
* the data, the acknolegdement error occurred for the ADDR field.
*/
static int _i2c_status_to_errno(i2c_t dev)
{
if (_i2c_bus[dev].status & I2C_ARBITRATION_LOST_INT_ENA_M) {
LOG_TAG_DEBUG("i2c", "arbitration lost dev=%u\n", dev);
return -EAGAIN;
}
if (_i2c_bus[dev].status & I2C_NACK_INT_ENA_M) {
LOG_TAG_DEBUG("i2c", "ack error dev=%u\n", dev);
if (_i2c_bus[dev].cmd_op == I2C_LL_CMD_WRITE) {
/*
* @note: In a write transfer, an ACK error can happen for the
* address field as well as for data. If the FIFO still contains
* all data bytes, the ACK error happened in address field and we
* have to return -ENXIO. Otherwise, the ACK error happened in data
* field and we have to return -EIO.
*/
uint32_t cnt;
i2c_ll_get_txfifo_len(_i2c_hw[dev].dev, &cnt);
return ((SOC_I2C_FIFO_LEN - cnt) >= _i2c_bus[dev].len) ? -ENXIO : -EIO;
}
else {
/*
* @note: In a read transfer, an ACK is only expected for the
* address field. Thus, an ACK error can only happen for the address
* field. Therefore, we always return -ENXIO in case of an ACK
* error.
*/
return -ENXIO;
}
}
if (_i2c_bus[dev].status & I2C_TIME_OUT_INT_ENA_M) {
LOG_TAG_DEBUG("i2c", "bus timeout dev=%u\n", dev);
_i2c_reset_fsm(dev);
return -ETIMEDOUT;
}
return 0;
}
static void _i2c_start_cmd(i2c_t dev)
{
DEBUG ("%s\n", __func__);
/* place START condition command in command queue */
i2c_ll_hw_cmd_t cmd = { .op_code = I2C_LL_CMD_RESTART };
i2c_ll_master_write_cmd_reg(_i2c_hw[dev].dev, cmd, _i2c_bus[dev].cmd);
/* increment the command counter */
_i2c_bus[dev].cmd++;
}
static void _i2c_stop_cmd(i2c_t dev)
{
DEBUG ("%s\n", __func__);
/* place STOP condition command in command queue */
i2c_ll_hw_cmd_t cmd = { .op_code = I2C_LL_CMD_STOP };
i2c_ll_master_write_cmd_reg(_i2c_hw[dev].dev, cmd, _i2c_bus[dev].cmd);
/* increment the command counter */
_i2c_bus[dev].cmd++;
}
static void _i2c_end_cmd(i2c_t dev)
{
DEBUG ("%s\n", __func__);
/* place END command for continues data transmission in command queue */
i2c_ll_hw_cmd_t cmd = { .op_code = I2C_LL_CMD_END };
i2c_ll_master_write_cmd_reg(_i2c_hw[dev].dev, cmd, _i2c_bus[dev].cmd);
/* increment the command counter */
_i2c_bus[dev].cmd++;
}
static void _i2c_write_cmd(i2c_t dev, const uint8_t* data, uint8_t len)
{
DEBUG ("%s dev=%u data=%p len=%d\n", __func__, dev, data, len);
if (len > SOC_I2C_FIFO_LEN) {
LOG_TAG_ERROR("i2c", "Maximum number of bytes (32 bytes) that can be "
"sent with on transfer reached\n");
return;
}
/* store the data in TX FIFO */
i2c_ll_write_txfifo(_i2c_hw[dev].dev, (uint8_t *)data, len);
/* place WRITE command for multiple bytes in command queue */
i2c_ll_hw_cmd_t cmd = { .op_code = I2C_LL_CMD_WRITE,
.byte_num = len,
.ack_en = 1,
.ack_exp = 0,
.ack_val = 0 };
i2c_ll_master_write_cmd_reg(_i2c_hw[dev].dev, cmd, _i2c_bus[dev].cmd);
/* increment the command counter */
_i2c_bus[dev].cmd++;
}
static void _i2c_read_cmd(i2c_t dev, uint8_t len, bool last)
{
DEBUG ("%s dev=%u len=%d\n", __func__, dev, len);
if (len < 1 || len > SOC_I2C_FIFO_LEN) {
/* at least one byte has to be read */
LOG_TAG_ERROR("i2c", "At least one byte has to be read\n");
return;
}
if (len > 1)
{
/* place READ command for len-1 bytes with positive ack in command queue*/
i2c_ll_hw_cmd_t cmd = { .op_code = I2C_LL_CMD_READ,
.byte_num = len-1,
.ack_en = 0,
.ack_exp = 0,
.ack_val = 0 };
i2c_ll_master_write_cmd_reg(_i2c_hw[dev].dev, cmd, _i2c_bus[dev].cmd);
/* increment the command counter */
_i2c_bus[dev].cmd++;
}
/* place READ command for last byte with negative ack in last segment in command queue*/
i2c_ll_hw_cmd_t cmd = { .op_code = I2C_LL_CMD_READ,
.byte_num = 1,
.ack_en = 0,
.ack_exp = 0,
.ack_val = last ? 1 : 0 };
i2c_ll_master_write_cmd_reg(_i2c_hw[dev].dev, cmd, _i2c_bus[dev].cmd);
/* increment the command counter */
_i2c_bus[dev].cmd++;
}
/* a maximum transfer 33 byte * 9 clock cycles * 1/100000 s takes at
* I2C_SPEED_NORMAL about 450 us, at I2C_SPEED_NORMAL_LOW about 4.5 ms */
#define I2C_TRANSFER_TIMEOUT 640
/* combination of all transfer interrupts */
#define I2C_LL_MASTER_INT (I2C_LL_MASTER_TX_INT | I2C_LL_MASTER_RX_INT)
void _i2c_transfer_timeout(void *arg)
{
i2c_t dev = (i2c_t)(uintptr_t)arg;
/* reset the hardware if I2C got stuck */
_i2c_reset_fsm(dev);
/* set result to timeout */
_i2c_bus[dev].status = I2C_TIME_OUT_INT_ENA_M;
/* wake up the thread that is waiting for the results */
mutex_unlock(&_i2c_bus[dev].cmd_lock);
}
/* Transfer of commands in I2C controller command pipeline */
static void _i2c_transfer(i2c_t dev)
{
DEBUG("%s cmd=%d\n", __func__, _i2c_bus[dev].cmd);
/* disable and enable all transmission interrupts and clear current status */
i2c_ll_clear_intr_mask(_i2c_hw[dev].dev, I2C_LL_MASTER_INT);
i2c_ll_enable_intr_mask(_i2c_hw[dev].dev, I2C_LL_MASTER_INT);
/* set a timer for the case the I2C hardware gets stuck */
#if defined(MODULE_ZTIMER_MSEC)
uint32_t timeout = ((I2C_TRANSFER_TIMEOUT * KHZ(1)) / _i2c_bus[dev].clk_freq) + 1;
ztimer_t timer = { .callback = _i2c_transfer_timeout,
.arg = (void*)(uintptr_t)dev };
ztimer_set(ZTIMER_MSEC, &timer, timeout);
#endif
/* start the execution of commands in command pipeline */
_i2c_bus[dev].status = 0;
i2c_ll_update(_i2c_hw[dev].dev);
i2c_ll_start_trans(_i2c_hw[dev].dev);
/* wait for transfer results and remove timeout timer*/
mutex_lock(&_i2c_bus[dev].cmd_lock);
#if defined(MODULE_ZTIMER_MSEC)
ztimer_remove(ZTIMER_MSEC, &timer);
#endif
/* returned from transmission */
DEBUG("%s status=%08"PRIx32"\n", __func__, _i2c_bus[dev].status);
/* reset command and data index */
_i2c_bus[dev].cmd = 0;
}
static void IRAM_ATTR _i2c_intr_handler(void *arg)
{
/* to satisfy the compiler */
(void)arg;
irq_isr_enter();
/* all I2C peripheral interrupt sources are routed to the same interrupt,
so we have to use the status register to distinguish interruptees */
for (unsigned dev = 0; dev < I2C_NUMOF; dev++) {
uint32_t mask;
i2c_ll_get_intr_mask(_i2c_hw[dev].dev, &mask);
/* test for transfer related interrupts */
if (mask) {
_i2c_bus[dev].status = mask;
/* disable all interrupts and clear pending interrupts */
i2c_ll_clear_intr_mask(_i2c_hw[dev].dev, I2C_LL_MASTER_INT);
i2c_ll_enable_intr_mask(_i2c_hw[dev].dev, I2C_LL_MASTER_INT);
/* wake up the thread that is waiting for the results */
mutex_unlock(&_i2c_bus[dev].cmd_lock);
}
}
irq_isr_exit();
}
void i2c_print_config(void)
{
if (I2C_NUMOF) {
for (unsigned dev = 0; dev < I2C_NUMOF; dev++) {
printf("\tI2C_DEV(%u)\tscl=%d sda=%d\n",
dev, i2c_config[dev].scl, i2c_config[dev].sda);
}
}
else {
LOG_TAG_INFO("i2c", "no I2C devices\n");
}
}
static void _i2c_configure_gpios(i2c_t dev)
{
gpio_init(i2c_config[dev].scl, i2c_config[dev].scl_pullup ? GPIO_IN_OD_PU : GPIO_IN_OD_PU);
gpio_set(i2c_config[dev].scl);
esp_rom_gpio_connect_out_signal(i2c_config[dev].scl, i2c_periph_signal[dev].scl_out_sig, 0, 0);
esp_rom_gpio_connect_in_signal(i2c_config[dev].scl, i2c_periph_signal[dev].scl_in_sig, 0);
gpio_init(i2c_config[dev].sda, i2c_config[dev].sda_pullup ? GPIO_IN_OD_PU : GPIO_IN_OD_PU);
gpio_set(i2c_config[dev].sda);
esp_rom_gpio_connect_out_signal(i2c_config[dev].sda, i2c_periph_signal[dev].sda_out_sig, 0, 0);
esp_rom_gpio_connect_in_signal(i2c_config[dev].sda, i2c_periph_signal[dev].sda_in_sig, 0);
/* store the usage type in GPIO table */
gpio_set_pin_usage(i2c_config[dev].scl, _I2C);
gpio_set_pin_usage(i2c_config[dev].sda, _I2C);
}
static void _i2c_reset_and_configure(i2c_t dev)
{
i2c_ll_enable_controller_clock(_i2c_hw[dev].dev, true);
I2C_RCC_ATOMIC() {
i2c_ll_enable_bus_clock(dev, true);
i2c_ll_reset_register(dev);
}
i2c_ll_master_init(_i2c_hw[dev].dev);
i2c_ll_set_data_mode(_i2c_hw[dev].dev, I2C_DATA_MODE_MSB_FIRST, I2C_DATA_MODE_MSB_FIRST);
i2c_ll_txfifo_rst(_i2c_hw[dev].dev);
i2c_ll_rxfifo_rst(_i2c_hw[dev].dev);
soc_module_clk_t clk_src = (soc_module_clk_t)I2C_CLK_SRC_DEFAULT;
uint32_t clk_src_hz;
esp_clk_tree_src_get_freq_hz(clk_src, ESP_CLK_TREE_SRC_FREQ_PRECISION_APPROX, &clk_src_hz);
i2c_hal_clk_config_t clk_cfg;
i2c_ll_master_cal_bus_clk(clk_src_hz, _i2c_bus[dev].clk_freq, &clk_cfg);
i2c_ll_master_set_bus_timing(_i2c_hw[dev].dev, &clk_cfg);
i2c_ll_master_set_filter(_i2c_hw[dev].dev, I2C_GLITCH_IGNORE_CNT);
i2c_ll_set_source_clk(_i2c_hw[dev].dev, (soc_periph_i2c_clk_src_t)clk_src);
i2c_ll_update(_i2c_hw[dev].dev);
/* clear pending interrupts */
i2c_ll_clear_intr_mask(_i2c_hw[dev].dev, I2C_LL_MASTER_EVENT_INTR);
}
static void _i2c_reset_fsm(i2c_t dev)
{
#if SOC_I2C_SUPPORT_HW_FSM_RST
i2c_ll_master_fsm_rst(_i2c_hw[dev].dev);
_i2c_clear_bus(dev);
#else
_i2c_clear_bus(dev);
_i2c_reset_and_configure(dev);
#endif
}
static void _i2c_clear_bus(i2c_t dev)
{
#if SOC_I2C_SUPPORT_HW_CLR_BUS
i2c_ll_master_clr_bus(_i2c_hw[dev].dev, I2C_LL_RESET_SLV_SCL_PULSE_NUM_DEFAULT, true);
uint32_t timeout = system_get_time_ms() + I2C_CLR_BUS_TIMEOUT_MS;
while (i2c_ll_master_is_bus_clear_done(_i2c_hw[dev].dev) &&
(timeout > system_get_time_ms())) { }
if (timeout < system_get_time_ms()) {
LOG_TAG_ERROR("i2c", "clear bus failed\n");
}
i2c_ll_master_clr_bus(_i2c_hw[dev].dev, 0, false);
i2c_ll_update(_i2c_hw[dev].dev);
#else
gpio_set_pin_usage(i2c_config[dev].scl, _GPIO);
gpio_set_pin_usage(i2c_config[dev].sda, _GPIO);
gpio_init(i2c_config[dev].scl, i2c_config[dev].scl_pullup ? GPIO_IN_OD_PU : GPIO_IN_OD_PU);
gpio_init(i2c_config[dev].sda, i2c_config[dev].sda_pullup ? GPIO_IN_OD_PU : GPIO_IN_OD_PU);
gpio_set(i2c_config[dev].scl);
gpio_set(i2c_config[dev].sda);
/*
* If SDA is low it is driven by the slave, wait until SDA goes high, at
* maximum 9 clock cycles in standard mode at 100 kHz including the ACK bit.
*/
uint32_t half_cycle = 5;
int count = 9;
while (!gpio_read(i2c_config[dev].sda) && count--) {
gpio_clear(i2c_config[dev].scl);
esp_rom_delay_us(half_cycle);
gpio_set(i2c_config[dev].scl);
esp_rom_delay_us(half_cycle);
}
/* generate a STOP condition */
gpio_clear(i2c_config[dev].scl);
esp_rom_delay_us(half_cycle);
gpio_clear(i2c_config[dev].sda);
esp_rom_delay_us(half_cycle);
gpio_set(i2c_config[dev].scl);
esp_rom_delay_us(half_cycle);
gpio_set(i2c_config[dev].sda);
/* reconfigure gpios */
_i2c_configure_gpios(dev);
#endif
}