esp32: drivers: i2c: fix command sequence
Closes #45008 Signed-off-by: Lucas Tamborrino <lucas.tamborrino@espressif.com>
This commit is contained in:
parent
2ef3fe7bca
commit
092406b08d
1 changed files with 141 additions and 103 deletions
|
@ -299,13 +299,14 @@ static void IRAM_ATTR i2c_esp32_reset_fifo(const struct device *dev)
|
||||||
static int IRAM_ATTR i2c_esp32_transmit(const struct device *dev)
|
static int IRAM_ATTR i2c_esp32_transmit(const struct device *dev)
|
||||||
{
|
{
|
||||||
struct i2c_esp32_data *data = (struct i2c_esp32_data *const)(dev)->data;
|
struct i2c_esp32_data *data = (struct i2c_esp32_data *const)(dev)->data;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
/* start the transfer */
|
/* Start transmission*/
|
||||||
i2c_hal_update_config(&data->hal);
|
i2c_hal_update_config(&data->hal);
|
||||||
i2c_hal_trans_start(&data->hal);
|
i2c_hal_trans_start(&data->hal);
|
||||||
|
data->cmd_idx = 0;
|
||||||
|
|
||||||
int ret = k_sem_take(&data->cmd_sem, K_MSEC(I2C_TRANSFER_TIMEOUT_MSEC));
|
ret = k_sem_take(&data->cmd_sem, K_MSEC(I2C_TRANSFER_TIMEOUT_MSEC));
|
||||||
|
|
||||||
if (ret != 0) {
|
if (ret != 0) {
|
||||||
/* If the I2C slave is powered off or the SDA/SCL is */
|
/* If the I2C slave is powered off or the SDA/SCL is */
|
||||||
/* connected to ground, for example, I2C hw FSM would get */
|
/* connected to ground, for example, I2C hw FSM would get */
|
||||||
|
@ -319,25 +320,41 @@ static int IRAM_ATTR i2c_esp32_transmit(const struct device *dev)
|
||||||
ret = -ETIMEDOUT;
|
ret = -ETIMEDOUT;
|
||||||
} else if (data->status == I2C_STATUS_ACK_ERROR) {
|
} else if (data->status == I2C_STATUS_ACK_ERROR) {
|
||||||
ret = -EFAULT;
|
ret = -EFAULT;
|
||||||
} else {
|
|
||||||
ret = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void IRAM_ATTR i2c_esp32_write_addr(const struct device *dev, uint16_t addr)
|
|
||||||
|
static void IRAM_ATTR i2c_esp32_master_start(const struct device *dev)
|
||||||
{
|
{
|
||||||
struct i2c_esp32_data *data = (struct i2c_esp32_data *const)(dev)->data;
|
struct i2c_esp32_data *data = (struct i2c_esp32_data *const)(dev)->data;
|
||||||
uint8_t addr_len = 1;
|
|
||||||
uint8_t addr_byte = addr & 0xFF;
|
|
||||||
|
|
||||||
i2c_hw_cmd_t cmd = {
|
i2c_hw_cmd_t cmd = {
|
||||||
.op_code = I2C_LL_CMD_RESTART
|
.op_code = I2C_LL_CMD_RESTART
|
||||||
};
|
};
|
||||||
|
|
||||||
/* write re-start command */
|
|
||||||
i2c_hal_write_cmd_reg(&data->hal, cmd, data->cmd_idx++);
|
i2c_hal_write_cmd_reg(&data->hal, cmd, data->cmd_idx++);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void IRAM_ATTR i2c_esp32_master_stop(const struct device *dev)
|
||||||
|
{
|
||||||
|
struct i2c_esp32_data *data = (struct i2c_esp32_data *const)(dev)->data;
|
||||||
|
|
||||||
|
i2c_hw_cmd_t cmd = {
|
||||||
|
.op_code = I2C_LL_CMD_STOP
|
||||||
|
};
|
||||||
|
|
||||||
|
i2c_hal_write_cmd_reg(&data->hal, cmd, data->cmd_idx++);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int IRAM_ATTR i2c_esp32_write_addr(const struct device *dev, uint16_t addr)
|
||||||
|
{
|
||||||
|
struct i2c_esp32_data *data = (struct i2c_esp32_data *const)(dev)->data;
|
||||||
|
uint8_t addr_len = 1;
|
||||||
|
uint8_t addr_byte = addr & 0xFF;
|
||||||
|
|
||||||
|
data->status = I2C_STATUS_WRITE;
|
||||||
|
|
||||||
/* write address value in tx buffer */
|
/* write address value in tx buffer */
|
||||||
i2c_hal_write_txfifo(&data->hal, &addr_byte, 1);
|
i2c_hal_write_txfifo(&data->hal, &addr_byte, 1);
|
||||||
|
@ -347,57 +364,60 @@ static void IRAM_ATTR i2c_esp32_write_addr(const struct device *dev, uint16_t ad
|
||||||
addr_len++;
|
addr_len++;
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd = (i2c_hw_cmd_t) {
|
const i2c_hw_cmd_t cmd_end = {
|
||||||
|
.op_code = I2C_LL_CMD_END,
|
||||||
|
};
|
||||||
|
|
||||||
|
i2c_hw_cmd_t cmd = {
|
||||||
.op_code = I2C_LL_CMD_WRITE,
|
.op_code = I2C_LL_CMD_WRITE,
|
||||||
.ack_en = true,
|
.ack_en = true,
|
||||||
.byte_num = addr_len,
|
.byte_num = addr_len,
|
||||||
};
|
};
|
||||||
|
|
||||||
i2c_hal_write_cmd_reg(&data->hal, cmd, data->cmd_idx++);
|
i2c_hal_write_cmd_reg(&data->hal, cmd, data->cmd_idx++);
|
||||||
|
i2c_hal_write_cmd_reg(&data->hal, cmd_end, data->cmd_idx++);
|
||||||
|
i2c_hal_enable_master_tx_it(&data->hal);
|
||||||
|
|
||||||
|
return i2c_esp32_transmit(dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int IRAM_ATTR i2c_esp32_read_msg(const struct device *dev,
|
static int IRAM_ATTR i2c_esp32_master_read(const struct device *dev, struct i2c_msg *msg)
|
||||||
struct i2c_msg *msg, uint16_t addr)
|
|
||||||
{
|
{
|
||||||
struct i2c_esp32_data *data = (struct i2c_esp32_data *const)(dev)->data;
|
struct i2c_esp32_data *data = (struct i2c_esp32_data *const)(dev)->data;
|
||||||
|
|
||||||
uint8_t rd_filled = 0;
|
uint8_t rd_filled = 0;
|
||||||
uint8_t *read_pr = NULL;
|
uint8_t *read_pr = NULL;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
/* reset command index and set status as read operation */
|
|
||||||
data->cmd_idx = 0;
|
|
||||||
data->status = I2C_STATUS_READ;
|
data->status = I2C_STATUS_READ;
|
||||||
|
|
||||||
i2c_hw_cmd_t cmd = {
|
i2c_hw_cmd_t cmd = {
|
||||||
.op_code = I2C_LL_CMD_READ
|
.op_code = I2C_LL_CMD_READ,
|
||||||
};
|
};
|
||||||
|
const i2c_hw_cmd_t cmd_end = {
|
||||||
i2c_hw_cmd_t hw_end_cmd = {
|
|
||||||
.op_code = I2C_LL_CMD_END,
|
.op_code = I2C_LL_CMD_END,
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Set the R/W bit to R */
|
|
||||||
addr |= BIT(0);
|
|
||||||
|
|
||||||
if (msg->flags & I2C_MSG_RESTART) {
|
|
||||||
/* write restart command and address */
|
|
||||||
i2c_esp32_write_addr(dev, addr);
|
|
||||||
}
|
|
||||||
|
|
||||||
while (msg->len) {
|
while (msg->len) {
|
||||||
rd_filled = (msg->len > SOC_I2C_FIFO_LEN) ? SOC_I2C_FIFO_LEN : (msg->len - 1);
|
rd_filled = (msg->len > SOC_I2C_FIFO_LEN) ? SOC_I2C_FIFO_LEN : (msg->len - 1);
|
||||||
|
|
||||||
read_pr = msg->buf;
|
read_pr = msg->buf;
|
||||||
|
msg->buf += rd_filled;
|
||||||
msg->len -= rd_filled;
|
msg->len -= rd_filled;
|
||||||
|
|
||||||
if (rd_filled) {
|
if (rd_filled) {
|
||||||
cmd = (i2c_hw_cmd_t) {
|
cmd.ack_val = 0,
|
||||||
.op_code = I2C_LL_CMD_READ,
|
cmd.byte_num = rd_filled;
|
||||||
.ack_en = false,
|
|
||||||
.ack_val = 0,
|
|
||||||
.byte_num = rd_filled
|
|
||||||
};
|
|
||||||
|
|
||||||
i2c_hal_write_cmd_reg(&data->hal, cmd, data->cmd_idx++);
|
i2c_hal_write_cmd_reg(&data->hal, cmd, data->cmd_idx++);
|
||||||
|
i2c_hal_write_cmd_reg(&data->hal, cmd_end, data->cmd_idx++);
|
||||||
|
i2c_hal_enable_master_rx_it(&data->hal);
|
||||||
|
ret = i2c_esp32_transmit(dev);
|
||||||
|
if (ret < 0) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
i2c_hal_read_rxfifo(&data->hal, read_pr, rd_filled);
|
||||||
|
msg->buf += rd_filled;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* I2C master won't acknowledge the last byte read from the
|
/* I2C master won't acknowledge the last byte read from the
|
||||||
|
@ -405,40 +425,98 @@ static int IRAM_ATTR i2c_esp32_read_msg(const struct device *dev,
|
||||||
* recommended by the ESP32 Technical Reference Manual.
|
* recommended by the ESP32 Technical Reference Manual.
|
||||||
*/
|
*/
|
||||||
if (msg->len == 1) {
|
if (msg->len == 1) {
|
||||||
cmd = (i2c_hw_cmd_t) {
|
cmd.ack_val = 1,
|
||||||
.op_code = I2C_LL_CMD_READ,
|
cmd.byte_num = 1,
|
||||||
.byte_num = 1,
|
|
||||||
.ack_val = 1,
|
|
||||||
};
|
|
||||||
msg->len = 0;
|
msg->len = 0;
|
||||||
rd_filled++;
|
rd_filled++;
|
||||||
|
|
||||||
i2c_hal_write_cmd_reg(&data->hal, cmd, data->cmd_idx++);
|
i2c_hal_write_cmd_reg(&data->hal, cmd, data->cmd_idx++);
|
||||||
|
i2c_hal_write_cmd_reg(&data->hal, cmd_end, data->cmd_idx++);
|
||||||
|
i2c_hal_enable_master_rx_it(&data->hal);
|
||||||
|
ret = i2c_esp32_transmit(dev);
|
||||||
|
if (ret < 0) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
i2c_hal_read_rxfifo(&data->hal, read_pr, rd_filled);
|
||||||
|
msg->buf += rd_filled;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
|
||||||
if (msg->len == 0) {
|
}
|
||||||
cmd = (i2c_hw_cmd_t) {
|
|
||||||
.op_code = I2C_LL_CMD_STOP,
|
static int IRAM_ATTR i2c_esp32_read_msg(const struct device *dev,
|
||||||
.byte_num = 0,
|
struct i2c_msg *msg, uint16_t addr)
|
||||||
.ack_val = 0,
|
{
|
||||||
.ack_en = 0
|
int ret = 0;
|
||||||
};
|
struct i2c_esp32_data *data = (struct i2c_esp32_data *const)(dev)->data;
|
||||||
i2c_hal_write_cmd_reg(&data->hal, cmd, data->cmd_idx++);
|
|
||||||
|
/* Set the R/W bit to R */
|
||||||
|
addr |= BIT(0);
|
||||||
|
|
||||||
|
if (msg->flags & I2C_MSG_RESTART) {
|
||||||
|
i2c_esp32_master_start(dev);
|
||||||
|
ret = i2c_esp32_write_addr(dev, addr);
|
||||||
|
if (ret < 0) {
|
||||||
|
LOG_ERR("I2C transfer error: %d", ret);
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
i2c_hal_write_cmd_reg(&data->hal, hw_end_cmd, data->cmd_idx++);
|
ret = i2c_esp32_master_read(dev, msg);
|
||||||
i2c_hal_enable_master_rx_it(&data->hal);
|
if (ret < 0) {
|
||||||
|
LOG_ERR("I2C transfer error: %d", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (msg->flags & I2C_MSG_STOP) {
|
||||||
|
i2c_esp32_master_stop(dev);
|
||||||
ret = i2c_esp32_transmit(dev);
|
ret = i2c_esp32_transmit(dev);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
LOG_ERR("I2C transfer error: %d", ret);
|
LOG_ERR("I2C transfer error: %d", ret);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
i2c_hal_read_rxfifo(&data->hal, msg->buf, rd_filled);
|
return 0;
|
||||||
msg->buf += rd_filled;
|
}
|
||||||
|
|
||||||
/* reset fifo read pointer */
|
static int IRAM_ATTR i2c_esp32_master_write(const struct device *dev, struct i2c_msg *msg)
|
||||||
data->cmd_idx = 0;
|
{
|
||||||
|
struct i2c_esp32_data *data = (struct i2c_esp32_data *const)(dev)->data;
|
||||||
|
uint8_t wr_filled = 0;
|
||||||
|
uint8_t *write_pr = NULL;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
data->status = I2C_STATUS_WRITE;
|
||||||
|
|
||||||
|
i2c_hw_cmd_t cmd = {
|
||||||
|
.op_code = I2C_LL_CMD_WRITE,
|
||||||
|
.ack_en = true,
|
||||||
|
};
|
||||||
|
|
||||||
|
const i2c_hw_cmd_t cmd_end = {
|
||||||
|
.op_code = I2C_LL_CMD_END,
|
||||||
|
};
|
||||||
|
|
||||||
|
while (msg->len) {
|
||||||
|
wr_filled = (msg->len > SOC_I2C_FIFO_LEN) ? SOC_I2C_FIFO_LEN : msg->len;
|
||||||
|
|
||||||
|
write_pr = msg->buf;
|
||||||
|
msg->buf += wr_filled;
|
||||||
|
msg->len -= wr_filled;
|
||||||
|
cmd.byte_num = wr_filled;
|
||||||
|
|
||||||
|
if (wr_filled > 0) {
|
||||||
|
i2c_hal_write_txfifo(&data->hal, write_pr, wr_filled);
|
||||||
|
i2c_hal_write_cmd_reg(&data->hal, cmd, data->cmd_idx++);
|
||||||
|
i2c_hal_write_cmd_reg(&data->hal, cmd_end, data->cmd_idx++);
|
||||||
|
i2c_hal_enable_master_tx_it(&data->hal);
|
||||||
|
ret = i2c_esp32_transmit(dev);
|
||||||
|
if (ret < 0) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -448,65 +526,25 @@ static int IRAM_ATTR i2c_esp32_write_msg(const struct device *dev,
|
||||||
struct i2c_msg *msg, uint16_t addr)
|
struct i2c_msg *msg, uint16_t addr)
|
||||||
{
|
{
|
||||||
struct i2c_esp32_data *data = (struct i2c_esp32_data *const)(dev)->data;
|
struct i2c_esp32_data *data = (struct i2c_esp32_data *const)(dev)->data;
|
||||||
uint8_t wr_filled = 0;
|
|
||||||
uint8_t *write_pr = NULL;
|
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
/* reset command index and set status as write operation */
|
|
||||||
data->cmd_idx = 0;
|
|
||||||
data->status = I2C_STATUS_WRITE;
|
|
||||||
|
|
||||||
i2c_hw_cmd_t cmd = {
|
|
||||||
.op_code = I2C_LL_CMD_WRITE,
|
|
||||||
.ack_en = true
|
|
||||||
};
|
|
||||||
|
|
||||||
i2c_hw_cmd_t hw_end_cmd = {
|
|
||||||
.op_code = I2C_LL_CMD_END,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (msg->flags & I2C_MSG_RESTART) {
|
if (msg->flags & I2C_MSG_RESTART) {
|
||||||
/* write restart command and address */
|
i2c_esp32_master_start(dev);
|
||||||
i2c_esp32_write_addr(dev, addr);
|
ret = i2c_esp32_write_addr(dev, addr);
|
||||||
}
|
if (ret < 0) {
|
||||||
|
LOG_ERR("I2C transfer error: %d", ret);
|
||||||
for (;;) {
|
return ret;
|
||||||
wr_filled = (msg->len > SOC_I2C_FIFO_LEN) ? SOC_I2C_FIFO_LEN : msg->len;
|
|
||||||
write_pr = msg->buf;
|
|
||||||
msg->buf += wr_filled;
|
|
||||||
msg->len -= wr_filled;
|
|
||||||
cmd.byte_num = wr_filled;
|
|
||||||
|
|
||||||
if (wr_filled > 0) {
|
|
||||||
i2c_hal_write_txfifo(&data->hal, write_pr, wr_filled);
|
|
||||||
i2c_hal_write_cmd_reg(&data->hal, cmd, data->cmd_idx++);
|
|
||||||
i2c_hal_write_cmd_reg(&data->hal, hw_end_cmd, data->cmd_idx++);
|
|
||||||
|
|
||||||
i2c_hal_enable_master_tx_it(&data->hal);
|
|
||||||
|
|
||||||
ret = i2c_esp32_transmit(dev);
|
|
||||||
if (ret < 0) {
|
|
||||||
LOG_ERR("I2C transfer error: %d", ret);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
data->cmd_idx = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (msg->len == 0) {
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* add stop command in a new transmission */
|
ret = i2c_esp32_master_write(dev, msg);
|
||||||
if (msg->len == 0 && (msg->flags & I2C_MSG_STOP)) {
|
if (ret < 0) {
|
||||||
cmd = (i2c_hw_cmd_t) {
|
LOG_ERR("I2C transfer error: %d", ret);
|
||||||
.op_code = I2C_LL_CMD_STOP,
|
return ret;
|
||||||
.ack_en = false,
|
}
|
||||||
.byte_num = 0
|
|
||||||
};
|
if (msg->flags & I2C_MSG_STOP) {
|
||||||
i2c_hal_write_cmd_reg(&data->hal, cmd, data->cmd_idx);
|
i2c_esp32_master_stop(dev);
|
||||||
i2c_hal_enable_master_tx_it(&data->hal);
|
|
||||||
ret = i2c_esp32_transmit(dev);
|
ret = i2c_esp32_transmit(dev);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
LOG_ERR("I2C transfer error: %d", ret);
|
LOG_ERR("I2C transfer error: %d", ret);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue