Skip to content

Add support for I2C Slave Drivers #5224

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions drivers/i2c/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,5 @@ zephyr_library_sources_ifdef(CONFIG_I2C_STM32_V2
)

zephyr_library_sources_ifdef(CONFIG_USERSPACE i2c_handlers.c)

add_subdirectory_ifdef(CONFIG_I2C_SLAVE slave)
1 change: 1 addition & 0 deletions drivers/i2c/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,7 @@ config I2C_NIOS2
source "drivers/i2c/Kconfig.dw"

source "drivers/i2c/Kconfig.esp32"
source "drivers/i2c/slave/Kconfig"

source "drivers/i2c/Kconfig.gpio"

Expand Down
4 changes: 4 additions & 0 deletions drivers/i2c/Kconfig.stm32
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
6 changes: 6 additions & 0 deletions drivers/i2c/i2c_handlers.c
Original file line number Diff line number Diff line change
Expand Up @@ -57,3 +57,9 @@ Z_SYSCALL_HANDLER(i2c_transfer, dev, msgs, num_msgs, addr)
(u8_t)num_msgs, (u16_t)addr,
ssf);
}

Z_SYSCALL_HANDLER1_SIMPLE(i2c_slave_driver_register, K_OBJ_DRIVER_I2C,
struct device *);

Z_SYSCALL_HANDLER1_SIMPLE(i2c_slave_driver_unregister, K_OBJ_DRIVER_I2C,
struct device *);
70 changes: 51 additions & 19 deletions drivers/i2c/i2c_ll_stm32.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -53,28 +53,36 @@ 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;

LL_I2C_Enable(i2c);

/* Check for validity of all messages, to prevent having to abort
* in the middle of a transfer
*/
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, this could be in a helper shred by all i2c controllers instead, with some parameters like the max messgae length.

current = msg;

/*
* Set I2C_MSG_RESTART flag on first message in order to send start
* condition
*/
current->flags |= I2C_MSG_RESTART;
while (num_msgs > 0) {
u8_t *next_msg_flags = NULL;

if (num_msgs > 1) {
for (u8_t i = 1; i <= num_msgs; i++) {
/* Maximum length of a single message is 255 Bytes */
if (current->len > 255) {
ret = -EINVAL;
break;
}

if (i < num_msgs) {
next = current + 1;
next_msg_flags = &(next->flags);

/*
* Stop or restart condition between messages
* Restart condition between messages
* of different directions is required
*/
if (OPERATION(current) != OPERATION(next)) {
Expand All @@ -83,17 +91,37 @@ static int i2c_stm32_transfer(struct device *dev, struct i2c_msg *msg,
break;
}
}
}

if (current->len > 255) {
ret = -EINVAL;
break;
/* Stop condition is only allowed on last message */
if (current->flags & I2C_MSG_STOP) {
ret = -EINVAL;
break;
}
} else {
/* Stop condition is required for the last message */
current->flags |= I2C_MSG_STOP;
}

/* Stop condition is required for the last message */
if ((num_msgs == 1) && !(current->flags & I2C_MSG_STOP)) {
ret = -EINVAL;
break;
current++;
}

if (ret) {
return ret;
}

/* Send out messages */
#if defined(CONFIG_I2C_STM32_V1)
LL_I2C_Enable(i2c);
#endif

current = msg;

while (num_msgs > 0) {
u8_t *next_msg_flags = NULL;

if (num_msgs > 1) {
next = current + 1;
next_msg_flags = &(next->flags);
}

if ((current->flags & I2C_MSG_RW_MASK) == I2C_MSG_WRITE) {
Expand All @@ -111,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)
Expand Down
14 changes: 14 additions & 0 deletions drivers/i2c/i2c_ll_stm32.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,26 +34,40 @@ 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,
u16_t sadr);
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);
#ifdef CONFIG_I2C_STM32_COMBINED_INTERRUPT
void stm32_i2c_combined_isr(void *arg);
#endif

#ifdef CONFIG_I2C_SLAVE
int i2c_stm32_slave_register(struct device *dev,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @dwagenk I just fixed these ... sorry

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)
Expand Down
Loading