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:
parent
94d3156540
commit
c7875b75aa
4 changed files with 268 additions and 30 deletions
|
@ -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"
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue