stm32,i2c: Fix large I2C transactions on I2C V1

Previous commit added support of large transactions on I2C v2,
this commit implements some changes to also add support
of large transactions on I2C v1. Some refactoring is also done
to put the code in the right source files.

Fixes zephyrproject-rtos#58866

Signed-off-by: Michael Grand <m.grand@trustngo.tech>
This commit is contained in:
Michael Grand 2023-06-08 14:15:27 +02:00 committed by Fabio Baltieri
commit 78d3f2a6a0
4 changed files with 75 additions and 65 deletions

View file

@ -93,56 +93,6 @@ 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,
@ -220,7 +170,7 @@ static int i2c_stm32_transfer(const struct device *dev, struct i2c_msg *msg,
next = current + 1;
next_msg_flags = &(next->flags);
}
ret = i2c_stm32_transaction(dev, *current, next_msg_flags, slave);
ret = stm32_i2c_transaction(dev, *current, next_msg_flags, slave);
if (ret < 0) {
break;
}

View file

@ -80,12 +80,9 @@ struct i2c_stm32_data {
#endif
};
int32_t stm32_i2c_msg_write(const struct device *dev, struct i2c_msg *msg,
uint8_t *flg,
uint16_t sadr);
int32_t stm32_i2c_msg_read(const struct device *dev, struct i2c_msg *msg,
uint8_t *flg,
uint16_t sadr);
int32_t stm32_i2c_transaction(const struct device *dev,
struct i2c_msg msg, uint8_t *next_msg_flags,
uint16_t periph);
int32_t stm32_i2c_configure_timing(const struct device *dev, uint32_t clk);
int i2c_stm32_runtime_configure(const struct device *dev, uint32_t config);

View file

@ -613,7 +613,7 @@ end:
stm32_i2c_master_mode_end(dev);
}
int32_t stm32_i2c_msg_write(const struct device *dev, struct i2c_msg *msg,
static int32_t stm32_i2c_msg_write(const struct device *dev, struct i2c_msg *msg,
uint8_t *next_msg_flags, uint16_t saddr)
{
struct i2c_stm32_data *data = dev->data;
@ -632,7 +632,7 @@ int32_t stm32_i2c_msg_write(const struct device *dev, struct i2c_msg *msg,
return msg_end(dev, next_msg_flags, __func__);
}
int32_t stm32_i2c_msg_read(const struct device *dev, struct i2c_msg *msg,
static int32_t stm32_i2c_msg_read(const struct device *dev, struct i2c_msg *msg,
uint8_t *next_msg_flags, uint16_t saddr)
{
const struct i2c_stm32_config *cfg = dev->config;
@ -706,7 +706,7 @@ static int stm32_i2c_wait_timeout(uint16_t *timeout)
}
}
int32_t stm32_i2c_msg_write(const struct device *dev, struct i2c_msg *msg,
static int32_t stm32_i2c_msg_write(const struct device *dev, struct i2c_msg *msg,
uint8_t *next_msg_flags, uint16_t saddr)
{
const struct i2c_stm32_config *cfg = dev->config;
@ -804,7 +804,7 @@ end:
return res;
}
int32_t stm32_i2c_msg_read(const struct device *dev, struct i2c_msg *msg,
static int32_t stm32_i2c_msg_read(const struct device *dev, struct i2c_msg *msg,
uint8_t *next_msg_flags, uint16_t saddr)
{
const struct i2c_stm32_config *cfg = dev->config;
@ -986,3 +986,17 @@ int32_t stm32_i2c_configure_timing(const struct device *dev, uint32_t clock)
return 0;
}
int stm32_i2c_transaction(const struct device *dev,
struct i2c_msg msg, uint8_t *next_msg_flags,
uint16_t periph)
{
int ret;
if ((msg.flags & I2C_MSG_RW_MASK) == I2C_MSG_WRITE) {
ret = stm32_i2c_msg_write(dev, &msg, next_msg_flags, periph);
} else {
ret = stm32_i2c_msg_read(dev, &msg, next_msg_flags, periph);
}
return ret;
}

View file

@ -427,7 +427,7 @@ void stm32_i2c_error_isr(void *arg)
}
#endif
int stm32_i2c_msg_write(const struct device *dev, struct i2c_msg *msg,
static int stm32_i2c_msg_write(const struct device *dev, struct i2c_msg *msg,
uint8_t *next_msg_flags, uint16_t slave)
{
const struct i2c_stm32_config *cfg = dev->config;
@ -485,7 +485,7 @@ error:
return -EIO;
}
int stm32_i2c_msg_read(const struct device *dev, struct i2c_msg *msg,
static int stm32_i2c_msg_read(const struct device *dev, struct i2c_msg *msg,
uint8_t *next_msg_flags, uint16_t slave)
{
const struct i2c_stm32_config *cfg = dev->config;
@ -607,7 +607,7 @@ static inline int msg_done(const struct device *dev,
return 0;
}
int stm32_i2c_msg_write(const struct device *dev, struct i2c_msg *msg,
static int stm32_i2c_msg_write(const struct device *dev, struct i2c_msg *msg,
uint8_t *next_msg_flags, uint16_t slave)
{
const struct i2c_stm32_config *cfg = dev->config;
@ -637,7 +637,7 @@ int stm32_i2c_msg_write(const struct device *dev, struct i2c_msg *msg,
return msg_done(dev, msg->flags);
}
int stm32_i2c_msg_read(const struct device *dev, struct i2c_msg *msg,
static int stm32_i2c_msg_read(const struct device *dev, struct i2c_msg *msg,
uint8_t *next_msg_flags, uint16_t slave)
{
const struct i2c_stm32_config *cfg = dev->config;
@ -738,3 +738,52 @@ int stm32_i2c_configure_timing(const struct device *dev, uint32_t clock)
return 0;
}
int stm32_i2c_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 V2
* 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;
}