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;
|
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)
|
#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,
|
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 = current + 1;
|
||||||
next_msg_flags = &(next->flags);
|
next_msg_flags = &(next->flags);
|
||||||
}
|
}
|
||||||
do {
|
ret = i2c_stm32_transaction(dev, *current, next_msg_flags, slave);
|
||||||
uint32_t temp_len = current->len;
|
if (ret < 0)
|
||||||
uint8_t tmp_msg_flags = current->flags & ~I2C_MSG_RESTART;
|
break;
|
||||||
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);
|
|
||||||
current++;
|
current++;
|
||||||
num_msgs--;
|
num_msgs--;
|
||||||
}
|
}
|
||||||
exit:
|
|
||||||
k_sem_give(&data->bus_mutex);
|
k_sem_give(&data->bus_mutex);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue