Skip to content
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
7 changes: 3 additions & 4 deletions drivers/clock_control/clock_control_silabs_siwx91x.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#define DT_DRV_COMPAT silabs_siwx91x_clock
#define LF_FSM_CLOCK_FREQUENCY 32768
#define XTAL_FREQUENCY 40000000
#define INTF_PLL_FREQUENCY 160000000

LOG_MODULE_REGISTER(siwx91x_clock, CONFIG_CLOCK_CONTROL_LOG_LEVEL);

Expand Down Expand Up @@ -184,7 +185,7 @@ static int siwx91x_clock_get_rate(const struct device *dev, clock_control_subsys
*rate = LF_FSM_CLOCK_FREQUENCY;
return 0;
case SIWX91X_CLK_GSPI:
*rate = RSI_CLK_GetBaseClock(M4_GSPI);
*rate = INTF_PLL_FREQUENCY;
return 0;
default:
/* For now, no other driver need clock rate */
Expand Down Expand Up @@ -249,9 +250,7 @@ static int siwx91x_clock_init(const struct device *dev)
DT_PROP(DT_PATH(cpus, cpu_0), clock_frequency));

/* Use interface PLL at configured frequency as peripheral clock */
sl_si91x_clock_manager_set_pll_freq(INFT_PLL,
DT_PROP(DT_PATH(cpus, cpu_0), clock_frequency),
PLL_REF_CLK_VAL_XTAL);
sl_si91x_clock_manager_set_pll_freq(INFT_PLL, INTF_PLL_FREQUENCY, PLL_REF_CLK_VAL_XTAL);

/* FIXME: Currently the clock consumer use clocks without power on them.
* This should be fixed in drivers. Meanwhile, get the list of required
Expand Down
33 changes: 28 additions & 5 deletions drivers/gpio/gpio_silabs_siwx91x.c
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,29 @@ static int gpio_siwx91x_pm_action(const struct device *dev, enum pm_device_actio
return 0;
}


static int gpio_siwx91x_port_pm_action(const struct device *dev, enum pm_device_action action)
{
const struct gpio_siwx91x_port_config *port_cfg = dev->config;
const struct device *parent = port_cfg->parent;

switch (action) {
case PM_DEVICE_ACTION_TURN_ON:
break;
case PM_DEVICE_ACTION_TURN_OFF:
break;
case PM_DEVICE_ACTION_RESUME:
pm_device_runtime_get(parent);
break;
case PM_DEVICE_ACTION_SUSPEND:
pm_device_runtime_put(parent);
break;
default:
return -ENOTSUP;
}
return 0;
}

static int gpio_siwx91x_port_get(const struct device *port, gpio_port_value_t *value)
{
const struct gpio_siwx91x_port_config *port_cfg = port->config;
Expand Down Expand Up @@ -378,7 +401,7 @@ static inline int gpio_siwx91x_init_port(const struct device *port)
__ASSERT(port_cfg->port < cfg->port_count, "Too many ports");
data->ports[port_cfg->port] = port;

return 0;
return pm_device_driver_init(port, gpio_siwx91x_port_pm_action);
}

static void gpio_siwx91x_isr(const struct device *parent)
Expand Down Expand Up @@ -449,10 +472,10 @@ static DEVICE_API(gpio, gpio_siwx91x_api) = {
.pin_config_info = pin_config_info_##n, \
.total_pin_cnt = __builtin_popcount(GPIO_PORT_PIN_MASK_FROM_DT_NODE(n)), \
}; \
\
DEVICE_DT_DEFINE(n, gpio_siwx91x_init_port, NULL, &gpio_siwx91x_port_data##n, \
&gpio_siwx91x_port_config##n, PRE_KERNEL_1, CONFIG_GPIO_INIT_PRIORITY, \
&gpio_siwx91x_api);
PM_DEVICE_DT_DEFINE(n, gpio_siwx91x_port_pm_action); \
DEVICE_DT_DEFINE(n, gpio_siwx91x_init_port, PM_DEVICE_DT_GET(n), \
&gpio_siwx91x_port_data##n, &gpio_siwx91x_port_config##n, PRE_KERNEL_1, \
CONFIG_GPIO_INIT_PRIORITY, &gpio_siwx91x_api);

#define CONFIGURE_SHARED_INTERRUPT(node_id, prop, idx) \
IRQ_CONNECT(DT_IRQ_BY_IDX(node_id, idx, irq), DT_IRQ_BY_IDX(node_id, idx, priority), \
Expand Down
117 changes: 104 additions & 13 deletions drivers/spi/spi_silabs_siwx91x_gspi.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ LOG_MODULE_REGISTER(spi_siwx91x_gspi, CONFIG_SPI_LOG_LEVEL);
#define GSPI_MAX_BAUDRATE_FOR_DYNAMIC_CLOCK 110000000
#define GSPI_MAX_BAUDRATE_FOR_POS_EDGE_SAMPLE 40000000
#define GSPI_DMA_MAX_DESCRIPTOR_TRANSFER_SIZE 4096
#define SPI_HIGH_BURST_FREQ_THRESHOLD_HZ 10000000

/* Warning for unsupported configurations */
#if defined(CONFIG_SPI_ASYNC) && !defined(CONFIG_SPI_SILABS_SIWX91X_GSPI_DMA)
Expand All @@ -48,7 +49,7 @@ struct gspi_siwx91x_config {
const struct device *clock_dev;
clock_control_subsys_t clock_subsys;
const struct pinctrl_dev_config *pcfg;
uint8_t mosi_overrun __aligned(4);
uint8_t mosi_overrun __aligned(32);
};

