drivers: i2c: add thread-safe semaphore

Add a semaphore to ensure that only one transaction
happens at a time when threads want to transfer
simultaneously.

Signed-off-by: Wei-Tai Lee <wtlee@andestech.com>
This commit is contained in:
Wei-Tai Lee 2023-06-08 10:35:40 +08:00 committed by Carles Cufí
commit fc89a85c29
2 changed files with 14 additions and 17 deletions

View file

@ -44,7 +44,8 @@ static void i2c_atciic100_default_control(const struct device *dev)
struct i2c_atciic100_dev_data_t *dev_data = dev->data;
uint32_t reg = 0;
k_sem_init(&dev_data->i2c_busy_sem, 1, 1);
k_sem_init(&dev_data->bus_lock, 1, 1);
k_sem_init(&dev_data->device_sync_sem, 0, 1);
/* Reset I2C bus */
reg = sys_read32(I2C_CMD(dev));
@ -140,7 +141,7 @@ static int i2c_atciic100_configure(const struct device *dev,
dev_data->driver_state |= I2C_DRV_CFG_PARAM;
unlock:
k_sem_give(&dev_data->i2c_busy_sem);
k_sem_give(&dev_data->bus_lock);
return ret;
}
@ -154,6 +155,8 @@ static int i2c_atciic100_transfer(const struct device *dev,
uint8_t burst_write_len = msgs[0].len + msgs[1].len;
uint8_t burst_write_buf[I2C_MAX_COUNT + BURST_CMD_COUNT];
k_sem_take(&dev_data->bus_lock, K_FOREVER);
if ((msgs[0].flags == I2C_MSG_WRITE)
&& (msgs[1].flags == (I2C_MSG_WRITE | I2C_MSG_STOP))) {
@ -186,14 +189,7 @@ static int i2c_atciic100_transfer(const struct device *dev,
exit:
/* Wait for transfer complete */
k_sem_take(&dev_data->i2c_busy_sem, K_FOREVER);
if (dev_data->status.target_ack != 1) {
k_sem_give(&dev_data->i2c_busy_sem);
return -EIO;
}
dev_data->status.target_ack = 0;
k_sem_give(&dev_data->i2c_busy_sem);
k_sem_give(&dev_data->bus_lock);
return ret;
}
@ -213,8 +209,6 @@ static int i2c_atciic100_controller_send(const struct device *dev,
return -EIO;
}
k_sem_take(&dev_data->i2c_busy_sem, K_FOREVER);
/* Disable all I2C interrupts */
reg = sys_read32(I2C_INTE(dev));
reg &= (~IEN_ALL);
@ -301,6 +295,7 @@ static int i2c_atciic100_controller_send(const struct device *dev,
reg |= (CMD_ISSUE_TRANSACTION);
sys_write32(reg, I2C_CMD(dev));
k_sem_take(&dev_data->device_sync_sem, K_FOREVER);
return 0;
}
@ -319,8 +314,6 @@ static int i2c_atciic100_controller_receive(const struct device *dev,
return -EIO;
}
k_sem_take(&dev_data->i2c_busy_sem, K_FOREVER);
/* Disable all I2C interrupts */
reg = sys_read32(I2C_INTE(dev));
reg &= (~IEN_ALL);
@ -394,6 +387,7 @@ static int i2c_atciic100_controller_receive(const struct device *dev,
reg |= (CMD_ISSUE_TRANSACTION);
sys_write32(reg, I2C_CMD(dev));
k_sem_take(&dev_data->device_sync_sem, K_FOREVER);
return 0;
}
@ -533,6 +527,8 @@ static void i2c_cmpl_handler(const struct device *dev, uint32_t reg_stat)
/* Clear & set driver state to controller rx complete */
dev_data->driver_state = I2C_DRV_CONTROLLER_RX_CMPL;
}
k_sem_give(&dev_data->device_sync_sem);
}
#if defined(CONFIG_I2C_TARGET)
@ -578,7 +574,6 @@ static void i2c_cmpl_handler(const struct device *dev, uint32_t reg_stat)
dev_data->status.arbitration_lost = 0;
#endif
k_sem_give(&dev_data->i2c_busy_sem);
}
#if defined(CONFIG_I2C_TARGET)
@ -594,7 +589,7 @@ static void andes_i2c_target_event(const struct device *dev,
* A new I2C data transaction(START-ADDRESS-DATA-STOP)
*/
if (reg_stat & STATUS_ADDR_HIT) {
if (k_sem_take(&dev_data->i2c_busy_sem, K_NO_WAIT) != 0) {
if (k_sem_take(&dev_data->bus_lock, K_NO_WAIT) != 0) {
return;
}
@ -637,6 +632,7 @@ static void andes_i2c_target_event(const struct device *dev,
if (reg_stat & STATUS_CMPL) {
i2c_cmpl_handler(dev, reg_stat);
k_sem_give(&dev_data->bus_lock);
}
}

View file

@ -223,7 +223,8 @@ struct _i2c_status {
};
struct i2c_atciic100_dev_data_t {
struct k_sem i2c_busy_sem;
struct k_sem bus_lock;
struct k_sem device_sync_sem;
volatile uint32_t driver_state;
uint8_t *middleware_rx_buf;
uint8_t *middleware_tx_buf;