drivers: i2c: sam0: Resolve spurious trailing 0xff on reads

Previously, the I2C bus would have an extended idle period on write
operations, and a spurious trailing byte on read operations (after a
final NACK).

This patch relocates the issuance of the stop condition from the driving
thread's context, into the ISR.

Thanks to @benediktibk for discovery and initial patch.

Signed-off-by: Attie Grande <attie.grande@argentum-systems.co.uk>
This commit is contained in:
Attie Grande 2022-10-03 21:39:51 +01:00 committed by Fabio Baltieri
parent 6755ac7f12
commit de18f97f42

View file

@ -117,6 +117,9 @@ static bool i2c_sam0_terminate_on_error(const struct device *dev)
wait_synchronization(i2c);
i2c->INTENCLR.reg = SERCOM_I2CM_INTENCLR_MASK;
if (i2c->INTFLAG.reg & (SERCOM_I2CM_INTFLAG_MB | SERCOM_I2CM_INTFLAG_SB)) {
i2c->CTRLB.bit.CMD = 3;
}
k_sem_give(&data->sem);
return true;
}
@ -151,6 +154,7 @@ static void i2c_sam0_isr(const struct device *dev)
if (status & SERCOM_I2CM_INTFLAG_MB) {
if (!data->msg.size) {
i2c->INTENCLR.reg = SERCOM_I2CM_INTENCLR_MASK;
i2c->CTRLB.bit.CMD = 3;
k_sem_give(&data->sem);
return;
}
@ -166,6 +170,7 @@ static void i2c_sam0_isr(const struct device *dev)
* require write synchronization.
*/
i2c->CTRLB.bit.ACKACT = 1;
i2c->CTRLB.bit.CMD = 3;
}
*data->msg.buffer = i2c->DATA.reg;
@ -484,9 +489,6 @@ static int i2c_sam0_transfer(const struct device *dev, struct i2c_msg *msgs,
k_sem_take(&data->sem, K_FOREVER);
if (data->msg.status) {
/* return the bus to idle */
i2c->CTRLB.bit.CMD = 3;
if (data->msg.status & SERCOM_I2CM_STATUS_ARBLOST) {
LOG_DBG("Arbitration lost on %s",
dev->name);
@ -500,23 +502,6 @@ static int i2c_sam0_transfer(const struct device *dev, struct i2c_msg *msgs,
goto unlock;
}
/*
* Only send a stop if after this message:
* - it is explicitly requested that we send a stop, or
* - we are not conducting a restart, with more messages to follow
*
* Note: nothing validates the flags, so default to a stop if a stop is
* requested... do not let a restart request override it
*/
bool send_stop = (data->msgs->flags & I2C_MSG_STOP)
|| !((data->msgs->flags & I2C_MSG_RESTART) && (data->num_msgs > 1));
if (send_stop) {
while (!i2c->STATUS.bit.CLKHOLD) {
}
i2c->CTRLB.bit.CMD = 3;
}
data->num_msgs--;
data->msgs++;
}