struct gspi_siwx91x_data {
Expand Down Expand Up @@ -76,13 +77,37 @@ static bool spi_siwx91x_is_dma_enabled_instance(const struct device *dev)
#endif
}

void gspi_siwx91x_pick_lower_freq(uint32_t clock_hz, uint32_t requested_hz, uint32_t *actual_hz_out,
uint32_t *div_out)
{
/* Calculate divider that ensures freq <= requested */
uint32_t divider = DIV_ROUND_UP(clock_hz, 2 * requested_hz);
uint32_t actual_hz;

if (divider == 0U) {
divider = 1U;
}

/* Compute the actual achievable frequency */
actual_hz = clock_hz / (2U * divider);

if (actual_hz_out) {
*actual_hz_out = actual_hz;
}

if (div_out) {
*div_out = divider;
}
}

static int gspi_siwx91x_config(const struct device *dev, const struct spi_config *spi_cfg,
spi_callback_t cb, void *userdata)
{
__maybe_unused struct gspi_siwx91x_data *data = dev->data;
const struct gspi_siwx91x_config *cfg = dev->config;
uint32_t bit_rate = spi_cfg->frequency;
uint32_t clk_div_factor;
uint32_t actual_freq;
uint32_t clock_rate;
int ret;
__maybe_unused int channel_filter;
Expand Down Expand Up @@ -119,7 +144,13 @@ static int gspi_siwx91x_config(const struct device *dev, const struct spi_config
if (ret) {
return ret;
}
clk_div_factor = ((clock_rate / spi_cfg->frequency) / 2);

gspi_siwx91x_pick_lower_freq(clock_rate, spi_cfg->frequency, &actual_freq,
&clk_div_factor);
if (spi_cfg->frequency != actual_freq) {
LOG_INF("Requested %u Hz, programmed %u Hz (divider=%u)",
spi_cfg->frequency, actual_freq, clk_div_factor);
}
}

if (clk_div_factor < 1) {
Expand All @@ -145,8 +176,9 @@ static int gspi_siwx91x_config(const struct device *dev, const struct spi_config
/* Update the number of Data Bits */
cfg->reg->GSPI_WRITE_DATA2 = SPI_WORD_SIZE_GET(spi_cfg->operation);

/* Swap the read data inside the GSPI controller it-self */
cfg->reg->GSPI_CONFIG2_b.GSPI_RD_DATA_SWAP_MNL_CSN0 = 0;
/* Swap the write and read data inside the GSPI controller it-self */
cfg->reg->GSPI_CONFIG2_b.GSPI_RD_DATA_SWAP_MNL_CSN0 = 1;
cfg->reg->GSPI_CONFIG2_b.GSPI_WR_DATA_SWAP_MNL_CSN0 = 1;

/* Enable full-duplex mode and manual read/write */
cfg->reg->GSPI_CONFIG1_b.SPI_FULL_DUPLEX_EN = 1;
Expand Down Expand Up @@ -183,8 +215,10 @@ static int gspi_siwx91x_config(const struct device *dev, const struct spi_config
}
}

#ifdef CONFIG_SPI_ASYNC
data->ctx.callback = cb;
data->ctx.callback_data = userdata;
#endif
#endif
data->ctx.config = spi_cfg;

Expand Down Expand Up @@ -217,16 +251,16 @@ static void gspi_siwx91x_dma_tx_callback(const struct device *dev, void *user_da

static int gspi_siwx91x_dma_config(const struct device *dev,
struct gspi_siwx91x_dma_channel *channel, uint32_t block_count,
bool is_tx, uint8_t dfs)
bool is_tx, uint8_t dfs, uint8_t burst_size)
{
struct dma_config cfg = {
.channel_direction = is_tx ? MEMORY_TO_PERIPHERAL : PERIPHERAL_TO_MEMORY,
.channel_priority = 1,
.complete_callback_en = 0,
.source_data_size = dfs,
.dest_data_size = dfs,
.source_burst_length = 1,
.dest_burst_length = 1,
.source_burst_length = burst_size,
.dest_burst_length = burst_size,
.block_count = block_count,
.head_block = channel->dma_descriptors,
.dma_slot = channel->dma_slot,
Expand Down Expand Up @@ -344,7 +378,8 @@ static void gspi_siwx91x_reset_desc(struct gspi_siwx91x_dma_channel *channel)
static int gspi_siwx91x_prepare_dma_channel(const struct device *spi_dev,
const struct spi_buf *buffer, size_t buffer_count,
struct gspi_siwx91x_dma_channel *channel,
size_t padded_transaction_size, bool is_tx)
size_t padded_transaction_size, bool is_tx,
uint8_t burst_size)
{
const struct gspi_siwx91x_config *cfg = spi_dev->config;
struct gspi_siwx91x_data *data = spi_dev->data;
Expand All @@ -361,12 +396,13 @@ static int gspi_siwx91x_prepare_dma_channel(const struct device *spi_dev,
}

ret = gspi_siwx91x_dma_config(spi_dev, channel,
ARRAY_INDEX(channel->dma_descriptors, desc) + 1, is_tx, dfs);
ARRAY_INDEX(channel->dma_descriptors, desc) + 1, is_tx, dfs,
burst_size);
return ret;
}

static int gspi_siwx91x_prepare_dma_transaction(const struct device *dev,
size_t padded_transaction_size)
size_t padded_transaction_size, uint8_t burst_size)
{
int ret;
struct gspi_siwx91x_data *data = dev->data;
Expand All @@ -376,13 +412,15 @@ static int gspi_siwx91x_prepare_dma_transaction(const struct device *dev,
}

ret = gspi_siwx91x_prepare_dma_channel(dev, data->ctx.current_tx, data->ctx.tx_count,
&data->dma_tx, padded_transaction_size, true);
&data->dma_tx, padded_transaction_size, true,
burst_size);
if (ret) {
return ret;
}

ret = gspi_siwx91x_prepare_dma_channel(dev, data->ctx.current_rx, data->ctx.rx_count,
&data->dma_rx, padded_transaction_size, false);
&data->dma_rx, padded_transaction_size, false,
burst_size);

