Skip to content

Commit 309faa9

Browse files
drivers: i2c: dw: add restart support for buffer mode comm
- In slave mode, there is no interrupt generated for a RESTART condition. The buffer mode communication logic currently assumes that the transfer terminates only when a STOP condition is detected. - When the bus issues a RESTART instead of STOP, the buffer mode state is not handled correctly, which can lead to data mismatch or communication errors when the transfer direction changes. - Handle this case by detecting a START condition and resetting the buffer mode state accordingly. Signed-off-by: Shreehari HK <shreehari.hk@alifsemi.com>
1 parent 4007a1e commit 309faa9

File tree

1 file changed

+32
-1
lines changed

1 file changed

+32
-1
lines changed

drivers/i2c/i2c_dw.c

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -459,6 +459,37 @@ static void i2c_dw_isr(const struct device *port)
459459

460460
i2c_dw_slave_read_clear_intr_bits(port);
461461

462+
if (intr_stat.bits.start_det) {
463+
#ifdef CONFIG_I2C_TARGET_BUFFER_MODE
464+
if (dw->rx_pos != 0) {
465+
/* FIFO needs to be drained here
466+
* so we don't miss the next interrupt
467+
*/
468+
while (test_bit_status_rfne(reg_base)) {
469+
data = i2c_dw_read_byte_non_blocking(port);
470+
if (dw->rx_pos < CONFIG_I2C_TAR_DATA_BUF_MAX_LEN) {
471+
dw->rx_buf[dw->rx_pos++] = data;
472+
}
473+
}
474+
475+
if (slave_cb->buf_write_received) {
476+
slave_cb->buf_write_received(dw->slave_cfg,
477+
dw->rx_buf,
478+
dw->rx_pos);
479+
}
480+
dw->rx_pos = 0;
481+
}
482+
483+
if (dw->tx_pos != 0) {
484+
/* Just reset the write buffer and count values
485+
* if buffer write is incomplete
486+
*/
487+
dw->tx_pos = 0;
488+
dw->tx_len = 0;
489+
dw->tx_buf = NULL;
490+
}
491+
#endif
492+
}
462493
if (intr_stat.bits.rx_full) {
463494
if (dw->state != I2C_DW_CMD_SEND) {
464495
dw->state = I2C_DW_CMD_SEND;
@@ -992,7 +1023,7 @@ static int i2c_dw_slave_register(const struct device *dev, struct i2c_target_con
9921023
dw->slave_cfg = cfg;
9931024
ret = i2c_dw_set_slave_mode(dev, cfg->address, cfg->flags);
9941025
write_intr_mask(DW_INTR_MASK_RX_FULL | DW_INTR_MASK_RD_REQ | DW_INTR_MASK_TX_ABRT |
995-
DW_INTR_MASK_STOP_DET,
1026+
DW_INTR_MASK_START_DET | DW_INTR_MASK_STOP_DET,
9961027
reg_base);
9971028

9981029
return ret;

0 commit comments

Comments
 (0)