nuttx/arch/xtensa/src/esp32s3/esp32s3_i2c.c
Gustavo Henrique Nihei ea829cf7d5 xtensa/esp32s3: Add driver for I2C peripheral in Master mode
Signed-off-by: Gustavo Henrique Nihei <gustavo.nihei@espressif.com>
2022-06-30 00:32:17 +03:00

1741 lines
48 KiB
C

/****************************************************************************
* arch/xtensa/src/esp32s3/esp32s3_i2c.c
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#ifdef CONFIG_ESP32S3_I2C
#include <assert.h>
#include <debug.h>
#include <errno.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include <sys/types.h>
#include <time.h>
#include <nuttx/arch.h>
#include <nuttx/clock.h>
#include <nuttx/irq.h>
#include <nuttx/i2c/i2c_master.h>
#include <nuttx/semaphore.h>
#include <nuttx/spinlock.h>
#include <arch/board/board.h>
#include "esp32s3_gpio.h"
#include "esp32s3_i2c.h"
#include "esp32s3_irq.h"
#include "xtensa.h"
#include "hardware/esp32s3_gpio_sigmap.h"
#include "hardware/esp32s3_i2c.h"
#include "hardware/esp32s3_soc.h"
#include "hardware/esp32s3_system.h"
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/* Command format */
#define I2C_BASE_CMD(_cmd, _check_ack) (((_cmd) << 11) | \
((_check_ack) << 8))
#define I2C_SEND_CMD(_cmd, _check_ack, _bytes) (((_cmd) << 11) | \
((_check_ack) << 8) | \
(_bytes))
#define I2C_RECV_CMD(_cmd, _ack_val, _bytes) (((_cmd) << 11) | \
((_ack_val) << 10) | \
(_bytes))
/* Helper */
#ifdef CONFIG_I2C_POLLED
#define TIMESPEC_TO_US(sec, nano) ((sec) * USEC_PER_SEC + (nano) / NSEC_PER_USEC)
#endif
#define ESP32S3_I2CTIMEOTICKS \
(SEC2TICK(CONFIG_ESP32S3_I2CTIMEOSEC) + MSEC2TICK(CONFIG_ESP32S3_I2CTIMEOMS))
/* Default option */
#define I2C_FIFO_SIZE (32)
#define I2C_FILTER_CYC_NUM_DEF (7)
#define I2C_CLK_FREQ_DEF (100 * 1000)
#define I2C_INT_ERR_MASK (I2C_NACK_INT_ENA | \
I2C_TIME_OUT_INT_ENA | \
I2C_ARBITRATION_LOST_INT_ENA)
#define I2C_SCL_CYC_NUM_DEF 9
/* I2C event trace logic.
* NOTE: trace uses the internal, non-standard, low-level debug interface
* syslog() but does not require that any other debug is enabled.
*/
#ifndef CONFIG_I2C_TRACE
# define i2c_tracereset(p)
# define i2c_tracenew(p,s)
# define i2c_traceevent(p,e,a,s)
# define i2c_tracedump(p)
#endif
#ifndef CONFIG_I2C_NTRACE
# define CONFIG_I2C_NTRACE 32
#endif
/****************************************************************************
* Private Types
****************************************************************************/
/* I2C state */
enum esp32s3_i2cstate_e
{
I2CSTATE_IDLE = 0,
I2CSTATE_PROC,
I2CSTATE_STOP,
#ifndef CONFIG_I2C_POLLED
I2CSTATE_FINISH,
#endif
I2CSTATE_ERROR
};
/* I2C hardware command */
enum i2c_opmode_e
{
I2C_CMD_RESTART = 6, /* I2C restart command */
I2C_CMD_WRITE = 1, /* I2C write command */
I2C_CMD_READ = 3, /* I2C read command */
I2C_CMD_STOP = 2, /* I2C stop command */
I2C_CMD_END = 4 /* I2C end command */
};
#ifdef CONFIG_I2C_TRACE
/* Trace events */
enum esp32s3_trace_e
{
I2CEVENT_NONE = 0, /* No events have occurred with this status */
I2CEVENT_SENDADDR, /* Start/Master bit set and address sent, param = addr */
I2CEVENT_SENDBYTE, /* Send byte, param = bytes */
I2CEVENT_RCVMODEEN, /* Receive mode enabled, param = 0 */
I2CEVENT_RCVBYTE, /* Read more dta, param = bytes */
I2CEVENT_STOP, /* Last byte sten, send stop, param = length */
I2CEVENT_ERROR /* Error occurred, param = 0 */
};
/* Trace data */
struct esp32s3_trace_s
{
uint32_t status; /* I2C 32-bit SR status */
uint32_t count; /* Interrupt count when status change */
enum esp32s3_trace_e event; /* Last event that occurred with this status */
uint32_t parm; /* Parameter associated with the event */
clock_t time; /* First of event or first status */
};
#endif /* CONFIG_I2C_TRACE */
/* I2C Device hardware configuration */
struct esp32s3_i2c_config_s
{
uint32_t clk_freq; /* Clock frequency */
uint8_t scl_pin; /* GPIO configuration for SCL as SCL */
uint8_t sda_pin; /* GPIO configuration for SDA as SDA */
#ifndef CONFIG_I2C_POLLED
uint8_t periph; /* Peripheral ID */
uint8_t irq; /* Interrupt ID */
#endif
uint32_t clk_bit; /* Clock enable bit */
uint32_t rst_bit; /* I2C reset bit */
uint32_t scl_insig; /* I2C SCL input signal index */
uint32_t scl_outsig; /* I2C SCL output signal index */
uint32_t sda_insig; /* I2C SDA input signal index */
uint32_t sda_outsig; /* I2C SDA output signal index */
};
/* I2C Device Private Data */
struct esp32s3_i2c_priv_s
{
const struct i2c_ops_s *ops; /* Standard I2C operations */
uint32_t id; /* I2C instance */
/* Port configuration */
const struct esp32s3_i2c_config_s *config;
int refs; /* Reference count */
sem_t sem_excl; /* Mutual exclusion semaphore */
#ifndef CONFIG_I2C_POLLED
sem_t sem_isr; /* Interrupt wait semaphore */
int cpuint; /* CPU interrupt assigned to this I2C */
uint8_t cpu; /* CPU ID */
#endif
/* I2C work state (see enum esp32s3_i2cstate_e) */
volatile enum esp32s3_i2cstate_e i2cstate;
struct i2c_msg_s *msgv; /* Message list */
uint8_t msgid; /* Current message ID */
ssize_t bytes; /* Processed data bytes */
uint32_t error; /* I2C transform error */
bool ready_read; /* If I2C has read data */
uint32_t clk_freq; /* Current I2C Clock frequency */
spinlock_t lock; /* Device specific lock */
#ifdef CONFIG_I2C_TRACE
/* I2C trace support */
int tndx; /* Trace array index */
clock_t start_time; /* Time when the trace was started */
/* The actual trace data */
struct esp32s3_trace_s trace[CONFIG_I2C_NTRACE];
#endif
};
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
static void i2c_init_clock(struct esp32s3_i2c_priv_s *priv,
uint32_t clock);
static void i2c_init(struct esp32s3_i2c_priv_s *priv);
static void i2c_deinit(struct esp32s3_i2c_priv_s *priv);
static int i2c_transfer(struct i2c_master_s *dev, struct i2c_msg_s *msgs,
int count);
static inline void i2c_process(struct esp32s3_i2c_priv_s *priv,
uint32_t status);
static void i2c_sem_init(struct esp32s3_i2c_priv_s *priv);
static void i2c_sem_destroy(struct esp32s3_i2c_priv_s *priv);
static void i2c_sem_post(struct esp32s3_i2c_priv_s *priv);
static int i2c_sem_wait(struct esp32s3_i2c_priv_s *priv);
#ifndef CONFIG_I2C_POLLED
static int i2c_sem_waitdone(struct esp32s3_i2c_priv_s *priv);
#endif
#ifdef CONFIG_I2C_POLLED
static int i2c_polling_waitdone(struct esp32s3_i2c_priv_s *priv);
#endif
static void i2c_clear_bus(struct esp32s3_i2c_priv_s *priv);
static void i2c_reset_fsmc(struct esp32s3_i2c_priv_s *priv);
#ifdef CONFIG_I2C_RESET
static int i2c_reset(struct i2c_master_s *dev);
#endif
#ifdef CONFIG_I2C_TRACE
static void i2c_tracereset(struct esp32s3_i2c_priv_s *priv);
static void i2c_tracenew(struct esp32s3_i2c_priv_s *priv, uint32_t status);
static void i2c_traceevent(struct esp32s3_i2c_priv_s *priv,
enum esp32s3_trace_e event,
uint32_t parm,
uint32_t status);
static void i2c_tracedump(struct esp32s3_i2c_priv_s *priv);
#endif /* CONFIG_I2C_TRACE */
/****************************************************************************
* Private Data
****************************************************************************/
/* I2C interface */
static const struct i2c_ops_s g_esp32s3_i2c_ops =
{
.transfer = i2c_transfer
#ifdef CONFIG_I2C_RESET
, .reset = i2c_reset
#endif
};
/* I2C device structures */
#ifdef CONFIG_ESP32S3_I2C0
static const struct esp32s3_i2c_config_s g_esp32s3_i2c0_config =
{
.clk_freq = I2C_CLK_FREQ_DEF,
.scl_pin = CONFIG_ESP32S3_I2C0_SCLPIN,
.sda_pin = CONFIG_ESP32S3_I2C0_SDAPIN,
#ifndef CONFIG_I2C_POLLED
.periph = ESP32S3_PERIPH_I2C_EXT0,
.irq = ESP32S3_IRQ_I2C_EXT0,
#endif
.clk_bit = SYSTEM_I2C_EXT0_CLK_EN,
.rst_bit = SYSTEM_I2C_EXT0_RST,
.scl_insig = I2CEXT0_SCL_IN_IDX,
.scl_outsig = I2CEXT0_SCL_OUT_IDX,
.sda_insig = I2CEXT0_SDA_IN_IDX,
.sda_outsig = I2CEXT0_SDA_OUT_IDX
};
static struct esp32s3_i2c_priv_s g_esp32s3_i2c0_priv =
{
.ops = &g_esp32s3_i2c_ops,
.id = ESP32S3_I2C0,
.config = &g_esp32s3_i2c0_config,
.refs = 0,
.i2cstate = I2CSTATE_IDLE,
.msgv = NULL,
.msgid = 0,
.bytes = 0,
.ready_read = false
};
#endif /* CONFIG_ESP32S3_I2C0 */
#ifdef CONFIG_ESP32S3_I2C1
static const struct esp32s3_i2c_config_s g_esp32s3_i2c1_config =
{
.clk_freq = I2C_CLK_FREQ_DEF,
.scl_pin = CONFIG_ESP32S3_I2C1_SCLPIN,
.sda_pin = CONFIG_ESP32S3_I2C1_SDAPIN,
#ifndef CONFIG_I2C_POLLED
.periph = ESP32S3_PERIPH_I2C_EXT1,
.irq = ESP32S3_IRQ_I2C_EXT1,
#endif
.clk_bit = SYSTEM_I2C_EXT1_CLK_EN,
.rst_bit = SYSTEM_I2C_EXT1_RST,
.scl_insig = I2CEXT1_SCL_IN_IDX,
.scl_outsig = I2CEXT1_SCL_OUT_IDX,
.sda_insig = I2CEXT1_SDA_IN_IDX,
.sda_outsig = I2CEXT1_SDA_OUT_IDX
};
static struct esp32s3_i2c_priv_s g_esp32s3_i2c1_priv =
{
.ops = &g_esp32s3_i2c_ops,
.id = ESP32S3_I2C1,
.config = &g_esp32s3_i2c1_config,
.refs = 0,
.i2cstate = I2CSTATE_IDLE,
.msgv = NULL,
.msgid = 0,
.bytes = 0,
.ready_read = false
};
#endif /* CONFIG_ESP32S3_I2C1 */
/* Trace events strings */
#ifdef CONFIG_I2C_TRACE
static const char *g_trace_names[] =
{
"NONE ",
"SENDADDR ",
"SENDBYTE ",
"RCVMODEEN ",
"RCVBYTE ",
"STOP ",
"ERROR "
};
#endif
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: i2c_reset_fifo
*
* Description:
* Reset I2C RX and TX hardware FIFO.
*
* Parameters:
* priv - Pointer to the internal driver state structure.
*
****************************************************************************/
static void i2c_reset_fifo(struct esp32s3_i2c_priv_s *priv)
{
uint32_t bits = I2C_TX_FIFO_RST | I2C_RX_FIFO_RST;
modifyreg32(I2C_FIFO_CONF_REG(priv->id), 0, bits);
modifyreg32(I2C_FIFO_CONF_REG(priv->id), bits, 0);
}
/****************************************************************************
* Name: i2c_intr_enable
*
* Description:
* Enable I2C interrupts.
*
* Parameters:
* priv - Pointer to the internal driver state structure.
*
****************************************************************************/
static void i2c_intr_enable(struct esp32s3_i2c_priv_s *priv)
{
putreg32(UINT32_MAX, I2C_INT_CLR_REG(priv->id));
putreg32(I2C_TRANS_COMPLETE_INT_ENA | I2C_END_DETECT_INT_ENA |
I2C_INT_ERR_MASK, I2C_INT_ENA_REG(priv->id));
}
/****************************************************************************
* Name: i2c_intr_disable
*
* Description:
* Disable I2C interrupts.
*
* Parameters:
* priv - Pointer to the internal driver state structure.
*
****************************************************************************/
static void i2c_intr_disable(struct esp32s3_i2c_priv_s *priv)
{
putreg32(0, I2C_INT_ENA_REG(priv->id));
putreg32(UINT32_MAX, I2C_INT_CLR_REG(priv->id));
}
/****************************************************************************
* Name: i2c_sendstart
*
* Description:
* Send I2C start signal.
*
* Parameters:
* priv - Pointer to the internal driver state structure.
*
****************************************************************************/
static void i2c_sendstart(struct esp32s3_i2c_priv_s *priv)
{
struct i2c_msg_s *msg = &priv->msgv[priv->msgid];
/* Write I2C command registers */
putreg32(I2C_BASE_CMD(I2C_CMD_RESTART, 0), I2C_COMD0_REG(priv->id));
putreg32(I2C_SEND_CMD(I2C_CMD_WRITE, 1, 1), I2C_COMD1_REG(priv->id));
putreg32(I2C_BASE_CMD(I2C_CMD_END, 0), I2C_COMD2_REG(priv->id));
/* Write data to FIFO register */
if ((msg->flags & I2C_M_READ) == 0)
{
putreg32(I2C_WRITEADDR8(msg->addr), I2C_DATA_REG(priv->id));
}
else
{
putreg32(I2C_READADDR8(msg->addr), I2C_DATA_REG(priv->id));
}
/* Enable I2C master TX interrupt */
i2c_intr_enable(priv);
/* Update I2C configuration */
modifyreg32(I2C_CTR_REG(priv->id), 0, I2C_CONF_UPGATE);
/* Configure the I2C to trigger a transaction */
modifyreg32(I2C_CTR_REG(priv->id), 0, I2C_TRANS_START);
}
/****************************************************************************
* Name: i2c_senddata
*
* Description:
* Send I2C data.
*
* Parameters:
* priv - Pointer to the internal driver state structure.
*
****************************************************************************/
static void i2c_senddata(struct esp32s3_i2c_priv_s *priv)
{
struct i2c_msg_s *msg = &priv->msgv[priv->msgid];
int n = msg->length - priv->bytes;
n = n < I2C_FIFO_SIZE ? n : I2C_FIFO_SIZE;
putreg32(I2C_SEND_CMD(I2C_CMD_WRITE, 1, n), I2C_COMD0_REG(priv->id));
putreg32(I2C_BASE_CMD(I2C_CMD_END, 0), I2C_COMD1_REG(priv->id));
for (int i = 0; i < n; i++)
{
putreg32(msg->buffer[priv->bytes + i], I2C_DATA_REG(priv->id));
}
priv->bytes += n;
/* Enable I2C master TX interrupt */
i2c_intr_enable(priv);
/* Update I2C configuration */
modifyreg32(I2C_CTR_REG(priv->id), 0, I2C_CONF_UPGATE);
/* Configure the I2C to trigger a transaction */
modifyreg32(I2C_CTR_REG(priv->id), 0, I2C_TRANS_START);
}
/****************************************************************************
* Name: i2c_recvdata
*
* Description:
* Transfer data from the FIFO to the driver buffer.
*
* Parameters:
* priv - Pointer to the internal driver state structure.
*
****************************************************************************/
static void i2c_recvdata(struct esp32s3_i2c_priv_s *priv)
{
struct i2c_msg_s *msg = &priv->msgv[priv->msgid];
uint32_t cmd = getreg32(I2C_COMD0_REG(priv->id));
uint8_t n = cmd & 0xff;
uint32_t data = 0;
for (int i = 0; i < n; i++)
{
data = getreg32(I2C_DATA_REG(priv->id));
msg->buffer[priv->bytes + i] = data & 0xff;
}
priv->bytes += n;
}
/****************************************************************************
* Name: i2c_startrecv
*
* Description:
* Configure I2C to prepare receiving data and it will create an interrupt
* to receive real data.
*
* Parameters:
* priv - Pointer to the internal driver state structure.
*
****************************************************************************/
static void i2c_startrecv(struct esp32s3_i2c_priv_s *priv)
{
int ack_value;
struct i2c_msg_s *msg = &priv->msgv[priv->msgid];
int n = msg->length - priv->bytes;
if (n > 1)
{
n -= 1;
n = n < I2C_FIFO_SIZE ? n : I2C_FIFO_SIZE;
ack_value = 0;
}
else
{
ack_value = 1;
}
putreg32(I2C_RECV_CMD(I2C_CMD_READ, ack_value, n),
I2C_COMD0_REG(priv->id));
putreg32(I2C_BASE_CMD(I2C_CMD_END, 0), I2C_COMD1_REG(priv->id));
/* Enable I2C master RX interrupt */
i2c_intr_enable(priv);
/* Update I2C configuration */
modifyreg32(I2C_CTR_REG(priv->id), 0, I2C_CONF_UPGATE);
/* Configure the I2C to trigger a transaction */
modifyreg32(I2C_CTR_REG(priv->id), 0, I2C_TRANS_START);
}
/****************************************************************************
* Name: i2c_sendstop
*
* Description:
* Send I2C STOP signal.
*
* Parameters:
* priv - Pointer to the internal driver state structure.
*
****************************************************************************/
static void i2c_sendstop(struct esp32s3_i2c_priv_s *priv)
{
putreg32(I2C_BASE_CMD(I2C_CMD_STOP, 0), I2C_COMD0_REG(priv->id));
/* Enable I2C master TX interrupt */
i2c_intr_enable(priv);
/* Update I2C configuration */
modifyreg32(I2C_CTR_REG(priv->id), 0, I2C_CONF_UPGATE);
/* Configure the I2C to trigger a transaction */
modifyreg32(I2C_CTR_REG(priv->id), 0, I2C_TRANS_START);
}
/****************************************************************************
* Name: i2c_init_clock
*
* Description:
* Initialize I2C hardware clock.
*
* Parameters:
* priv - Pointer to the internal driver state structure.
* bus_freq - Clock frequency of the I2C bus in Hz.
*
****************************************************************************/
static void i2c_init_clock(struct esp32s3_i2c_priv_s *priv,
uint32_t bus_freq)
{
if (bus_freq == priv->clk_freq)
{
return;
}
uint32_t reg_value = 0;
uint32_t scl_low = 0;
uint32_t scl_high = 0;
uint32_t scl_wait_high = 0;
uint32_t sda_hold = 0;
uint32_t sda_sample = 0;
uint32_t setup = 0;
uint32_t hold = 0;
uint32_t timeout = 0;
uint32_t source_clk = XTAL_CLK_FREQ;
uint32_t clkm_div = source_clk / (bus_freq * 1024) + 1;
uint32_t sclk_freq = source_clk / clkm_div;
uint32_t half_cycle = sclk_freq / bus_freq / 2;
modifyreg32(I2C_CLK_CONF_REG(priv->id), I2C_SCLK_DIV_NUM_M,
VALUE_TO_FIELD((clkm_div - 1), I2C_SCLK_DIV_NUM));
/* According to the Technical Reference Manual, the following timings must
* be subtracted by 1.
* Moreover, the frequency calculation also shows that we must subtract 3
* to the total SCL.
*/
scl_low = half_cycle;
putreg32(scl_low - 1 - 2, I2C_SCL_LOW_PERIOD_REG(priv->id));
/* By default, scl_wait_high must be less than scl_high.
* A time compensation is needed for when the bus frequency is higher
* than 50K.
*/
scl_high = (bus_freq <= 50000) ? half_cycle :
(half_cycle / 5 * 4 + 4);
scl_wait_high = half_cycle - scl_high;
reg_value = VALUE_TO_FIELD(scl_high - 1 - 1, I2C_SCL_HIGH_PERIOD);
reg_value |= VALUE_TO_FIELD(scl_wait_high - 1 - 1,
I2C_SCL_WAIT_HIGH_PERIOD);
putreg32(reg_value, I2C_SCL_HIGH_PERIOD_REG(priv->id));
sda_hold = half_cycle / 2;
putreg32(sda_hold - 1, I2C_SDA_HOLD_REG(priv->id));
/* scl_wait_high < sda_sample <= scl_high */
sda_sample = half_cycle / 2;
putreg32(sda_sample - 1, I2C_SDA_SAMPLE_REG(priv->id));
setup = half_cycle;
putreg32(setup - 1, I2C_SCL_RSTART_SETUP_REG(priv->id));
putreg32(setup - 1, I2C_SCL_STOP_SETUP_REG(priv->id));
hold = half_cycle;
putreg32(hold - 1, I2C_SCL_START_HOLD_REG(priv->id));
putreg32(hold - 1, I2C_SCL_STOP_HOLD_REG(priv->id));
/* By default, we set the timeout value to about 10 bus cycles
* log(20*half_cycle)/log(2) = log(half_cycle)/log(2) + log(20)/log(2)
*/
timeout = sizeof(half_cycle) * 8;
timeout -= __builtin_clz(5 * half_cycle);
timeout += 2;
reg_value = I2C_TIME_OUT_EN;
reg_value |= VALUE_TO_FIELD(timeout, I2C_TIME_OUT_VALUE);
putreg32(reg_value, I2C_TO_REG(priv->id));
priv->clk_freq = bus_freq;
}
/****************************************************************************
* Name: i2c_init
*
* Description:
* Initialize I2C hardware.
*
* Parameters:
* priv - Pointer to the internal driver state structure.
*
****************************************************************************/
static void i2c_init(struct esp32s3_i2c_priv_s *priv)
{
const struct esp32s3_i2c_config_s *config = priv->config;
esp32s3_gpiowrite(config->scl_pin, 1);
esp32s3_configgpio(config->scl_pin, INPUT_PULLUP | OUTPUT_OPEN_DRAIN);
esp32s3_gpio_matrix_out(config->scl_pin, config->scl_outsig, 0, 0);
esp32s3_gpio_matrix_in(config->scl_pin, config->scl_insig, 0);
esp32s3_gpiowrite(config->sda_pin, 1);
esp32s3_configgpio(config->sda_pin, INPUT_PULLUP | OUTPUT_OPEN_DRAIN);
esp32s3_gpio_matrix_out(config->sda_pin, config->sda_outsig, 0, 0);
esp32s3_gpio_matrix_in(config->sda_pin, config->sda_insig, 0);
/* Enable I2C hardware */
modifyreg32(SYSTEM_PERIP_CLK_EN0_REG, 0, config->clk_bit);
modifyreg32(SYSTEM_PERIP_RST_EN0_REG, config->rst_bit, 0);
/* Disable I2C interrupts */
i2c_intr_disable(priv);
/* Initialize I2C Master */
putreg32(I2C_MS_MODE | I2C_CLK_EN | I2C_SCL_FORCE_OUT | I2C_SDA_FORCE_OUT,
I2C_CTR_REG(priv->id));
/* Set FIFO mode */
modifyreg32(I2C_FIFO_CONF_REG(priv->id), I2C_NONFIFO_EN, 0);
/* Ensure I2C data mode is set to MSB */
modifyreg32(I2C_CTR_REG(priv->id), I2C_TX_LSB_FIRST | I2C_RX_LSB_FIRST, 0);
i2c_reset_fifo(priv);
/* Configure the hardware filter function */
putreg32(I2C_SCL_FILTER_EN | I2C_SDA_FILTER_EN |
VALUE_TO_FIELD(I2C_FILTER_CYC_NUM_DEF, I2C_SCL_FILTER_THRES) |
VALUE_TO_FIELD(I2C_FILTER_CYC_NUM_DEF, I2C_SDA_FILTER_THRES),
I2C_FILTER_CFG_REG(priv->id));
/* Set I2C source clock */
modifyreg32(I2C_CLK_CONF_REG(priv->id), I2C_SCLK_SEL, 0);
/* Configure I2C bus frequency */
i2c_init_clock(priv, config->clk_freq);
/* Update I2C configuration */
modifyreg32(I2C_CTR_REG(priv->id), 0, I2C_CONF_UPGATE);
}
/****************************************************************************
* Name: i2c_deinit
*
* Description:
* Disable I2C hardware.
*
* Parameters:
* priv - Pointer to the internal driver state structure.
*
****************************************************************************/
static void i2c_deinit(struct esp32s3_i2c_priv_s *priv)
{
const struct esp32s3_i2c_config_s *config = priv->config;
priv->clk_freq = 0;
modifyreg32(SYSTEM_PERIP_RST_EN0_REG, 0, config->rst_bit);
modifyreg32(SYSTEM_PERIP_CLK_EN0_REG, config->clk_bit, 0);
}
/****************************************************************************
* Name: i2c_reset_fsmc
*
* Description:
* Reset I2C hardware state machine and registers.
*
* Parameters:
* priv - Pointer to the internal driver state structure.
*
****************************************************************************/
static void i2c_reset_fsmc(struct esp32s3_i2c_priv_s *priv)
{
/* Reset FSM machine */
modifyreg32(I2C_CTR_REG(priv->id), 0, I2C_FSM_RST);
i2c_clear_bus(priv);
}
/****************************************************************************
* Name: i2c_sem_waitdone
*
* Description:
* Wait for a transfer to complete.
*
* Parameters:
* priv - Pointer to the internal driver state structure.
*
* Returned Value:
* Zero (OK) is returned on success. A negated errno value is returned on
* failure.
*
****************************************************************************/
#ifndef CONFIG_I2C_POLLED
static int i2c_sem_waitdone(struct esp32s3_i2c_priv_s *priv)
{
/* Wait on ISR semaphore */
return nxsem_tickwait_uninterruptible(&priv->sem_isr,
ESP32S3_I2CTIMEOTICKS);
}
#endif
/****************************************************************************
* Name: i2c_polling_waitdone
*
* Description:
* Wait for a transfer to complete by polling status interrupt registers,
* which indicates the status of the I2C operations. This function is only
* used in polling driven mode.
*
* Parameters:
* priv - Pointer to the internal driver state structure.
*
* Returned Values:
* Zero (OK) is returned on successfull transfer. -ETIMEDOUT is returned
* in case a transfer didn't finish within the timeout interval. And ERROR
* is returned in case of any I2C error during the transfer has happened.
*
****************************************************************************/
#ifdef CONFIG_I2C_POLLED
static int i2c_polling_waitdone(struct esp32s3_i2c_priv_s *priv)
{
int ret;
clock_t current;
clock_t timeout;
uint32_t status = 0;
/* Get the current absolute time and add an offset as timeout.
* Preferable to use monotonic, so in case the time changes,
* the time reference is kept, i.e., current time can't jump
* forward and backwards.
*/
current = clock_systime_ticks();
timeout = current + SEC2TICK(10);
/* Loop while a transfer is in progress
* and an error didn't occur within the timeout
*/
while ((current < timeout) && (priv->error == 0))
{
/* Check if any interrupt triggered, clear them
* process the operation.
*/
status = getreg32(I2C_INT_STATUS_REG(priv->id));
if (status != 0)
{
/* Check if the stop operation ended. Don't use
* I2CSTATE_FINISH, because it is set before the stop
* signal really ends. This works for interrupts because
* the i2c_state is checked in the next interrupt when
* stop signal has concluded. This is not the case of
* polling.
*/
if ((status & I2C_TRANS_COMPLETE_INT_ST) != 0)
{
putreg32(status, I2C_INT_CLR_REG(priv->id));
break;
}
putreg32(status, I2C_INT_CLR_REG(priv->id));
i2c_process(priv, status);
}
/* Update current time */
current = clock_systime_ticks();
}
/* Return a negated value in case of timeout, and in the other scenarios
* return a positive value.
* The transfer function will check the status of priv to check the other
* scenarios.
*/
if (current >= timeout)
{
ret = -ETIMEDOUT;
}
else if (priv->error != 0)
{
ret = ERROR;
}
else
{
ret = OK;
}
/* Disable all interrupts */
i2c_intr_disable(priv);
return ret;
}
#endif
/****************************************************************************
* Name: i2c_sem_wait
*
* Description:
* Take the exclusive access, waiting as necessary.
*
* Parameters:
* priv - Pointer to the internal driver state structure.
*
* Returned Value:
* Zero (OK) is returned on success. A negated errno value is returned on
* failure.
*
****************************************************************************/
static int i2c_sem_wait(struct esp32s3_i2c_priv_s *priv)
{
return nxsem_wait_uninterruptible(&priv->sem_excl);
}
/****************************************************************************
* Name: i2c_sem_post
*
* Description:
* Release the mutual exclusion semaphore.
*
* Parameters:
* priv - Pointer to the internal driver state structure.
*
****************************************************************************/
static void i2c_sem_post(struct esp32s3_i2c_priv_s *priv)
{
nxsem_post(&priv->sem_excl);
}
/****************************************************************************
* Name: i2c_sem_destroy
*
* Description:
* Destroy semaphores.
*
* Parameters:
* priv - Pointer to the internal driver state structure.
*
****************************************************************************/
static void i2c_sem_destroy(struct esp32s3_i2c_priv_s *priv)
{
nxsem_destroy(&priv->sem_excl);
#ifndef CONFIG_I2C_POLLED
nxsem_destroy(&priv->sem_isr);
#endif
}
/****************************************************************************
* Name: i2c_sem_init
*
* Description:
* Initialize semaphores.
*
* Parameters:
* priv - Pointer to the internal driver state structure.
*
****************************************************************************/
static void i2c_sem_init(struct esp32s3_i2c_priv_s *priv)
{
nxsem_init(&priv->sem_excl, 0, 1);
/* This semaphore is used for signaling and, hence, should not have
* priority inheritance enabled.
*/
#ifndef CONFIG_I2C_POLLED
nxsem_init(&priv->sem_isr, 0, 0);
nxsem_set_protocol(&priv->sem_isr, SEM_PRIO_NONE);
#endif
}
/****************************************************************************
* Device Driver Operations
****************************************************************************/
/****************************************************************************
* Name: i2c_transfer
*
* Description:
* Generic I2C transfer function.
*
* Parameters:
* dev - Device-specific state data
* msgs - A pointer to a set of message descriptors
* count - The number of transfers to perform
*
* Returned Value:
* Zero (OK) is returned on success. A negated errno value is returned on
* failure.
*
****************************************************************************/
static int i2c_transfer(struct i2c_master_s *dev, struct i2c_msg_s *msgs,
int count)
{
int ret = OK;
struct esp32s3_i2c_priv_s *priv = (struct esp32s3_i2c_priv_s *)dev;
DEBUGASSERT(count > 0);
ret = i2c_sem_wait(priv);
if (ret < 0)
{
return ret;
}
/* If previous state is different than idle,
* reset the FSMC to the idle state.
*/
if (priv->i2cstate != I2CSTATE_IDLE)
{
i2c_reset_fsmc(priv);
priv->i2cstate = I2CSTATE_IDLE;
}
/* Transfer the messages to the internal struct
* and loop count times to make all transfers.
*/
priv->msgv = msgs;
for (int i = 0; i < count; i++)
{
/* Clear TX and RX FIFOs. */
i2c_reset_fifo(priv);
priv->bytes = 0;
priv->msgid = i;
priv->ready_read = false;
priv->error = 0;
priv->i2cstate = I2CSTATE_PROC;
/* Set the SCLK frequency for the current msg. */
i2c_init_clock(priv, msgs[i].frequency);
if ((msgs[i].flags & I2C_M_NOSTART) != 0)
{
i2c_traceevent(priv, I2CEVENT_SENDBYTE, priv->bytes,
getreg32(I2C_SR_REG(priv->id)));
i2c_senddata(priv);
if (priv->bytes == msgs[i].length)
{
if ((msgs[i].flags & I2C_M_NOSTOP) == 0)
{
priv->i2cstate = I2CSTATE_STOP;
}
#ifndef CONFIG_I2C_POLLED
else
{
priv->i2cstate = I2CSTATE_FINISH;
}
#endif
}
}
else
{
/* Reset I2C trace logic */
i2c_tracereset(priv);
i2c_traceevent(priv, I2CEVENT_SENDADDR, msgs[i].addr,
getreg32(I2C_SR_REG(priv->id)));
i2c_sendstart(priv);
}
#ifndef CONFIG_I2C_POLLED
if (i2c_sem_waitdone(priv) < 0)
{
/* Timed out - transfer was not completed within the timeout */
i2cerr("Message %" PRIu8 " timed out.\n", priv->msgid);
ret = -ETIMEDOUT;
break;
}
else
{
if (priv->error != 0)
{
/* An error occurred */
i2cerr("Transfer error %" PRIu32 "\n", priv->error);
ret = -EIO;
break;
}
else
{
/* Successful transfer, update the I2C state to idle */
priv->i2cstate = I2CSTATE_IDLE;
ret = OK;
}
}
#else
ret = i2c_polling_waitdone(priv);
if (ret < 0)
{
if (ret == -ETIMEDOUT)
{
break;
}
else
{
ret = -EIO;
break;
}
}
else
{
/* Successful transfer, update the I2C state to idle */
priv->i2cstate = I2CSTATE_IDLE;
ret = OK;
}
#endif
i2cinfo("Message %" PRIu8 " transfer complete.\n", priv->msgid);
}
/* Dump the trace result */
i2c_tracedump(priv);
i2c_sem_post(priv);
return ret;
}
/****************************************************************************
* Name: i2c_clear_bus
*
* Description:
* Clear I2C bus, when the slave is stuck in a deadlock and keeps pulling
* the bus low, master can control the SCL bus to generate 9 CLKs.
*
* Parameters:
* priv - Pointer to the internal driver state structure.
*
****************************************************************************/
static void i2c_clear_bus(struct esp32s3_i2c_priv_s *priv)
{
modifyreg32(I2C_SCL_SP_CONF_REG(priv->id),
I2C_SCL_RST_SLV_EN | I2C_SCL_RST_SLV_NUM_M,
VALUE_TO_FIELD(I2C_SCL_CYC_NUM_DEF, I2C_SCL_RST_SLV_NUM));
modifyreg32(I2C_CTR_REG(priv->id), 0, I2C_CONF_UPGATE);
modifyreg32(I2C_SCL_SP_CONF_REG(priv->id), 0, I2C_SCL_RST_SLV_EN);
}
/****************************************************************************
* Name: i2c_reset
*
* Description:
* Perform an I2C bus reset in an attempt to break loose stuck I2C devices.
*
* Input Parameters:
* dev - Device-specific state data
*
* Returned Value:
* Zero (OK) on success; a negated errno value on failure.
*
****************************************************************************/
#ifdef CONFIG_I2C_RESET
static int i2c_reset(struct i2c_master_s *dev)
{
irqstate_t flags;
struct esp32s3_i2c_priv_s *priv = (struct esp32s3_i2c_priv_s *)dev;
DEBUGASSERT(dev != NULL);
DEBUGASSERT(priv->refs > 0);
flags = spin_lock_irqsave(&priv->lock);
i2c_reset_fsmc(priv);
/* Clear bus */
i2c_clear_bus(priv);
priv->i2cstate = I2CSTATE_IDLE;
priv->msgid = 0;
priv->bytes = 0;
priv->ready_read = false;
spin_unlock_irqrestore(&priv->lock, flags);
return OK;
}
#endif
/****************************************************************************
* Name: i2c_traceclear
*
* Description:
* Set I2C trace fields to default value.
*
* Parameters:
* priv - Pointer to the internal driver state structure.
*
****************************************************************************/
#ifdef CONFIG_I2C_TRACE
static void i2c_traceclear(struct esp32s3_i2c_priv_s *priv)
{
struct esp32s3_trace_s *trace = &priv->trace[priv->tndx];
trace->status = 0;
trace->count = 0;
trace->event = I2CEVENT_NONE;
trace->parm = 0;
trace->time = 0;
}
#endif /* CONFIG_I2C_TRACE */
/****************************************************************************
* Name: i2c_tracereset
*
* Description:
* Reset the trace info for a new data collection.
*
* Parameters:
* priv - Pointer to the internal driver state structure.
*
****************************************************************************/
#ifdef CONFIG_I2C_TRACE
static void i2c_tracereset(struct esp32s3_i2c_priv_s *priv)
{
priv->tndx = 0;
priv->start_time = clock_systime_ticks();
i2c_traceclear(priv);
}
#endif /* CONFIG_I2C_TRACE */
/****************************************************************************
* Name: i2c_tracenew
*
* Description:
* Create a new trace entry.
*
* Parameters:
* priv - Pointer to the internal driver state structure.
* status - Current value of I2C status register.
*
****************************************************************************/
#ifdef CONFIG_I2C_TRACE
static void i2c_tracenew(struct esp32s3_i2c_priv_s *priv, uint32_t status)
{
struct esp32s3_trace_s *trace = &priv->trace[priv->tndx];
/* Check if the current entry is already initialized or if its status had
* already changed
*/
if (trace->count == 0 || status != trace->status)
{
/* Check whether the status changed */
if (trace->count != 0)
{
/* Bump up the trace index (unless we are out of trace entries) */
if (priv->tndx >= (CONFIG_I2C_NTRACE - 1))
{
i2cerr("ERROR: Trace table overflow\n");
return;
}
priv->tndx++;
trace = &priv->trace[priv->tndx];
}
/* Initialize the new trace entry */
i2c_traceclear(priv);
trace->status = status;
trace->count = 1;
trace->time = clock_systime_ticks();
}
else
{
/* Just increment the count of times that we have seen this status */
trace->count++;
}
}
#endif /* CONFIG_I2C_TRACE */
/****************************************************************************
* Name: i2c_traceevent
*
* Description:
* Record a new trace event.
*
* Parameters:
* priv - Pointer to the internal driver state structure.
* event - Event to be recorded on the trace.
* parm - Parameter associated with the event.
* status - Current value of I2C status register.
*
****************************************************************************/
#ifdef CONFIG_I2C_TRACE
static void i2c_traceevent(struct esp32s3_i2c_priv_s *priv,
enum esp32s3_trace_e event,
uint32_t parm,
uint32_t status)
{
/* Check for new trace setup */
i2c_tracenew(priv, status);
if (event != I2CEVENT_NONE)
{
struct esp32s3_trace_s *trace = &priv->trace[priv->tndx];
/* Initialize the new trace entry */
trace->event = event;
trace->parm = parm;
/* Bump up the trace index (unless we are out of trace entries) */
if (priv->tndx >= (CONFIG_I2C_NTRACE - 1))
{
i2cerr("ERROR: Trace table overflow\n");
return;
}
priv->tndx++;
i2c_traceclear(priv);
}
}
#endif /* CONFIG_I2C_TRACE */
/****************************************************************************
* Name: i2c_tracedump
*
* Description:
* Dump the trace results.
*
* Parameters:
* priv - Pointer to the internal driver state structure.
*
****************************************************************************/
#ifdef CONFIG_I2C_TRACE
static void i2c_tracedump(struct esp32s3_i2c_priv_s *priv)
{
syslog(LOG_DEBUG, "Elapsed time: %" PRIu32 "\n",
clock_systime_ticks() - priv->start_time);
for (int i = 0; i < priv->tndx; i++)
{
struct esp32s3_trace_s *trace = &priv->trace[i];
syslog(LOG_DEBUG,
"%2d. STATUS: %08" PRIx32 " COUNT: %3" PRIu32 " EVENT: %s(%2d)"
" PARM: %08" PRIx32 " TIME: %" PRIu32 "\n",
i + 1, trace->status, trace->count, g_trace_names[trace->event],
trace->event, trace->parm, trace->time - priv->start_time);
}
}
#endif /* CONFIG_I2C_TRACE */
/****************************************************************************
* Name: i2c_irq
*
* Description:
* This is the common I2C interrupt handler. It will be invoked when an
* interrupt is received on the device.
*
* Parameters:
* cpuint - CPU interrupt index
* context - Context data from the ISR
* arg - Opaque pointer to the internal driver state structure.
*
* Returned Value:
* Zero (OK) is returned on success. A negated errno value is returned on
* failure.
*
****************************************************************************/
#ifndef CONFIG_I2C_POLLED
static int i2c_irq(int cpuint, void *context, void *arg)
{
struct esp32s3_i2c_priv_s *priv = (struct esp32s3_i2c_priv_s *)arg;
/* Get the interrupt status and clear the interrupts that
* triggered.
*/
uint32_t irq_status = getreg32(I2C_INT_STATUS_REG(priv->id));
putreg32(irq_status, I2C_INT_CLR_REG(priv->id));
i2c_process(priv, irq_status);
return OK;
}
#endif
/****************************************************************************
* Name: i2c_process
*
* Description:
* This routine manages the transfer. It's called after some specific
* commands from the I2C controller are executed or in case of errors.
* It's responsible for writing/reading operations and transferring data
* from/to FIFO.
* It's called in the interrupt and polled driven mode.
*
* Parameters:
* priv - Pointer to the internal driver state structure.
* status - The current interrupt status register.
*
****************************************************************************/
static void i2c_process(struct esp32s3_i2c_priv_s *priv, uint32_t status)
{
struct i2c_msg_s *msg = &priv->msgv[priv->msgid];
/* Check for any errors */
if ((I2C_INT_ERR_MASK & status) != 0)
{
/* Save the errors, register the error event, disable interrupts
* and release the semaphore to conclude the transfer.
*/
priv->error = status & I2C_INT_ERR_MASK;
priv->i2cstate = I2CSTATE_ERROR;
i2c_traceevent(priv, I2CEVENT_ERROR, priv->error,
getreg32(I2C_SR_REG(priv->id)));
i2c_intr_disable(priv);
#ifndef CONFIG_I2C_POLLED
nxsem_post(&priv->sem_isr);
#endif
}
else /* If no error */
{
/* If a transfer has just initialized */
if (priv->i2cstate == I2CSTATE_PROC)
{
/* Check the flags to perform a read or write operation */
if ((msg->flags & I2C_M_READ) != 0)
{
/* RX FIFO has available data */
if (priv->ready_read)
{
i2c_traceevent(priv, I2CEVENT_RCVBYTE, priv->bytes,
getreg32(I2C_SR_REG(priv->id)));
i2c_recvdata(priv);
priv->ready_read = false;
}
/* Received all data. Finish the transaction
* and update the I2C state.
*/
if (priv->bytes == msg->length)
{
i2c_traceevent(priv, I2CEVENT_STOP, msg->length,
getreg32(I2C_SR_REG(priv->id)));
i2c_sendstop(priv);
#ifndef CONFIG_I2C_POLLED
priv->i2cstate = I2CSTATE_FINISH;
#endif
}
else /* Start a receive operation */
{
i2c_traceevent(priv, I2CEVENT_RCVMODEEN, 0,
getreg32(I2C_SR_REG(priv->id)));
i2c_startrecv(priv);
priv->ready_read = true;
}
}
else /* Write operation */
{
i2c_traceevent(priv, I2CEVENT_SENDBYTE, priv->bytes,
getreg32(I2C_SR_REG(priv->id)));
i2c_senddata(priv);
/* Finally sent the entire message. Update the I2C state to
* send a stop signal in the next interrupt.
*/
if (priv->bytes == msg->length)
{
if ((msg->flags & I2C_M_NOSTOP) == 0)
{
priv->i2cstate = I2CSTATE_STOP;
}
#ifndef CONFIG_I2C_POLLED
else
{
priv->i2cstate = I2CSTATE_FINISH;
}
#endif
}
}
}
else if (priv->i2cstate == I2CSTATE_STOP)
{
/* Transmitted all data. Finish the transaction sending a stop
* and update the I2C state.
*/
i2c_traceevent(priv, I2CEVENT_STOP, msg->length,
getreg32(I2C_SR_REG(priv->id)));
i2c_sendstop(priv);
#ifndef CONFIG_I2C_POLLED
priv->i2cstate = I2CSTATE_FINISH;
#endif
}
#ifndef CONFIG_I2C_POLLED
else if (priv->i2cstate == I2CSTATE_FINISH)
{
/* Disable all interrupts and release the semaphore */
i2c_intr_disable(priv);
nxsem_post(&priv->sem_isr);
}
#endif
}
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: esp32s3_i2cbus_initialize
*
* Description:
* Initialize the selected I2C port. And return a pointer to an unique
* instance of struct i2c_master_s. This function may be called to obtain
* multiple instances of the interface, each of which may be set up with a
* different frequency and slave address.
*
* Parameters:
* port - Port number of the I2C interface to be initialized.
*
* Returned Value:
* Pointer to valid I2C device structure is returned on success.
* A NULL pointer is returned on failure.
*
****************************************************************************/
struct i2c_master_s *esp32s3_i2cbus_initialize(int port)
{
irqstate_t flags;
struct esp32s3_i2c_priv_s *priv;
#ifndef CONFIG_I2C_POLLED
const struct esp32s3_i2c_config_s *config;
int ret;
#endif
switch (port)
{
#ifdef CONFIG_ESP32S3_I2C0
case ESP32S3_I2C0:
priv = &g_esp32s3_i2c0_priv;
break;
#endif
#ifdef CONFIG_ESP32S3_I2C1
case ESP32S3_I2C1:
priv = &g_esp32s3_i2c1_priv;
break;
#endif
default:
return NULL;
}
flags = spin_lock_irqsave(&priv->lock);
if (priv->refs++ != 0)
{
spin_unlock_irqrestore(&priv->lock, flags);
return (struct i2c_master_s *)priv;
}
#ifndef CONFIG_I2C_POLLED
config = priv->config;
/* Set up to receive peripheral interrupts on the current CPU */
priv->cpu = up_cpu_index();
priv->cpuint = esp32s3_setup_irq(priv->cpu, config->periph,
1, ESP32S3_CPUINT_LEVEL);
if (priv->cpuint < 0)
{
/* Failed to allocate a CPU interrupt of this type */
spin_unlock_irqrestore(&priv->lock, flags);
return NULL;
}
ret = irq_attach(config->irq, i2c_irq, priv);
if (ret != OK)
{
esp32s3_teardown_irq(priv->cpu, config->periph, priv->cpuint);
spin_unlock_irqrestore(&priv->lock, flags);
return NULL;
}
up_enable_irq(config->irq);
#endif
i2c_sem_init(priv);
i2c_init(priv);
spin_unlock_irqrestore(&priv->lock, flags);
return (struct i2c_master_s *)priv;
}
/****************************************************************************
* Name: esp32s3_i2cbus_uninitialize
*
* Description:
* De-initialize the selected I2C port and power down the device.
*
* Parameters:
* dev - Device structure as returned by
* esp32s3_i2cbus_initialize()
*
* Returned Value:
* OK is returned on success. ERROR is returned when internal reference
* count mismatches or dev points to invalid hardware device.
*
****************************************************************************/
int esp32s3_i2cbus_uninitialize(struct i2c_master_s *dev)
{
irqstate_t flags;
struct esp32s3_i2c_priv_s *priv = (struct esp32s3_i2c_priv_s *)dev;
DEBUGASSERT(dev != NULL);
if (priv->refs == 0)
{
return ERROR;
}
flags = spin_lock_irqsave(&priv->lock);
if (--priv->refs)
{
spin_unlock_irqrestore(&priv->lock, flags);
return OK;
}
spin_unlock_irqrestore(&priv->lock, flags);
#ifndef CONFIG_I2C_POLLED
up_disable_irq(priv->config->irq);
esp32s3_teardown_irq(priv->cpu, priv->config->periph, priv->cpuint);
#endif
i2c_deinit(priv);
i2c_sem_destroy(priv);
return OK;
}
#endif /* CONFIG_ESP32S3_I2C */