return ret;
}
Expand All @@ -395,6 +433,34 @@ static size_t gspi_siwx91x_longest_transfer_size(struct spi_context *instance_ct
return MAX(tx_transfer_size, rx_transfer_size);
}

static int gspi_siwx91x_burst_size_buf(const struct spi_buf *dma_spi_buf)
{
int burst_len = 4;

if (!dma_spi_buf->buf || !dma_spi_buf->len) {
return burst_len;
}

burst_len = MIN(burst_len, BIT(find_lsb_set((uint32_t)dma_spi_buf->buf) - 1));
burst_len = MIN(burst_len, BIT(find_lsb_set(dma_spi_buf->len) - 1));

return burst_len;
}

static int gspi_siwx91x_burst_size(struct spi_context *ctx)
{
int burst_len = 4;

for (int i = 0; i < ctx->tx_count; i++) {
burst_len = MIN(burst_len, gspi_siwx91x_burst_size_buf(ctx->current_tx + i));
}

for (int i = 0; i < ctx->rx_count; i++) {
burst_len = MIN(burst_len, gspi_siwx91x_burst_size_buf(ctx->current_rx + i));
}

return burst_len;
}
#endif /* CONFIG_SPI_SILABS_SIWX91X_GSPI_DMA */

static int gspi_siwx91x_transceive_dma(const struct device *dev, const struct spi_config *config)
Expand All @@ -405,17 +471,42 @@ static int gspi_siwx91x_transceive_dma(const struct device *dev, const struct sp
const struct device *dma_dev = data->dma_rx.dma_dev;
struct spi_context *ctx = &data->ctx;
size_t padded_transaction_size = gspi_siwx91x_longest_transfer_size(ctx);
uint8_t burst_size = 1;
int ret = 0;

if (padded_transaction_size == 0) {
return -EINVAL;
}

if (config->frequency >= SPI_HIGH_BURST_FREQ_THRESHOLD_HZ &&
data->dma_rx.dma_slot != 0xFF && data->dma_tx.dma_slot != 0xFF) {
/* NOTE: This condition ensures that high burst rates are only used with GPDMA.
*
* GPDMA (General-Purpose DMA) supports higher burst rates and operates at higher
* frequencies, unlike UDMA, which does not handle such speeds reliably.
* Therefore, the DMA slots are validated to ensure that the active DMA channels
* belong to GPDMA before enabling higher burst rates.
*
* Currently, DMA flow control (DMA_FLOW_CTRL) is not functioning correctly for
* memory-to-peripheral and peripheral-to-memory transfers. As a result, at lower
* SPI clock frequencies, GPDMA may read/write FIFOs at a much higher rate than the
* SPI peripheral, causing synchronization issues. However, at higher SPI clock
* frequencies (≥ 10 MHz), this timing mismatch is negligible, and higher burst
* rates operate as expected.
*
* In summary, high burst rates are safely enabled only for SPI transfers running
* at or above 10 MHz when using GPDMA.
*/
burst_size = gspi_siwx91x_burst_size(ctx);
}

cfg->reg->GSPI_FIFO_THRLD_b.RFIFO_RESET = 1;
cfg->reg->GSPI_FIFO_THRLD_b.WFIFO_RESET = 1;
cfg->reg->GSPI_FIFO_THRLD = 0;
cfg->reg->GSPI_FIFO_THRLD_b.FIFO_AEMPTY_THRLD = burst_size - 1;
cfg->reg->GSPI_FIFO_THRLD_b.FIFO_AFULL_THRLD = burst_size - 1;

ret = gspi_siwx91x_prepare_dma_transaction(dev, padded_transaction_size);
ret = gspi_siwx91x_prepare_dma_transaction(dev, padded_transaction_size, burst_size);
if (ret) {
return ret;
}
Expand Down
8 changes: 8 additions & 0 deletions drivers/wifi/siwx91x/siwx91x_wifi.c
Original file line number Diff line number Diff line change
Expand Up @@ -267,10 +267,18 @@ sl_status_t sl_si91x_host_process_data_frame(sl_wifi_interface_t interface,
sl_wifi_buffer_t *buffer)
{
sl_si91x_packet_t *si_pkt = sl_si91x_host_get_buffer_data(buffer, 0, NULL);
const struct net_eth_hdr *eth = (const struct net_eth_hdr *)si_pkt->data;
struct net_if *iface = net_if_get_first_wifi();
const struct net_linkaddr *ll = net_if_get_link_addr(iface);
struct net_pkt *pkt;
int ret;

/* NWP sometime echoes the Tx frames */
if (memcmp(eth->src.addr, ll->addr, sizeof(eth->src.addr)) == 0) {
LOG_DBG("Dropped packet (source MAC matches our MAC)");
return SL_STATUS_OK;
}

pkt = net_pkt_rx_alloc_with_buffer(iface, buffer->length, AF_UNSPEC, 0, K_NO_WAIT);
if (!pkt) {
LOG_ERR("net_pkt_rx_alloc_with_buffer() failed");
Expand Down
5 changes: 5 additions & 0 deletions dts/arm/silabs/siwg917.dtsi
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,7 @@
ngpios = <16>;
gpio-reserved-ranges = <0 6>;
silabs,pads = [ff ff ff ff ff ff 01 02 03 04 05 06 07 ff ff 08];
zephyr,pm-device-runtime-auto;
status = "okay";
};

Expand All @@ -216,6 +217,7 @@
#gpio-cells = <2>;
ngpios = <16>;
silabs,pads = [ff ff ff ff ff ff ff ff ff 00 00 00 00 00 00 09];
zephyr,pm-device-runtime-auto;
status = "okay";
};

Expand All @@ -226,6 +228,7 @@
#gpio-cells = <2>;
ngpios = <16>;
silabs,pads = [09 09 09 ff ff ff ff ff ff ff ff ff ff ff 0a 0b];
zephyr,pm-device-runtime-auto;
status = "okay";
};

