esp32: drivers: i2c: fix command sequence

Closes #45008

Signed-off-by: Lucas Tamborrino <lucas.tamborrino@espressif.com>
This commit is contained in:
Lucas Tamborrino 2022-05-25 09:27:51 -03:00 committed by Carles Cufí
commit 092406b08d

View file

@ -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++);
}
if (msg->len == 0) {
cmd = (i2c_hw_cmd_t) {
.op_code = I2C_LL_CMD_STOP,
.byte_num = 0,
.ack_val = 0,
.ack_en = 0
};
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_write_cmd_reg(&data->hal, hw_end_cmd, data->cmd_idx++);
i2c_hal_enable_master_rx_it(&data->hal); i2c_hal_enable_master_rx_it(&data->hal);
ret = i2c_esp32_transmit(dev); 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;
}
static int IRAM_ATTR i2c_esp32_read_msg(const struct device *dev,
struct i2c_msg *msg, uint16_t addr)
{
int ret = 0;
struct i2c_esp32_data *data = (struct i2c_esp32_data *const)(dev)->data;
/* 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;
}
}
ret = i2c_esp32_master_read(dev, msg);
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); if (msg->flags & I2C_MSG_STOP) {
msg->buf += rd_filled; i2c_esp32_master_stop(dev);
ret = i2c_esp32_transmit(dev);
if (ret < 0) {
LOG_ERR("I2C transfer error: %d", ret);
return ret;
}
}
/* reset fifo read pointer */ return 0;
data->cmd_idx = 0; }
static int IRAM_ATTR i2c_esp32_master_write(const struct device *dev, struct i2c_msg *msg)
{
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);
return ret;
}
} }
for (;;) { ret = i2c_esp32_master_write(dev, msg);
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) { if (ret < 0) {
LOG_ERR("I2C transfer error: %d", ret); LOG_ERR("I2C transfer error: %d", ret);
return ret; return ret;
} }
data->cmd_idx = 0; if (msg->flags & I2C_MSG_STOP) {
} i2c_esp32_master_stop(dev);
if (msg->len == 0) {
break;
}
}
/* add stop command in a new transmission */
if (msg->len == 0 && (msg->flags & I2C_MSG_STOP)) {
cmd = (i2c_hw_cmd_t) {
.op_code = I2C_LL_CMD_STOP,
.ack_en = false,
.byte_num = 0
};
i2c_hal_write_cmd_reg(&data->hal, cmd, data->cmd_idx);
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);