Skip to content
Open
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
158 changes: 94 additions & 64 deletions drivers/i2c/i2c_silabs.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,13 @@
#include <zephyr/drivers/pinctrl.h>
#include <zephyr/kernel.h>
#include <zephyr/logging/log.h>
#if defined(CONFIG_I2C_SILABS_DMA)
#include <zephyr/drivers/dma.h>
#endif
#include <zephyr/drivers/clock_control.h>
#include <zephyr/drivers/clock_control/clock_control_silabs.h>
#include <zephyr/pm/device.h>
#include <zephyr/pm/device_runtime.h>
#include <zephyr/pm/policy.h>
#include <sl_hal_i2c.h>
#include <sl_i2c.h>
#include <sl_status.h>
#include <sli_i2c.h>

#define LOG_LEVEL CONFIG_I2C_LOG_LEVEL
Expand All @@ -36,7 +32,7 @@ struct i2c_silabs_dma_config {
/* Structure for I2C device configuration */
struct i2c_silabs_dev_config {
const struct pinctrl_dev_config *pcfg; /* Pin configuration for the I2C instance */
I2C_TypeDef *base; /* I2C peripheral base address */
sl_peripheral_t peripheral; /* I2C peripheral structure */
uint32_t bitrate; /* I2C bitrate (clock frequency) */
void (*irq_config_func)(void); /* IRQ configuration function */
const struct device *clock; /* Clock device */
Expand All @@ -47,11 +43,12 @@ struct i2c_silabs_dev_config {
struct i2c_silabs_dev_data {
struct k_sem bus_lock; /* Semaphore to lock the I2C bus */
struct k_sem transfer_sem; /* Semaphore to manage transfer */
sli_i2c_instance_t i2c_instance; /* I2C instance structure */
sl_i2c_handle_t i2c_handle; /* I2C handle structure */
struct i2c_silabs_dma_config dma_rx; /* DMA configuration for RX */
struct i2c_silabs_dma_config dma_tx; /* DMA configuration for TX */
bool asynchronous; /* Indicates if transfer is asynchronous */
bool last_transfer; /* Transfer is the last in the sequence */
bool is_10bit_addr; /* Indicates if addr is 7-bit or 10-bit */
#if defined CONFIG_I2C_CALLBACK
i2c_callback_t callback; /* I2C callback function pointer */
void *callback_context; /* Context for I2C callback */
Expand All @@ -64,15 +61,15 @@ static int i2c_silabs_pm_action(const struct device *dev, enum pm_device_action

static bool i2c_silabs_is_dma_enabled_instance(const struct device *dev)
{
#ifdef CONFIG_I2C_SILABS_DMA
struct i2c_silabs_dev_data *data = dev->data;

if (!IS_ENABLED(CONFIG_I2C_SILABS_DMA)) {
return false;
}

__ASSERT_NO_MSG(!!data->dma_tx.dma_dev == !!data->dma_rx.dma_dev);

return data->dma_rx.dma_dev != NULL;
#else
return false;
#endif
}

static void i2c_silabs_pm_policy_state_lock_get(const struct device *dev)
Expand All @@ -92,18 +89,17 @@ static int i2c_silabs_dev_configure(const struct device *dev, uint32_t dev_confi
{
const struct i2c_silabs_dev_config *config = dev->config;
struct i2c_silabs_dev_data *data = dev->data;
sl_i2c_init_params_t init_params;

/* Determine the I2C speed and corresponding baudrate */
switch (I2C_SPEED_GET(dev_config)) {
case I2C_SPEED_STANDARD:
init_params.freq_mode = SL_I2C_FREQ_STANDARD_MODE;
data->i2c_handle.frequency_mode = SL_I2C_FREQ_STANDARD_MODE;
break;
case I2C_SPEED_FAST:
init_params.freq_mode = SL_I2C_FREQ_FAST_MODE;
data->i2c_handle.frequency_mode = SL_I2C_FREQ_FAST_MODE;
break;
case I2C_SPEED_FAST_PLUS:
init_params.freq_mode = SL_I2C_FREQ_FASTPLUS_MODE;
data->i2c_handle.frequency_mode = SL_I2C_FREQ_FASTPLUS_MODE;
break;
default:
return -EINVAL;
Expand All @@ -112,19 +108,17 @@ static int i2c_silabs_dev_configure(const struct device *dev, uint32_t dev_confi
/* Take the bus lock semaphore to ensure exclusive access */
k_sem_take(&data->bus_lock, K_FOREVER);
/* Initialize I2C parameters */
init_params.i2c_base_addr = config->base;
data->i2c_instance.i2c_base_addr = init_params.i2c_base_addr;
data->i2c_handle.i2c_peripheral = config->peripheral;

/* Set the operating mode (leader or follower) */
#if defined(CONFIG_I2C_TARGET)
init_params.operating_mode = SL_I2C_FOLLOWER_MODE;
data->i2c_handle.operating_mode = SL_I2C_FOLLOWER_MODE;
#else
init_params.operating_mode = SL_I2C_LEADER_MODE;
data->i2c_handle.operating_mode = SL_I2C_LEADER_MODE;
#endif /* CONFIG_I2C_TARGET */
data->i2c_instance.operating_mode = init_params.operating_mode;

/* Configure the I2C instance */
sli_i2c_instance_configuration(&init_params);
sli_i2c_init_core(&data->i2c_handle);

/* Release the bus lock semaphore */
k_sem_give(&data->bus_lock);
Expand All @@ -137,25 +131,22 @@ static int i2c_silabs_transfer_dma(const struct device *dev, struct i2c_msg *msg
uint16_t addr, i2c_callback_t cb, void *userdata,
bool asynchronous)
{
#if defined(CONFIG_I2C_SILABS_DMA)
struct i2c_silabs_dev_data *data = dev->data;
__maybe_unused const struct i2c_silabs_dev_config *config = dev->config;
sl_i2c_handle_t *i2c_handle = (sl_i2c_handle_t *)&data->i2c_instance;
#if defined(CONFIG_I2C_CALLBACK)
data->callback_invoked = false;
#endif
data->pm_lock_done = false;
uint8_t i = 0;
int err = 0;

if (!IS_ENABLED(CONFIG_I2C_SILABS_DMA)) {
return -ENOTSUP;
}

/* Get the power management policy state lock */
i2c_silabs_pm_policy_state_lock_get(dev);

#if defined(CONFIG_I2C_TARGET)
/* Set follower address in target mode */
sli_i2c_set_follower_address(config->base, addr, data->i2c_instance.is_10bit_addr);
#endif /* CONFIG_I2C_TARGET */

while (i < num_msgs) {
uint8_t msgs_in_transfer = 1;

Expand All @@ -167,33 +158,49 @@ static int i2c_silabs_transfer_dma(const struct device *dev, struct i2c_msg *msg
data->last_transfer = (i + msgs_in_transfer) == num_msgs;

if (msgs_in_transfer == 2) {
if (sl_i2c_transfer_non_blocking(i2c_handle, msgs[i].buf, msgs[i].len,
msgs[i + 1].buf, msgs[i + 1].len, NULL,
NULL) != 0) {
if (sl_i2c_leader_transfer_non_blocking(
&data->i2c_handle, addr, msgs[i].buf, msgs[i].len,
msgs[i + 1].buf, msgs[i + 1].len, NULL) != 0) {
k_sem_give(&data->bus_lock);
return -EIO;
}
} else if (msgs[i].flags & I2C_MSG_READ) {
/* Start DMA receive */
if (sl_i2c_receive_non_blocking(i2c_handle, msgs[i].buf, msgs[i].len, NULL,
NULL) != 0) {
#if defined(CONFIG_I2C_TARGET)
if (sl_i2c_follower_receive_non_blocking(&data->i2c_handle, msgs[i].buf,
msgs[i].len, NULL) != 0) {
k_sem_give(&data->bus_lock);
return -EIO;
}
#else
if (sl_i2c_leader_receive_non_blocking(&data->i2c_handle, addr, msgs[i].buf,
msgs[i].len, NULL) != 0) {
k_sem_give(&data->bus_lock);
return -EIO;
}
#endif
} else {
/* Start DMA send */
if (sl_i2c_send_non_blocking(i2c_handle, msgs[i].buf, msgs[i].len, NULL,
NULL) != 0) {
#if defined(CONFIG_I2C_TARGET)
if (sl_i2c_follower_send_non_blocking(&data->i2c_handle, msgs[i].buf,
msgs[i].len, NULL) != 0) {
k_sem_give(&data->bus_lock);
return -EIO;
}
#else
if (sl_i2c_leader_send_non_blocking(&data->i2c_handle, addr, msgs[i].buf,
msgs[i].len, NULL) != 0) {
k_sem_give(&data->bus_lock);
return -EIO;
}
#endif
}
if (!asynchronous) {
/* Wait for DMA transfer to complete */
if (k_sem_take(&data->transfer_sem, K_MSEC(CONFIG_I2C_SILABS_TIMEOUT))) {
err = -ETIMEDOUT;
}
if (data->i2c_instance.state == SLI_I2C_STATE_ERROR) {
if (data->i2c_handle.state == SL_I2C_STATE_ERROR) {
err = -EIO;
}
k_sem_reset(&data->transfer_sem);
Expand All @@ -205,17 +212,13 @@ static int i2c_silabs_transfer_dma(const struct device *dev, struct i2c_msg *msg
}

return err;
#else
return -ENOTSUP;
#endif /* CONFIG_I2C_SILABS_DMA */
}

/* Function to handle synchronous transfer */
static int i2c_silabs_transfer_sync(const struct device *dev, struct i2c_msg *msgs,
uint8_t num_msgs, uint16_t addr)
{
struct i2c_silabs_dev_data *data = dev->data;
sl_i2c_handle_t *i2c_handle = (sl_i2c_handle_t *)&data->i2c_instance;
uint8_t i = 0;

/* Get the power management policy state lock */
Expand All @@ -227,24 +230,46 @@ static int i2c_silabs_transfer_sync(const struct device *dev, struct i2c_msg *ms
if ((msgs[i].flags & I2C_MSG_WRITE) == 0 && (i + 1 < num_msgs) &&
(msgs[i + 1].flags & I2C_MSG_READ)) {
msgs_in_transfer = 2;
if (sl_i2c_transfer(i2c_handle, msgs[i].buf, msgs[i].len, msgs[i + 1].buf,
msgs[i + 1].len) != 0) {
if (sl_i2c_leader_transfer_blocking(&data->i2c_handle, addr, msgs[i].buf,
msgs[i].len, msgs[i + 1].buf,
msgs[i + 1].len,
CONFIG_I2C_SILABS_TIMEOUT) != 0) {
k_sem_give(&data->bus_lock);
return -EIO;
}
i++;
} else if (msgs[i].flags & I2C_MSG_READ) {
if (sl_i2c_receive_blocking(i2c_handle, msgs[i].buf, msgs[i].len,
CONFIG_I2C_SILABS_TIMEOUT) != 0) {
#if defined(CONFIG_I2C_TARGET)
if (sl_i2c_follower_receive_blocking(&data->i2c_handle, msgs[i].buf,
msgs[i].len,
CONFIG_I2C_SILABS_TIMEOUT) != 0) {
k_sem_give(&data->bus_lock);
return -ETIMEDOUT;
}
#else
if (sl_i2c_leader_receive_blocking(&data->i2c_handle, addr, msgs[i].buf,
msgs[i].len,
CONFIG_I2C_SILABS_TIMEOUT) != 0) {
k_sem_give(&data->bus_lock);
return -ETIMEDOUT;
}
#endif
} else {
if (sl_i2c_send_blocking(i2c_handle, msgs[i].buf, msgs[i].len,
CONFIG_I2C_SILABS_TIMEOUT) != 0) {
#if defined(CONFIG_I2C_TARGET)
if (sl_i2c_follower_send_blocking(&data->i2c_handle, msgs[i].buf,
msgs[i].len,
CONFIG_I2C_SILABS_TIMEOUT) != 0) {
k_sem_give(&data->bus_lock);
return -ETIMEDOUT;
}
#else
if (sl_i2c_leader_send_blocking(&data->i2c_handle, addr, msgs[i].buf,
msgs[i].len,
CONFIG_I2C_SILABS_TIMEOUT) != 0) {
k_sem_give(&data->bus_lock);
return -ETIMEDOUT;
}
#endif
}
i += msgs_in_transfer;
}
Expand All @@ -265,7 +290,6 @@ static int i2c_silabs_transfer_impl(const struct device *dev, struct i2c_msg *ms
{
struct i2c_silabs_dev_data *data = dev->data;
__maybe_unused const struct i2c_silabs_dev_config *config = dev->config;
sl_i2c_handle_t *i2c_handle = (sl_i2c_handle_t *)&data->i2c_instance;
int ret = -EINVAL; /* Initialize ret to a default error value */

/* Check for invalid number of messages */
Expand All @@ -276,9 +300,9 @@ static int i2c_silabs_transfer_impl(const struct device *dev, struct i2c_msg *ms
/* Check and set the address mode (7-bit or 10-bit) based on */
/* the provided address */
if (addr <= 0x7F) {
data->i2c_instance.is_10bit_addr = false; /* 7-bit address */
data->is_10bit_addr = false; /* 7-bit address */
} else if (addr <= 0x3FF) {
data->i2c_instance.is_10bit_addr = true; /* 10-bit address */
data->is_10bit_addr = true; /* 10-bit address */
} else {
return -EINVAL;
}
Expand All @@ -292,11 +316,13 @@ static int i2c_silabs_transfer_impl(const struct device *dev, struct i2c_msg *ms
return ret;
}

#if defined(CONFIG_I2C_TARGET)
/* Set the follower address */
if (sl_i2c_set_follower_address(i2c_handle, addr) != 0) {
if (sl_i2c_set_follower_address(&data->i2c_handle, addr, data->is_10bit_addr) != 0) {
k_sem_give(&data->bus_lock);
return -EINVAL;
}
#endif

if (i2c_silabs_is_dma_enabled_instance(dev)) {
/* DMA transfer handle a/synchronous transfers */
Expand Down Expand Up @@ -373,25 +399,23 @@ static int i2c_silabs_dev_init(const struct device *dev)
return -EINVAL;
}

#if defined(CONFIG_I2C_SILABS_DMA)
if (i2c_silabs_is_dma_enabled_instance(dev)) {
if (IS_ENABLED(CONFIG_I2C_SILABS_DMA) && i2c_silabs_is_dma_enabled_instance(dev)) {
if (!device_is_ready(data->dma_rx.dma_dev) ||
!device_is_ready(data->dma_tx.dma_dev)) {
return -ENODEV;
}
data->dma_rx.dma_channel = dma_request_channel(data->dma_rx.dma_dev, NULL);
data->i2c_instance.dma_channel.dma_rx_channel = data->dma_rx.dma_channel;
data->i2c_handle.dma_channel.dma_rx_channel = data->dma_rx.dma_channel;

data->dma_tx.dma_channel = dma_request_channel(data->dma_tx.dma_dev, NULL);
data->i2c_instance.dma_channel.dma_tx_channel = data->dma_tx.dma_channel;
data->i2c_handle.dma_channel.dma_tx_channel = data->dma_tx.dma_channel;

if (data->dma_rx.dma_channel < 0 || data->dma_tx.dma_channel < 0) {
dma_release_channel(data->dma_rx.dma_dev, data->dma_rx.dma_channel);
dma_release_channel(data->dma_tx.dma_dev, data->dma_tx.dma_channel);
return -EAGAIN;
}
}
#endif /* CONFIG_I2C_SILABS_DMA */

/* Configure IRQ */
config->irq_config_func();
Expand Down Expand Up @@ -445,15 +469,14 @@ static int i2c_silabs_pm_action(const struct device *dev, enum pm_device_action
void i2c_silabs_isr_handler(const struct device *dev)
{
struct i2c_silabs_dev_data *data = dev->data;
sli_i2c_instance_t *sl_i2c_instance = &data->i2c_instance;
sl_i2c_handle_t *sl_i2c_handle = &data->i2c_handle;

#if defined(CONFIG_I2C_TARGET)
sli_i2c_follower_dispatch_interrupt(sl_i2c_instance);
sli_i2c_follower_dispatch_interrupt(sl_i2c_handle);
#else
sli_i2c_leader_dispatch_interrupt(sl_i2c_instance);
sli_i2c_leader_dispatch_interrupt(sl_i2c_handle);
#endif
if (sl_i2c_instance->transfer_event != SL_I2C_EVENT_IN_PROGRESS &&
sl_i2c_instance->rstart == 0) {
if (sl_i2c_handle->event != SL_I2C_EVENT_IN_PROGRESS) {
if (!data->asynchronous) {
k_sem_give(&data->transfer_sem);
}
Expand All @@ -462,9 +485,9 @@ void i2c_silabs_isr_handler(const struct device *dev)
int err = 0;

data->callback_invoked = true;
if (sl_i2c_instance->transfer_event == SL_I2C_EVENT_ARBITRATION_LOST ||
sl_i2c_instance->transfer_event == SL_I2C_EVENT_BUS_ERROR ||
sl_i2c_instance->transfer_event == SL_I2C_EVENT_INVALID_ADDR) {
if (sl_i2c_handle->event == SL_I2C_EVENT_ARBITRATION_LOST ||
sl_i2c_handle->event == SL_I2C_EVENT_BUS_ERROR ||
sl_i2c_handle->event == SL_I2C_EVENT_INVALID_ADDR) {
err = -EIO;
}
data->callback(dev, err, data->callback_context);
Expand Down Expand Up @@ -503,10 +526,17 @@ static DEVICE_API(i2c, i2c_silabs_dev_driver_api) = {
irq_enable(DT_INST_IRQ(idx, irq));), \
()) \
} \
\
\
static const uint32_t i2c_bus_clock_##idx = DT_INST_CLOCKS_CELL(idx, enable); \
static const sl_peripheral_val_t i2c_peripheral_val_##idx = { \
.base = DT_INST_REG_ADDR(idx), \
.clk_branch = DT_INST_CLOCKS_CELL(idx, branch), \
.bus_clock = (DT_INST_CLOCKS_CELL(idx, enable) ? &i2c_bus_clock_##idx : NULL), \
}; \
\
static const struct i2c_silabs_dev_config i2c_silabs_dev_config_##idx = { \
.pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(idx), \
.base = (I2C_TypeDef *)DT_INST_REG_ADDR(idx), \
.peripheral = &i2c_peripheral_val_##idx, \
.bitrate = DT_INST_PROP(idx, clock_frequency), \
.irq_config_func = i2c_silabs_irq_config_##idx, \
.clock = DEVICE_DT_GET(DT_INST_CLOCKS_CTLR(idx)), \
Expand Down
2 changes: 1 addition & 1 deletion west.yml
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,7 @@ manifest:
groups:
- hal
- name: hal_silabs
revision: 764d9153c69178bc851625d5a6a15fde237e626a
revision: pull/142/head
path: modules/hal/silabs
groups:
- hal
Expand Down
Loading