Expand All @@ -236,6 +239,7 @@
#gpio-cells = <2>;
ngpios = <10>;
silabs,pads = [0c 0d 0e 0f 10 11 12 13 14 15 ff ff ff ff ff ff];
zephyr,pm-device-runtime-auto;
status = "okay";
};
};
Expand All @@ -260,6 +264,7 @@
ngpios = <12>;
gpio-reserved-ranges = <3 1>;
silabs,pads = [16 17 18 19 1a 1b 1c 1d 1e 1f 20 21 ff ff ff ff];
zephyr,pm-device-runtime-auto;
status = "okay";
};
};
Expand Down
2 changes: 2 additions & 0 deletions dts/arm/silabs/xg21/xg21.dtsi
Original file line number Diff line number Diff line change
Expand Up @@ -488,6 +488,7 @@
acmp0: acmp@5a008000 {
compatible = "silabs,acmp";
reg = <0x5a008000 0x4000>;
clocks = <&cmu CLOCK_AUTO CLOCK_BRANCH_INVALID>;
interrupt-names = "acmp0";
interrupts = <41 2>;
status = "disabled";
Expand All @@ -496,6 +497,7 @@
acmp1: acmp@5a00c000 {
compatible = "silabs,acmp";
reg = <0x5a00c000 0x4000>;
clocks = <&cmu CLOCK_AUTO CLOCK_BRANCH_INVALID>;
interrupt-names = "acmp1";
interrupts = <42 2>;
status = "disabled";
Expand Down
Loading