stm32,i2c: Fix large I2C transactions
The stm32's I2C peripheral has a maximum transmission size. Larger trans- action, that I2C itself allows, can be achieved be using the peripheral's reload-mode. In order to do that, st's low-lever drivers need to be informed according- ly. The previous iteration of the code mishandled the next_msg_flags para- meter, causing the issue to manifest itself. This refactors the inner loop of i2c_stm32_transfer() into its own func- tion. This passes the message parameter by value in order to be able to mutate its state while keeping the original datum from the user intact during the entire procedure. Fixes #43235 Signed-off-by: Frank Terbeck <ft@bewatermyfriend.org>
This commit is contained in:
parent
ad959bc2bb
commit
1f0b1403f5
1 changed files with 54 additions and 35 deletions
|
@ -61,6 +61,56 @@ int i2c_stm32_runtime_configure(const struct device *dev, uint32_t config)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static inline int
|
||||
i2c_stm32_transaction(const struct device *dev,
|
||||
struct i2c_msg msg, uint8_t *next_msg_flags,
|
||||
uint16_t periph)
|
||||
{
|
||||
/*
|
||||
* Perform a I2C transaction, while taking into account the STM32 I2C
|
||||
* peripheral has a limited maximum chunk size. Take appropriate action
|
||||
* if the message to send exceeds that limit.
|
||||
*
|
||||
* The last chunk of a transmission uses this function's next_msg_flags
|
||||
* parameter for its backend calls (_write/_read). Any previous chunks
|
||||
* use a copy of the current message's flags, with the STOP and RESTART
|
||||
* bits turned off. This will cause the backend to use reload-mode,
|
||||
* which will make the combination of all chunks to look like one big
|
||||
* transaction on the wire.
|
||||
*/
|
||||
const uint32_t i2c_stm32_maxchunk = 255U;
|
||||
const uint8_t saved_flags = msg.flags;
|
||||
uint8_t combine_flags =
|
||||
saved_flags & ~(I2C_MSG_STOP | I2C_MSG_RESTART);
|
||||
uint8_t *flagsp = NULL;
|
||||
uint32_t rest = msg.len;
|
||||
int ret = 0;
|
||||
|
||||
do { /* do ... while to allow zero-length transactions */
|
||||
if (msg.len > i2c_stm32_maxchunk) {
|
||||
msg.len = i2c_stm32_maxchunk;
|
||||
msg.flags &= ~I2C_MSG_STOP;
|
||||
flagsp = &combine_flags;
|
||||
} else {
|
||||
msg.flags = saved_flags;
|
||||
flagsp = next_msg_flags;
|
||||
}
|
||||
if ((msg.flags & I2C_MSG_RW_MASK) == I2C_MSG_WRITE) {
|
||||
ret = stm32_i2c_msg_write(dev, &msg, flagsp, periph);
|
||||
} else {
|
||||
ret = stm32_i2c_msg_read(dev, &msg, flagsp, periph);
|
||||
}
|
||||
if (ret < 0) {
|
||||
break;
|
||||
}
|
||||
rest -= msg.len;
|
||||
msg.buf += msg.len;
|
||||
msg.len = rest;
|
||||
} while (rest > 0U);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define OPERATION(msg) (((struct i2c_msg *) msg)->flags & I2C_MSG_RW_MASK)
|
||||
|
||||
static int i2c_stm32_transfer(const struct device *dev, struct i2c_msg *msg,
|
||||
|
@ -126,44 +176,13 @@ static int i2c_stm32_transfer(const struct device *dev, struct i2c_msg *msg,
|
|||
next = current + 1;
|
||||
next_msg_flags = &(next->flags);
|
||||
}
|
||||
do {
|
||||
uint32_t temp_len = current->len;
|
||||
uint8_t tmp_msg_flags = current->flags & ~I2C_MSG_RESTART;
|
||||
uint8_t tmp_next_msg_flags = next_msg_flags ?
|
||||
*next_msg_flags : 0;
|
||||
|
||||
if (current->len > 255) {
|
||||
current->len = 255U;
|
||||
current->flags &= ~I2C_MSG_STOP;
|
||||
if (next_msg_flags) {
|
||||
*next_msg_flags = current->flags &
|
||||
~I2C_MSG_RESTART;
|
||||
}
|
||||
}
|
||||
if ((current->flags & I2C_MSG_RW_MASK) ==
|
||||
I2C_MSG_WRITE) {
|
||||
ret = stm32_i2c_msg_write(dev, current,
|
||||
next_msg_flags,
|
||||
slave);
|
||||
} else {
|
||||
ret = stm32_i2c_msg_read(dev, current,
|
||||
next_msg_flags, slave);
|
||||
}
|
||||
|
||||
if (ret < 0) {
|
||||
goto exit;
|
||||
}
|
||||
if (next_msg_flags) {
|
||||
*next_msg_flags = tmp_next_msg_flags;
|
||||
}
|
||||
current->buf += current->len;
|
||||
current->flags = tmp_msg_flags;
|
||||
current->len = temp_len - current->len;
|
||||
} while (current->len > 0);
|
||||
ret = i2c_stm32_transaction(dev, *current, next_msg_flags, slave);
|
||||
if (ret < 0)
|
||||
break;
|
||||
current++;
|
||||
num_msgs--;
|
||||
}
|
||||
exit:
|
||||
|
||||
k_sem_give(&data->bus_mutex);
|
||||
return ret;
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue