risc-v/esp32-c3: Adds I2C polled support
This commit is contained in:
parent
bec68ad8ea
commit
2a7b97c0cd
@ -35,6 +35,7 @@
|
||||
#include <errno.h>
|
||||
#include <debug.h>
|
||||
#include <time.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#include <nuttx/arch.h>
|
||||
#include <nuttx/irq.h>
|
||||
@ -78,6 +79,12 @@
|
||||
((_ack_val) << 10) + \
|
||||
(_bytes))
|
||||
|
||||
/* Helper */
|
||||
|
||||
#ifdef CONFIG_I2C_POLLED
|
||||
#define TIMESPEC_TO_US(sec, nano) ((sec * USEC_PER_SEC) + (nano / NSEC_PER_USEC))
|
||||
#endif
|
||||
|
||||
/* I2C hardware FIFO depth */
|
||||
|
||||
#define I2C_FIFO_SIZE (32)
|
||||
@ -129,7 +136,9 @@ enum esp32c3_i2cstate_e
|
||||
I2CSTATE_IDLE = 0,
|
||||
I2CSTATE_PROC,
|
||||
I2CSTATE_STOP,
|
||||
#ifndef CONFIG_I2C_POLLED
|
||||
I2CSTATE_FINISH,
|
||||
#endif
|
||||
I2CSTATE_ERROR
|
||||
};
|
||||
|
||||
@ -181,8 +190,10 @@ struct esp32c3_i2c_config_s
|
||||
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 */
|
||||
@ -207,7 +218,10 @@ struct esp32c3_i2c_priv_s
|
||||
const struct esp32c3_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 */
|
||||
#endif
|
||||
|
||||
/* I2C work state (see enum esp32c3_i2cstate_e) */
|
||||
|
||||
@ -218,7 +232,9 @@ struct esp32c3_i2c_priv_s
|
||||
uint8_t msgid; /* Current message ID */
|
||||
ssize_t bytes; /* Processed data bytes */
|
||||
|
||||
#ifndef CONFIG_I2C_POLLED
|
||||
int cpuint; /* CPU interrupt assigned to this I2C */
|
||||
#endif
|
||||
|
||||
uint32_t error; /* I2C transform error */
|
||||
|
||||
@ -249,6 +265,12 @@ static void esp32c3_i2c_deinit(struct esp32c3_i2c_priv_s *priv);
|
||||
static int esp32c3_i2c_transfer(struct i2c_master_s *dev,
|
||||
struct i2c_msg_s *msgs,
|
||||
int count);
|
||||
static inline void esp32c3_i2c_process(struct esp32c3_i2c_priv_s *priv,
|
||||
uint32_t status);
|
||||
#ifdef CONFIG_I2C_POLLED
|
||||
static int esp32c3_i2c_polling_waitdone(FAR struct esp32c3_i2c_priv_s *priv);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_I2C_RESET
|
||||
static int esp32c3_i2c_reset(struct i2c_master_s *dev);
|
||||
#endif /* CONFIG_I2C_RESET */
|
||||
@ -287,8 +309,10 @@ static const struct esp32c3_i2c_config_s esp32c3_i2c0_config =
|
||||
.clk_freq = I2C_CLK_FREQ_DEF,
|
||||
.scl_pin = CONFIG_ESP32C3_I2C0_SCLPIN,
|
||||
.sda_pin = CONFIG_ESP32C3_I2C0_SDAPIN,
|
||||
#ifndef CONFIG_I2C_POLLED
|
||||
.periph = ESP32C3_PERIPH_I2C_EXT0,
|
||||
.irq = ESP32C3_IRQ_I2C_EXT0,
|
||||
#endif
|
||||
.clk_bit = SYSTEM_I2C_EXT0_CLK_EN,
|
||||
.rst_bit = SYSTEM_I2C_EXT0_RST,
|
||||
.scl_insig = I2CEXT0_SCL_IN_IDX,
|
||||
@ -307,7 +331,9 @@ static struct esp32c3_i2c_priv_s esp32c3_i2c0_priv =
|
||||
.msgv = NULL,
|
||||
.msgid = 0,
|
||||
.bytes = 0,
|
||||
#ifndef CONFIG_I2C_POLLED
|
||||
.cpuint = -ENOMEM,
|
||||
#endif
|
||||
.error = 0,
|
||||
.ready_read = false,
|
||||
.clk_freq = 0
|
||||
@ -474,7 +500,7 @@ static void esp32c3_i2c_senddata(struct esp32c3_i2c_priv_s *priv)
|
||||
* Name: esp32c3_i2c_recvdata
|
||||
*
|
||||
* Description:
|
||||
* Receive I2C data.
|
||||
* Transfer data from the FIFO to the driver buffer.
|
||||
*
|
||||
* Parameters:
|
||||
* priv - Pointer to the internal driver state structure.
|
||||
@ -765,7 +791,7 @@ static void esp32c3_i2c_reset_fsmc(struct esp32c3_i2c_priv_s *priv)
|
||||
* failure.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef CONFIG_I2C_POLLED
|
||||
static int esp32c3_i2c_sem_waitdone(struct esp32c3_i2c_priv_s *priv)
|
||||
{
|
||||
int ret;
|
||||
@ -780,6 +806,120 @@ static int esp32c3_i2c_sem_waitdone(struct esp32c3_i2c_priv_s *priv)
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
* Name: esp32c3_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 esp32c3_i2c_polling_waitdone(FAR struct esp32c3_i2c_priv_s *priv)
|
||||
{
|
||||
int ret;
|
||||
struct timespec current_time;
|
||||
struct timespec timeout;
|
||||
uint64_t current_us;
|
||||
uint64_t timeout_us;
|
||||
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.
|
||||
*/
|
||||
|
||||
#ifdef CONFIG_CLOCK_MONOTONIC
|
||||
clock_gettime(CLOCK_MONOTONIC, ¤t_time);
|
||||
#else
|
||||
clock_gettime(CLOCK_REALTIME, ¤t_time);
|
||||
#endif
|
||||
|
||||
timeout.tv_sec = current_time.tv_sec + 10;
|
||||
timeout.tv_nsec = current_time.tv_nsec + 0;
|
||||
|
||||
current_us = TIMESPEC_TO_US(current_time.tv_sec, current_time.tv_nsec);
|
||||
timeout_us = TIMESPEC_TO_US(timeout.tv_sec, timeout.tv_nsec);
|
||||
|
||||
/* Loop while a transfer is in progress
|
||||
* and an error didn't occur within the timeout
|
||||
*/
|
||||
|
||||
while ((current_us < timeout_us) && (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_M)
|
||||
{
|
||||
putreg32(status, I2C_INT_CLR_REG(priv->id));
|
||||
break;
|
||||
}
|
||||
|
||||
putreg32(status, I2C_INT_CLR_REG(priv->id));
|
||||
esp32c3_i2c_process(priv, status);
|
||||
}
|
||||
|
||||
/* Update current time */
|
||||
|
||||
#ifdef CONFIG_CLOCK_MONOTONIC
|
||||
clock_gettime(CLOCK_MONOTONIC, ¤t_time);
|
||||
#else
|
||||
clock_gettime(CLOCK_REALTIME, ¤t_time);
|
||||
#endif
|
||||
current_us = TIMESPEC_TO_US(current_time.tv_sec, current_time.tv_nsec);
|
||||
}
|
||||
|
||||
/* 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_us >= timeout_us)
|
||||
{
|
||||
ret = -ETIMEDOUT;
|
||||
}
|
||||
else if (priv->error)
|
||||
{
|
||||
ret = ERROR;
|
||||
}
|
||||
else
|
||||
{
|
||||
ret = OK;
|
||||
}
|
||||
|
||||
/* Disable all interrupts */
|
||||
|
||||
esp32c3_i2c_intr_disable(priv);
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
* Name: esp32c3_i2c_sem_wait
|
||||
@ -831,7 +971,9 @@ static void esp32c3_i2c_sem_post(struct esp32c3_i2c_priv_s *priv)
|
||||
static void esp32c3_i2c_sem_destroy(struct esp32c3_i2c_priv_s *priv)
|
||||
{
|
||||
nxsem_destroy(&priv->sem_excl);
|
||||
#ifndef CONFIG_I2C_POLLED
|
||||
nxsem_destroy(&priv->sem_isr);
|
||||
#endif
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
@ -852,9 +994,10 @@ static inline void esp32c3_i2c_sem_init(struct esp32c3_i2c_priv_s *priv)
|
||||
/* 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
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
@ -926,9 +1069,10 @@ static int esp32c3_i2c_transfer(struct i2c_master_s *dev,
|
||||
|
||||
esp32c3_i2c_sendstart(priv);
|
||||
|
||||
#ifndef CONFIG_I2C_POLLED
|
||||
if (esp32c3_i2c_sem_waitdone(priv) < 0)
|
||||
{
|
||||
i2cinfo("Message %" PRIu8 " timed out.\n", priv->msgid);
|
||||
i2cerr("Message %" PRIu8 " timed out.\n", priv->msgid);
|
||||
ret = -ETIMEDOUT;
|
||||
break;
|
||||
}
|
||||
@ -936,7 +1080,7 @@ static int esp32c3_i2c_transfer(struct i2c_master_s *dev,
|
||||
{
|
||||
if (priv->error != 0)
|
||||
{
|
||||
i2cinfo("Transfer error %" PRIu32 "\n", priv->error);
|
||||
i2cerr("Transfer error %" PRIu32 "\n", priv->error);
|
||||
ret = -EIO;
|
||||
break;
|
||||
}
|
||||
@ -946,6 +1090,28 @@ static int esp32c3_i2c_transfer(struct i2c_master_s *dev,
|
||||
ret = OK;
|
||||
}
|
||||
}
|
||||
#else
|
||||
ret = esp32c3_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);
|
||||
}
|
||||
@ -1294,14 +1460,40 @@ static void esp32c3_i2c_tracedump(struct esp32c3_i2c_priv_s *priv)
|
||||
* failure.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef CONFIG_I2C_POLLED
|
||||
static int esp32c3_i2c_irq(int cpuint, void *context, void *arg)
|
||||
{
|
||||
struct esp32c3_i2c_priv_s *priv = (struct esp32c3_i2c_priv_s *)arg;
|
||||
uint32_t irq_status = getreg32(I2C_INT_STATUS_REG(priv->id));
|
||||
|
||||
putreg32(irq_status, I2C_INT_CLR_REG(priv->id));
|
||||
esp32c3_i2c_process(priv , irq_status);
|
||||
return OK;
|
||||
}
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
* Name: esp32c3_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 inline void esp32c3_i2c_process(struct esp32c3_i2c_priv_s *priv,
|
||||
uint32_t irq_status)
|
||||
{
|
||||
struct i2c_msg_s *msg = &priv->msgv[priv->msgid];
|
||||
|
||||
uint32_t irq_status = getreg32(I2C_INT_STATUS_REG(priv->id));
|
||||
putreg32(irq_status, I2C_INT_CLR_REG(priv->id));
|
||||
/* Check for any errors */
|
||||
|
||||
if (I2C_INT_ERR_MASK & irq_status)
|
||||
{
|
||||
@ -1310,7 +1502,9 @@ static int esp32c3_i2c_irq(int cpuint, void *context, void *arg)
|
||||
esp32c3_i2c_traceevent(priv, I2CEVENT_ERROR, priv->error,
|
||||
getreg32(I2C_SR_REG(priv->id)));
|
||||
esp32c3_i2c_intr_disable(priv);
|
||||
#ifndef CONFIG_I2C_POLLED
|
||||
nxsem_post(&priv->sem_isr);
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -1332,8 +1526,9 @@ static int esp32c3_i2c_irq(int cpuint, void *context, void *arg)
|
||||
esp32c3_i2c_traceevent(priv, I2CEVENT_STOP, msg->length,
|
||||
getreg32(I2C_SR_REG(priv->id)));
|
||||
esp32c3_i2c_sendstop(priv);
|
||||
|
||||
#ifndef CONFIG_I2C_POLLED
|
||||
priv->i2cstate = I2CSTATE_FINISH;
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -1362,15 +1557,18 @@ static int esp32c3_i2c_irq(int cpuint, void *context, void *arg)
|
||||
getreg32(I2C_SR_REG(priv->id)));
|
||||
esp32c3_i2c_sendstop(priv);
|
||||
|
||||
#ifndef CONFIG_I2C_POLLED
|
||||
priv->i2cstate = I2CSTATE_FINISH;
|
||||
#endif
|
||||
}
|
||||
#ifndef CONFIG_I2C_POLLED
|
||||
else if (priv->i2cstate == I2CSTATE_FINISH)
|
||||
{
|
||||
esp32c3_i2c_intr_disable(priv);
|
||||
nxsem_post(&priv->sem_isr);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
@ -1397,10 +1595,12 @@ static int esp32c3_i2c_irq(int cpuint, void *context, void *arg)
|
||||
|
||||
struct i2c_master_s *esp32c3_i2cbus_initialize(int port)
|
||||
{
|
||||
int ret;
|
||||
irqstate_t flags;
|
||||
struct esp32c3_i2c_priv_s *priv;
|
||||
#ifndef CONFIG_I2C_POLLED
|
||||
const struct esp32c3_i2c_config_s *config;
|
||||
int ret;
|
||||
#endif
|
||||
|
||||
switch (port)
|
||||
{
|
||||
@ -1413,8 +1613,6 @@ struct i2c_master_s *esp32c3_i2cbus_initialize(int port)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
config = priv->config;
|
||||
|
||||
flags = enter_critical_section();
|
||||
|
||||
if ((volatile int)priv->refs++ != 0)
|
||||
@ -1427,6 +1625,8 @@ struct i2c_master_s *esp32c3_i2cbus_initialize(int port)
|
||||
return (struct i2c_master_s *)priv;
|
||||
}
|
||||
|
||||
#ifndef CONFIG_I2C_POLLED
|
||||
config = priv->config;
|
||||
if (priv->cpuint != -ENOMEM)
|
||||
{
|
||||
/* Disable the provided CPU Interrupt to configure it. */
|
||||
@ -1461,6 +1661,7 @@ struct i2c_master_s *esp32c3_i2cbus_initialize(int port)
|
||||
/* Enable the CPU interrupt that is linked to the I2C device. */
|
||||
|
||||
up_enable_irq(priv->cpuint);
|
||||
#endif
|
||||
|
||||
esp32c3_i2c_sem_init(priv);
|
||||
|
||||
@ -1511,9 +1712,11 @@ int esp32c3_i2cbus_uninitialize(struct i2c_master_s *dev)
|
||||
|
||||
leave_critical_section(flags);
|
||||
|
||||
#ifndef CONFIG_I2C_POLLED
|
||||
up_disable_irq(priv->cpuint);
|
||||
esp32c3_free_cpuint(priv->config->periph);
|
||||
priv->cpuint = -ENOMEM;
|
||||
#endif
|
||||
|
||||
esp32c3_i2c_deinit(priv);
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user