diff --git a/drivers/i2c/Kconfig.stm32 b/drivers/i2c/Kconfig.stm32 index 8821a7e3ed1..f7e7346c991 100644 --- a/drivers/i2c/Kconfig.stm32 +++ b/drivers/i2c/Kconfig.stm32 @@ -27,9 +27,13 @@ config I2C_STM32_V2 select HAS_DTS_I2C select USE_STM32_LL_I2C select USE_STM32_LL_RCC if SOC_SERIES_STM32F0X || SOC_SERIES_STM32F3X + select I2C_STM32_INTERRUPT if I2C_SLAVE + default n help Enable I2C support on the STM32 F0, F3 and L4X family of processors. This driver also supports the F7 and L0 series. + If I2C_SLAVE is enabled it selects I2C_STM32_INTERRUPT, since slave mode + is only supported by this driver with interrupts enabled. config I2C_STM32_INTERRUPT bool "STM32 MCU I2C Interrupt Support" diff --git a/drivers/i2c/i2c_ll_stm32.c b/drivers/i2c/i2c_ll_stm32.c index ad5b89030f0..48195747a84 100644 --- a/drivers/i2c/i2c_ll_stm32.c +++ b/drivers/i2c/i2c_ll_stm32.c @@ -18,7 +18,7 @@ #define SYS_LOG_LEVEL CONFIG_SYS_LOG_I2C_LEVEL #include -static int i2c_stm32_runtime_configure(struct device *dev, u32_t config) +int i2c_stm32_runtime_configure(struct device *dev, u32_t config) { const struct i2c_stm32_config *cfg = DEV_CFG(dev); struct i2c_stm32_data *data = DEV_DATA(dev); @@ -53,9 +53,11 @@ static int i2c_stm32_runtime_configure(struct device *dev, u32_t config) static int i2c_stm32_transfer(struct device *dev, struct i2c_msg *msg, u8_t num_msgs, u16_t slave) { +#if defined(CONFIG_I2C_STM32_V1) const struct i2c_stm32_config *cfg = DEV_CFG(dev); - struct i2c_msg *current, *next; I2C_TypeDef *i2c = cfg->i2c; +#endif + struct i2c_msg *current, *next; int ret = 0; /* Check for validity of all messages, to prevent having to abort @@ -108,7 +110,9 @@ static int i2c_stm32_transfer(struct device *dev, struct i2c_msg *msg, } /* Send out messages */ +#if defined(CONFIG_I2C_STM32_V1) LL_I2C_Enable(i2c); +#endif current = msg; @@ -135,15 +139,19 @@ static int i2c_stm32_transfer(struct device *dev, struct i2c_msg *msg, current++; num_msgs--; }; - +#if defined(CONFIG_I2C_STM32_V1) LL_I2C_Disable(i2c); - +#endif return ret; } static const struct i2c_driver_api api_funcs = { .configure = i2c_stm32_runtime_configure, .transfer = i2c_stm32_transfer, +#if defined(CONFIG_I2C_SLAVE) && defined(CONFIG_I2C_STM32_V2) + .slave_register = i2c_stm32_slave_register, + .slave_unregister = i2c_stm32_slave_unregister, +#endif }; static int i2c_stm32_init(struct device *dev) diff --git a/drivers/i2c/i2c_ll_stm32.h b/drivers/i2c/i2c_ll_stm32.h index cde627800e9..2a184d9ab55 100644 --- a/drivers/i2c/i2c_ll_stm32.h +++ b/drivers/i2c/i2c_ll_stm32.h @@ -34,12 +34,18 @@ struct i2c_stm32_data { unsigned int flags; #endif unsigned int is_write; + unsigned int is_arlo; unsigned int is_nack; unsigned int is_err; struct i2c_msg *msg; unsigned int len; u8_t *buf; } current; +#ifdef CONFIG_I2C_SLAVE + bool master_active; + struct i2c_slave_config *slave_cfg; + bool slave_attached; +#endif }; s32_t stm32_i2c_msg_write(struct device *dev, struct i2c_msg *msg, u8_t *flg, @@ -47,6 +53,7 @@ s32_t stm32_i2c_msg_write(struct device *dev, struct i2c_msg *msg, u8_t *flg, s32_t stm32_i2c_msg_read(struct device *dev, struct i2c_msg *msg, u8_t *flg, u16_t sadr); s32_t stm32_i2c_configure_timing(struct device *dev, u32_t clk); +int i2c_stm32_runtime_configure(struct device *dev, u32_t config); void stm32_i2c_event_isr(void *arg); void stm32_i2c_error_isr(void *arg); @@ -54,6 +61,13 @@ void stm32_i2c_error_isr(void *arg); void stm32_i2c_combined_isr(void *arg); #endif +#ifdef CONFIG_I2C_SLAVE +int i2c_stm32_slave_register(struct device *dev, + struct i2c_slave_config *config); +int i2c_stm32_slave_unregister(struct device *dev, + struct i2c_slave_config *config); +#endif + #define DEV_DATA(dev) ((struct i2c_stm32_data * const)(dev)->driver_data) #define DEV_CFG(dev) \ ((const struct i2c_stm32_config * const)(dev)->config->config_info) diff --git a/drivers/i2c/i2c_ll_stm32_v2.c b/drivers/i2c/i2c_ll_stm32_v2.c index a20110aa15e..43a8a911334 100644 --- a/drivers/i2c/i2c_ll_stm32_v2.c +++ b/drivers/i2c/i2c_ll_stm32_v2.c @@ -15,6 +15,7 @@ #include #include #include +#include "i2c-priv.h" #include "i2c_ll_stm32.h" #define SYS_LOG_LEVEL CONFIG_SYS_LOG_I2C_LEVEL @@ -50,17 +51,218 @@ static inline void msg_init(struct device *dev, struct i2c_msg *msg, LL_I2C_DisableAutoEndMode(i2c); LL_I2C_SetTransferRequest(i2c, transfer); LL_I2C_SetTransferSize(i2c, msg->len); + +#if defined(CONFIG_I2C_SLAVE) + data->master_active = true; +#endif + LL_I2C_Enable(i2c); + LL_I2C_GenerateStartCondition(i2c); } } #ifdef CONFIG_I2C_STM32_INTERRUPT + +static void stm32_i2c_disable_transfer_interrupts(struct device *dev) +{ + const struct i2c_stm32_config *cfg = DEV_CFG(dev); + I2C_TypeDef *i2c = cfg->i2c; + + LL_I2C_DisableIT_TX(i2c); + LL_I2C_DisableIT_RX(i2c); + LL_I2C_DisableIT_STOP(i2c); + LL_I2C_DisableIT_NACK(i2c); + LL_I2C_DisableIT_TC(i2c); + LL_I2C_DisableIT_ERR(i2c); +} + +static void stm32_i2c_enable_transfer_interrupts(struct device *dev) +{ + const struct i2c_stm32_config *cfg = DEV_CFG(dev); + I2C_TypeDef *i2c = cfg->i2c; + + LL_I2C_EnableIT_STOP(i2c); + LL_I2C_EnableIT_NACK(i2c); + LL_I2C_EnableIT_TC(i2c); + LL_I2C_EnableIT_ERR(i2c); +} + +static void stm32_i2c_master_mode_end(struct device *dev) +{ + const struct i2c_stm32_config *cfg = DEV_CFG(dev); + struct i2c_stm32_data *data = DEV_DATA(dev); + I2C_TypeDef *i2c = cfg->i2c; + + stm32_i2c_disable_transfer_interrupts(dev); + +#if defined(CONFIG_I2C_SLAVE) + data->master_active = false; + if (!data->slave_attached) { + LL_I2C_Disable(i2c); + } +#else + LL_I2C_Disable(i2c); +#endif + k_sem_give(&data->device_sync_sem); +} + +#if defined(CONFIG_I2C_SLAVE) +static void stm32_i2c_slave_event(struct device *dev) +{ + const struct i2c_stm32_config *cfg = DEV_CFG(dev); + struct i2c_stm32_data *data = DEV_DATA(dev); + I2C_TypeDef *i2c = cfg->i2c; + const struct i2c_slave_callbacks *slave_cb = + data->slave_cfg->callbacks; + + if (LL_I2C_IsActiveFlag_TXIS(i2c)) { + u8_t val; + + slave_cb->read_processed(data->slave_cfg, &val); + LL_I2C_TransmitData8(i2c, val); + return; + } + + if (LL_I2C_IsActiveFlag_RXNE(i2c)) { + u8_t val = LL_I2C_ReceiveData8(i2c); + + if (slave_cb->write_received(data->slave_cfg, val)) { + LL_I2C_AcknowledgeNextData(i2c, LL_I2C_NACK); + } + return; + } + + if (LL_I2C_IsActiveFlag_NACK(i2c)) { + LL_I2C_ClearFlag_NACK(i2c); + } + + if (LL_I2C_IsActiveFlag_STOP(i2c)) { + stm32_i2c_disable_transfer_interrupts(dev); + + /* Flush remaining TX byte before clearing Stop Flag */ + LL_I2C_ClearFlag_TXE(i2c); + + LL_I2C_ClearFlag_STOP(i2c); + + slave_cb->stop(data->slave_cfg); + + /* Prepare to ACK next transmissions address byte */ + LL_I2C_AcknowledgeNextData(i2c, LL_I2C_ACK); + } + + if (LL_I2C_IsActiveFlag_ADDR(i2c)) { + u32_t dir; + + LL_I2C_ClearFlag_ADDR(i2c); + + dir = LL_I2C_GetTransferDirection(i2c); + if (dir == LL_I2C_DIRECTION_WRITE) { + slave_cb->write_requested(data->slave_cfg); + LL_I2C_EnableIT_RX(i2c); + } else { + u8_t val; + + slave_cb->read_requested(data->slave_cfg, &val); + LL_I2C_TransmitData8(i2c, val); + LL_I2C_EnableIT_TX(i2c); + } + + stm32_i2c_enable_transfer_interrupts(dev); + } +} + +/* Attach and start I2C as slave */ +int i2c_stm32_slave_register(struct device *dev, + struct i2c_slave_config *config) +{ + const struct i2c_stm32_config *cfg = DEV_CFG(dev); + struct i2c_stm32_data *data = DEV_DATA(dev); + I2C_TypeDef *i2c = cfg->i2c; + u32_t bitrate_cfg; + int ret; + + if (!config) { + return -EINVAL; + } + + if (data->slave_attached) { + return -EBUSY; + } + + if (data->master_active) { + return -EBUSY; + } + + bitrate_cfg = _i2c_map_dt_bitrate(cfg->bitrate); + + ret = i2c_stm32_runtime_configure(dev, bitrate_cfg); + if (ret < 0) { + SYS_LOG_ERR("i2c: failure initializing"); + return ret; + } + + data->slave_cfg = config; + + LL_I2C_Enable(i2c); + + LL_I2C_SetOwnAddress1(i2c, config->address << 1, + LL_I2C_OWNADDRESS1_7BIT); + LL_I2C_EnableOwnAddress1(i2c); + + data->slave_attached = true; + + SYS_LOG_DBG("i2c: slave registered"); + + LL_I2C_EnableIT_ADDR(i2c); + + return 0; +} + +int i2c_stm32_slave_unregister(struct device *dev, + struct i2c_slave_config *config) +{ + const struct i2c_stm32_config *cfg = DEV_CFG(dev); + struct i2c_stm32_data *data = DEV_DATA(dev); + I2C_TypeDef *i2c = cfg->i2c; + + if (!data->slave_attached) { + return -EINVAL; + } + + if (data->master_active) { + return -EBUSY; + } + + LL_I2C_DisableOwnAddress1(i2c); + + LL_I2C_DisableIT_ADDR(i2c); + stm32_i2c_disable_transfer_interrupts(dev); + + LL_I2C_ClearFlag_NACK(i2c); + LL_I2C_ClearFlag_STOP(i2c); + LL_I2C_ClearFlag_ADDR(i2c); + + LL_I2C_Disable(i2c); + + SYS_LOG_DBG("i2c: slave unregistered"); + + return 0; +} + +#endif /* defined(CONFIG_I2C_SLAVE) */ + static void stm32_i2c_event(struct device *dev) { const struct i2c_stm32_config *cfg = DEV_CFG(dev); struct i2c_stm32_data *data = DEV_DATA(dev); I2C_TypeDef *i2c = cfg->i2c; +#if defined(CONFIG_I2C_SLAVE) + if (data->slave_attached && !data->master_active) { + stm32_i2c_slave_event(dev); + return; + } +#endif if (data->current.len) { /* Send next byte */ if (LL_I2C_IsActiveFlag_TXIS(i2c)) { @@ -97,19 +299,14 @@ static void stm32_i2c_event(struct device *dev) if (data->current.msg->flags & I2C_MSG_STOP) { LL_I2C_GenerateStopCondition(i2c); } else { - goto end; + stm32_i2c_disable_transfer_interrupts(dev); + k_sem_give(&data->device_sync_sem); } } return; end: - LL_I2C_DisableIT_TX(i2c); - LL_I2C_DisableIT_RX(i2c); - LL_I2C_DisableIT_STOP(i2c); - LL_I2C_DisableIT_NACK(i2c); - LL_I2C_DisableIT_TC(i2c); - LL_I2C_DisableIT_ERR(i2c); - k_sem_give(&data->device_sync_sem); + stm32_i2c_master_mode_end(dev); } static int stm32_i2c_error(struct device *dev) @@ -118,6 +315,19 @@ static int stm32_i2c_error(struct device *dev) struct i2c_stm32_data *data = DEV_DATA(dev); I2C_TypeDef *i2c = cfg->i2c; +#if defined(CONFIG_I2C_SLAVE) + if (data->slave_attached && !data->master_active) { + /* No need for a slave error function right now. */ + return 0; + } +#endif + + if (LL_I2C_IsActiveFlag_ARLO(i2c)) { + LL_I2C_ClearFlag_ARLO(i2c); + data->current.is_arlo = 1; + goto end; + } + if (LL_I2C_IsActiveFlag_BERR(i2c)) { LL_I2C_ClearFlag_BERR(i2c); data->current.is_err = 1; @@ -126,13 +336,7 @@ static int stm32_i2c_error(struct device *dev) return 0; end: - LL_I2C_DisableIT_TX(i2c); - LL_I2C_DisableIT_RX(i2c); - LL_I2C_DisableIT_STOP(i2c); - LL_I2C_DisableIT_NACK(i2c); - LL_I2C_DisableIT_TC(i2c); - LL_I2C_DisableIT_ERR(i2c); - k_sem_give(&data->device_sync_sem); + stm32_i2c_master_mode_end(dev); return -EIO; } @@ -163,7 +367,6 @@ void stm32_i2c_error_isr(void *arg) } #endif - int stm32_i2c_msg_write(struct device *dev, struct i2c_msg *msg, u8_t *next_msg_flags, uint16_t slave) { @@ -179,21 +382,25 @@ int stm32_i2c_msg_write(struct device *dev, struct i2c_msg *msg, data->current.msg = msg; msg_init(dev, msg, next_msg_flags, slave, LL_I2C_REQUEST_WRITE); - LL_I2C_EnableIT_STOP(i2c); - LL_I2C_EnableIT_NACK(i2c); - LL_I2C_EnableIT_TC(i2c); - LL_I2C_EnableIT_ERR(i2c); + stm32_i2c_enable_transfer_interrupts(dev); LL_I2C_EnableIT_TX(i2c); k_sem_take(&data->device_sync_sem, K_FOREVER); - if (data->current.is_nack || data->current.is_err) { + if (data->current.is_nack || data->current.is_err || + data->current.is_arlo) { goto error; } return 0; error: + if (data->current.is_arlo) { + SYS_LOG_DBG("%s: ARLO %d", __func__, + data->current.is_arlo); + data->current.is_arlo = 0; + } + if (data->current.is_nack) { SYS_LOG_DBG("%s: NACK", __func__); data->current.is_nack = 0; @@ -218,26 +425,31 @@ int stm32_i2c_msg_read(struct device *dev, struct i2c_msg *msg, data->current.len = msg->len; data->current.buf = msg->buf; data->current.is_write = 0; + data->current.is_arlo = 0; data->current.is_err = 0; data->current.is_nack = 0; data->current.msg = msg; msg_init(dev, msg, next_msg_flags, slave, LL_I2C_REQUEST_READ); - LL_I2C_EnableIT_STOP(i2c); - LL_I2C_EnableIT_NACK(i2c); - LL_I2C_EnableIT_TC(i2c); - LL_I2C_EnableIT_ERR(i2c); + stm32_i2c_enable_transfer_interrupts(dev); LL_I2C_EnableIT_RX(i2c); k_sem_take(&data->device_sync_sem, K_FOREVER); - if (data->current.is_nack || data->current.is_err) { + if (data->current.is_nack || data->current.is_err || + data->current.is_arlo) { goto error; } return 0; error: + if (data->current.is_arlo) { + SYS_LOG_DBG("%s: ARLO %d", __func__, + data->current.is_arlo); + data->current.is_arlo = 0; + } + if (data->current.is_nack) { SYS_LOG_DBG("%s: NACK", __func__); data->current.is_nack = 0;