drivers: i2c: sam0: Implement bus locking

After experiencing a few deadlocks, it was discovered that this bus does
not implement any form of mutual exclusion... this patch addresses this
and resolves potential deadlocks.

Signed-off-by: Attie Grande <attie.grande@argentum-systems.co.uk>
This commit is contained in:
Attie Grande 2022-02-21 13:59:58 +00:00 committed by Carles Cufí
commit 66e8eae66a

View file

@ -50,6 +50,7 @@ struct i2c_sam0_msg {
}; };
struct i2c_sam0_dev_data { struct i2c_sam0_dev_data {
struct k_sem lock;
struct k_sem sem; struct k_sem sem;
struct i2c_sam0_msg msg; struct i2c_sam0_msg msg;
struct i2c_msg *msgs; struct i2c_msg *msgs;
@ -381,6 +382,7 @@ static int i2c_sam0_transfer(const struct device *dev, struct i2c_msg *msgs,
const struct i2c_sam0_dev_config *const cfg = dev->config; const struct i2c_sam0_dev_config *const cfg = dev->config;
SercomI2cm *i2c = cfg->regs; SercomI2cm *i2c = cfg->regs;
uint32_t addr_reg; uint32_t addr_reg;
int ret;
if (!num_msgs) { if (!num_msgs) {
return 0; return 0;
@ -388,10 +390,13 @@ static int i2c_sam0_transfer(const struct device *dev, struct i2c_msg *msgs,
data->num_msgs = num_msgs; data->num_msgs = num_msgs;
data->msgs = msgs; data->msgs = msgs;
k_sem_take(&data->lock, K_FOREVER);
for (; data->num_msgs > 0;) { for (; data->num_msgs > 0;) {
if (!data->msgs->len) { if (!data->msgs->len) {
if ((data->msgs->flags & I2C_MSG_RW_MASK) == I2C_MSG_READ) { if ((data->msgs->flags & I2C_MSG_RW_MASK) == I2C_MSG_READ) {
return -EINVAL; ret = -EINVAL;
goto unlock;
} }
} }
@ -423,7 +428,8 @@ static int i2c_sam0_transfer(const struct device *dev, struct i2c_msg *msgs,
#ifdef SERCOM_I2CM_ADDR_TENBITEN #ifdef SERCOM_I2CM_ADDR_TENBITEN
addr_reg |= SERCOM_I2CM_ADDR_TENBITEN; addr_reg |= SERCOM_I2CM_ADDR_TENBITEN;
#else #else
return -ENOTSUP; ret = -ENOTSUP;
goto unlock;
#endif #endif
} }
@ -481,12 +487,14 @@ static int i2c_sam0_transfer(const struct device *dev, struct i2c_msg *msgs,
if (data->msg.status & SERCOM_I2CM_STATUS_ARBLOST) { if (data->msg.status & SERCOM_I2CM_STATUS_ARBLOST) {
LOG_DBG("Arbitration lost on %s", LOG_DBG("Arbitration lost on %s",
dev->name); dev->name);
return -EAGAIN; ret = -EAGAIN;
goto unlock;
} }
LOG_ERR("Transaction error on %s: %08X", LOG_ERR("Transaction error on %s: %08X",
dev->name, data->msg.status); dev->name, data->msg.status);
return -EIO; ret = -EIO;
goto unlock;
} }
/* /*
@ -510,7 +518,11 @@ static int i2c_sam0_transfer(const struct device *dev, struct i2c_msg *msgs,
data->msgs++; data->msgs++;
} }
return 0; ret = 0;
unlock:
k_sem_give(&data->lock);
return ret;
} }
static int i2c_sam0_set_apply_bitrate(const struct device *dev, static int i2c_sam0_set_apply_bitrate(const struct device *dev,
@ -717,6 +729,7 @@ static int i2c_sam0_initialize(const struct device *dev)
return retval; return retval;
} }
k_sem_init(&data->lock, 1, 1);
k_sem_init(&data->sem, 0, 1); k_sem_init(&data->sem, 0, 1);
cfg->irq_config_func(dev); cfg->irq_config_func(dev);