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 <narmstrong@baylibre.com>
Signed-off-by: Daniel Wagenknecht <wagenknecht@clage.de>
This commit is contained in:
Daniel Wagenknecht 2018-03-01 10:53:49 +01:00 committed by Carles Cufí
commit c7875b75aa
4 changed files with 268 additions and 30 deletions

View file

@ -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"

View file

@ -18,7 +18,7 @@
#define SYS_LOG_LEVEL CONFIG_SYS_LOG_I2C_LEVEL
#include <logging/sys_log.h>
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)

View file

@ -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)

View file

@ -15,6 +15,7 @@
#include <board.h>
#include <errno.h>
#include <i2c.h>
#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;