Skip to content

Commit 2ec4986

Browse files
rustypig91cfriedt
authored andcommitted
drivers: i2c: sam0: Continue write/read if next message allows it
Update sam0 i2c driver to directly send/receive next message if it is in the same direction and the current message has no stop or restart flags. Seems like in some drivers this is the expected behaviour. Fixes #36857 Signed-off-by: Christoffer Zakrisson <rustypig91@gmail.com>
1 parent 285a073 commit 2ec4986

File tree

1 file changed

+40
-20
lines changed

1 file changed

+40
-20
lines changed

drivers/i2c/i2c_sam0.c

Lines changed: 40 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@ struct i2c_sam0_msg {
5252
struct i2c_sam0_dev_data {
5353
struct k_sem sem;
5454
struct i2c_sam0_msg msg;
55+
struct i2c_msg *msgs;
56+
uint8_t num_msgs;
5557
};
5658

5759
#define DEV_NAME(dev) ((dev)->name)
@@ -137,6 +139,18 @@ static void i2c_sam0_isr(const struct device *dev)
137139
return;
138140
}
139141

142+
/*
143+
* Directly send/receive next message if it is in the same direction and
144+
* the current message has no stop flag and the next message has no
145+
* restart flag.
146+
*/
147+
const bool continue_next = (data->msg.size == 1) && (data->num_msgs > 1) &&
148+
((data->msgs[0].flags & I2C_MSG_RW_MASK) ==
149+
(data->msgs[1].flags & I2C_MSG_RW_MASK)) &&
150+
!(data->msgs[0].flags & I2C_MSG_STOP) &&
151+
!(data->msgs[1].flags & I2C_MSG_RESTART) &&
152+
((status & (SERCOM_I2CM_INTFLAG_MB | SERCOM_I2CM_INTFLAG_SB)));
153+
140154
if (status & SERCOM_I2CM_INTFLAG_MB) {
141155
if (!data->msg.size) {
142156
i2c->INTENCLR.reg = SERCOM_I2CM_INTENCLR_MASK;
@@ -147,12 +161,8 @@ static void i2c_sam0_isr(const struct device *dev)
147161
i2c->DATA.reg = *data->msg.buffer;
148162
data->msg.buffer++;
149163
data->msg.size--;
150-
151-
return;
152-
}
153-
154-
if (status & SERCOM_I2CM_INTFLAG_SB) {
155-
if (data->msg.size == 1) {
164+
} else if (status & SERCOM_I2CM_INTFLAG_SB) {
165+
if (!continue_next) {
156166
/*
157167
* If this is the last byte, then prepare for an auto
158168
* NACK before doing the actual read. This does not
@@ -165,12 +175,20 @@ static void i2c_sam0_isr(const struct device *dev)
165175
data->msg.buffer++;
166176
data->msg.size--;
167177

168-
if (!data->msg.size) {
178+
if (!continue_next) {
169179
i2c->INTENCLR.reg = SERCOM_I2CM_INTENCLR_MASK;
170180
k_sem_give(&data->sem);
171181
return;
172182
}
173-
return;
183+
}
184+
185+
if (continue_next) {
186+
data->msgs++;
187+
data->num_msgs--;
188+
189+
data->msg.buffer = data->msgs->buf;
190+
data->msg.size = data->msgs->len;
191+
data->msg.status = 0;
174192
}
175193
}
176194

@@ -373,10 +391,12 @@ static int i2c_sam0_transfer(const struct device *dev, struct i2c_msg *msgs,
373391
if (!num_msgs) {
374392
return 0;
375393
}
394+
data->num_msgs = num_msgs;
395+
data->msgs = msgs;
376396

377-
for (; num_msgs > 0;) {
378-
if (!msgs->len) {
379-
if ((msgs->flags & I2C_MSG_RW_MASK) == I2C_MSG_READ) {
397+
for (; data->num_msgs > 0;) {
398+
if (!data->msgs->len) {
399+
if ((data->msgs->flags & I2C_MSG_RW_MASK) == I2C_MSG_READ) {
380400
return -EINVAL;
381401
}
382402
}
@@ -392,20 +412,20 @@ static int i2c_sam0_transfer(const struct device *dev, struct i2c_msg *msgs,
392412
SERCOM_I2CM_STATUS_BUSERR;
393413
wait_synchronization(i2c);
394414

395-
data->msg.buffer = msgs->buf;
396-
data->msg.size = msgs->len;
415+
data->msg.buffer = data->msgs->buf;
416+
data->msg.size = data->msgs->len;
397417
data->msg.status = 0;
398418

399419
addr_reg = addr << 1U;
400-
if ((msgs->flags & I2C_MSG_RW_MASK) == I2C_MSG_READ) {
420+
if ((data->msgs->flags & I2C_MSG_RW_MASK) == I2C_MSG_READ) {
401421
addr_reg |= 1U;
402422

403423
/* Set to auto ACK */
404424
i2c->CTRLB.bit.ACKACT = 0;
405425
wait_synchronization(i2c);
406426
}
407427

408-
if (msgs->flags & I2C_MSG_ADDR_10_BITS) {
428+
if (data->msgs->flags & I2C_MSG_ADDR_10_BITS) {
409429
#ifdef SERCOM_I2CM_ADDR_TENBITEN
410430
addr_reg |= SERCOM_I2CM_ADDR_TENBITEN;
411431
#else
@@ -432,7 +452,7 @@ static int i2c_sam0_transfer(const struct device *dev, struct i2c_msg *msgs,
432452
i2c->INTENSET.reg = SERCOM_I2CM_INTENSET_ERROR;
433453
#endif
434454

435-
if ((msgs->flags & I2C_MSG_RW_MASK) == I2C_MSG_READ) {
455+
if ((data->msgs->flags & I2C_MSG_RW_MASK) == I2C_MSG_READ) {
436456
/*
437457
* Always set MB even when reading, since that's how
438458
* some errors are indicated.
@@ -472,9 +492,9 @@ static int i2c_sam0_transfer(const struct device *dev, struct i2c_msg *msgs,
472492
return -EIO;
473493
}
474494

475-
if (msgs->flags & I2C_MSG_STOP) {
495+
if (data->msgs->flags & I2C_MSG_STOP) {
476496
i2c->CTRLB.bit.CMD = 3;
477-
} else if ((msgs->flags & I2C_MSG_RESTART) && num_msgs > 1) {
497+
} else if ((data->msgs->flags & I2C_MSG_RESTART) && data->num_msgs > 1) {
478498
/*
479499
* No action, since we do this automatically if we
480500
* don't send an explicit stop
@@ -487,8 +507,8 @@ static int i2c_sam0_transfer(const struct device *dev, struct i2c_msg *msgs,
487507
i2c->CTRLB.bit.CMD = 3;
488508
}
489509

490-
num_msgs--;
491-
msgs++;
510+
data->num_msgs--;
511+
data->msgs++;
492512
}
493513

494514
return 0;

0 commit comments

Comments
 (0)