From c7875b75aa51ee60264201aa1778ceed67a04d69 Mon Sep 17 00:00:00 2001 From: Daniel Wagenknecht Date: Thu, 1 Mar 2018 10:53:49 +0100 Subject: [PATCH] i2c: stm32_v2: implement slave support This patch adds I2C Slave support conforming to the syscalls and funcs, only for the STM32 V2 I2C Driver for the moment. It is capable of handling multi-master bus setups. Signed-off-by: Neil Armstrong Signed-off-by: Daniel Wagenknecht --- drivers/i2c/Kconfig.stm32 | 4 + drivers/i2c/i2c_ll_stm32.c | 16 ++- drivers/i2c/i2c_ll_stm32.h | 14 ++ drivers/i2c/i2c_ll_stm32_v2.c | 264 ++++++++++++++++++++++++++++++---- 4 files changed, 268 insertions(+), 30 deletions(-) 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;