i2c-mcux: take semaphore during transfer

Neither i2c_transfer in i2c.h nor i2c_mcux_transfer in i2c_mcux.c
have any sort of locking. If e.g. an i2c eprom is updated using a
shell and simultaneously another thread access a lm75 then one
of the two transfers will fail or produce a random result.

This changes addresses this issue by that all i2c_msgs of one
i2c_transfer are completed before allowing a subsequent transfer
to start.

The code has been validated on a FRDM_K64F.

Signed-off-by: Andreas Dröscher <github@anticat.ch>
This commit is contained in:
Andreas Dröscher 2020-10-16 14:30:54 +02:00 committed by Maureen Helm
commit 49a3ce5881

View file

@ -34,6 +34,7 @@ struct i2c_mcux_config {
struct i2c_mcux_data { struct i2c_mcux_data {
i2c_master_handle_t handle; i2c_master_handle_t handle;
struct k_sem lock;
struct k_sem device_sync_sem; struct k_sem device_sync_sem;
status_t callback_status; status_t callback_status;
}; };
@ -42,6 +43,7 @@ static int i2c_mcux_configure(const struct device *dev,
uint32_t dev_config_raw) uint32_t dev_config_raw)
{ {
I2C_Type *base = DEV_BASE(dev); I2C_Type *base = DEV_BASE(dev);
struct i2c_mcux_data *data = DEV_DATA(dev);
const struct i2c_mcux_config *config = DEV_CFG(dev); const struct i2c_mcux_config *config = DEV_CFG(dev);
uint32_t clock_freq; uint32_t clock_freq;
uint32_t baudrate; uint32_t baudrate;
@ -69,7 +71,9 @@ static int i2c_mcux_configure(const struct device *dev,
} }
clock_freq = CLOCK_GetFreq(config->clock_source); clock_freq = CLOCK_GetFreq(config->clock_source);
k_sem_take(&data->lock, K_FOREVER);
I2C_MasterSetBaudRate(base, baudrate, clock_freq); I2C_MasterSetBaudRate(base, baudrate, clock_freq);
k_sem_give(&data->lock);
return 0; return 0;
} }
@ -109,11 +113,15 @@ static int i2c_mcux_transfer(const struct device *dev, struct i2c_msg *msgs,
struct i2c_mcux_data *data = DEV_DATA(dev); struct i2c_mcux_data *data = DEV_DATA(dev);
i2c_master_transfer_t transfer; i2c_master_transfer_t transfer;
status_t status; status_t status;
int ret = 0;
k_sem_take(&data->lock, K_FOREVER);
/* Iterate over all the messages */ /* Iterate over all the messages */
for (int i = 0; i < num_msgs; i++) { for (int i = 0; i < num_msgs; i++) {
if (I2C_MSG_ADDR_10_BITS & msgs->flags) { if (I2C_MSG_ADDR_10_BITS & msgs->flags) {
return -ENOTSUP; ret = -ENOTSUP;
break;
} }
/* Initialize the transfer descriptor */ /* Initialize the transfer descriptor */
@ -142,7 +150,8 @@ static int i2c_mcux_transfer(const struct device *dev, struct i2c_msg *msgs,
*/ */
if (status != kStatus_Success) { if (status != kStatus_Success) {
I2C_MasterTransferAbort(base, &data->handle); I2C_MasterTransferAbort(base, &data->handle);
return -EIO; ret = -EIO;
break;
} }
/* Wait for the transfer to complete */ /* Wait for the transfer to complete */
@ -153,14 +162,17 @@ static int i2c_mcux_transfer(const struct device *dev, struct i2c_msg *msgs,
*/ */
if (data->callback_status != kStatus_Success) { if (data->callback_status != kStatus_Success) {
I2C_MasterTransferAbort(base, &data->handle); I2C_MasterTransferAbort(base, &data->handle);
return -EIO; ret = -EIO;
break;
} }
/* Move to the next message */ /* Move to the next message */
msgs++; msgs++;
} }
return 0; k_sem_give(&data->lock);
return ret;
} }
static void i2c_mcux_isr(const struct device *dev) static void i2c_mcux_isr(const struct device *dev)
@ -180,6 +192,7 @@ static int i2c_mcux_init(const struct device *dev)
i2c_master_config_t master_config; i2c_master_config_t master_config;
int error; int error;
k_sem_init(&data->lock, 1, 1);
k_sem_init(&data->device_sync_sem, 0, UINT_MAX); k_sem_init(&data->device_sync_sem, 0, UINT_MAX);
clock_freq = CLOCK_GetFreq(config->clock_source); clock_freq = CLOCK_GetFreq(config->clock_source);