From c1a6252006c035b138c5c42b19ff3a11b3b38d6d Mon Sep 17 00:00:00 2001 From: Martin Hoff Date: Wed, 26 Nov 2025 16:06:59 +0100 Subject: [PATCH 1/5] drivers: power_domain: siwx91x: fix the link between pd and cpu state This patch is needed to block the pm_state "PM_STATE_SUSPEND_TO_IDLE" when a device on the power domain (actually all the peripherals) is active. Without this patch, cpu can decide to go to deep sleep while a peripheral is active. Signed-off-by: Martin Hoff --- drivers/power_domain/power_domain_silabs_siwx91x.c | 4 +++- dts/arm/silabs/siwg917.dtsi | 1 + soc/silabs/silabs_siwx91x/Kconfig.defconfig | 3 +++ 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/power_domain/power_domain_silabs_siwx91x.c b/drivers/power_domain/power_domain_silabs_siwx91x.c index 8350e8a25e15b..8b94ef9619e1b 100644 --- a/drivers/power_domain/power_domain_silabs_siwx91x.c +++ b/drivers/power_domain/power_domain_silabs_siwx91x.c @@ -5,7 +5,7 @@ */ #include -#include +#include #define DT_DRV_COMPAT silabs_siwx91x_power_domain @@ -13,10 +13,12 @@ static int siwx91x_pd_pm_action(const struct device *dev, enum pm_device_action { switch (action) { case PM_DEVICE_ACTION_RESUME: + pm_policy_device_power_lock_get(dev); pm_device_children_action_run(dev, PM_DEVICE_ACTION_TURN_ON, NULL); break; case PM_DEVICE_ACTION_SUSPEND: pm_device_children_action_run(dev, PM_DEVICE_ACTION_TURN_OFF, NULL); + pm_policy_device_power_lock_put(dev); break; case PM_DEVICE_ACTION_TURN_ON: break; diff --git a/dts/arm/silabs/siwg917.dtsi b/dts/arm/silabs/siwg917.dtsi index cf69489bf9817..dd956749b0ebe 100644 --- a/dts/arm/silabs/siwg917.dtsi +++ b/dts/arm/silabs/siwg917.dtsi @@ -134,6 +134,7 @@ siwx91x_soc_pd: siwx91x-soc-pd { compatible = "silabs,siwx91x-power-domain"; #power-domain-cells = <0>; + zephyr,disabling-power-states = <&pstate_ps4_sleep>; zephyr,pm-device-runtime-auto; status = "okay"; }; diff --git a/soc/silabs/silabs_siwx91x/Kconfig.defconfig b/soc/silabs/silabs_siwx91x/Kconfig.defconfig index 848201bca2532..12487e0547c0e 100644 --- a/soc/silabs/silabs_siwx91x/Kconfig.defconfig +++ b/soc/silabs/silabs_siwx91x/Kconfig.defconfig @@ -27,6 +27,9 @@ configdefault PM_DEVICE_RUNTIME configdefault POWER_DOMAIN default y +configdefault PM_POLICY_DEVICE_CONSTRAINTS + default y + endif # PM_DEVICE if SILABS_SIWX91X_NWP From 241784bb0613cd22b6ab2ce826d3eb123d126a12 Mon Sep 17 00:00:00 2001 From: Martin Hoff Date: Wed, 26 Nov 2025 16:15:23 +0100 Subject: [PATCH 2/5] drivers: spi: siwx91x: ensure device runtime is released on error Added calls to pm_device_runtime_put() to ensure proper device runtime management when configuration or DMA transceive operations fail. Signed-off-by: Martin Hoff --- drivers/spi/spi_silabs_siwx91x_gspi.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/spi/spi_silabs_siwx91x_gspi.c b/drivers/spi/spi_silabs_siwx91x_gspi.c index 4b60676abb670..8d0984fe55d0d 100644 --- a/drivers/spi/spi_silabs_siwx91x_gspi.c +++ b/drivers/spi/spi_silabs_siwx91x_gspi.c @@ -555,6 +555,7 @@ static int gspi_siwx91x_transceive(const struct device *dev, const struct spi_co ret = gspi_siwx91x_config(dev, config, cb, userdata); if (ret) { spi_context_release(&data->ctx, ret); + pm_device_runtime_put(dev); return ret; } } @@ -566,6 +567,9 @@ static int gspi_siwx91x_transceive(const struct device *dev, const struct spi_co if (spi_siwx91x_is_dma_enabled_instance(dev)) { /* Perform DMA transceive */ ret = gspi_siwx91x_transceive_dma(dev, config); + if (ret < 0) { + pm_device_runtime_put(dev); + } spi_context_release(&data->ctx, ret); } else { /* Perform synchronous polling transceive */ From 997023f7179a81e187285fab294fb0d449cebb56 Mon Sep 17 00:00:00 2001 From: Martin Hoff Date: Wed, 26 Nov 2025 16:34:45 +0100 Subject: [PATCH 3/5] drivers: dma: siwx91x: replace pm_state with pm_device (udma and gpdma) Removed unnecessary power management state lock calls since the DMA is on a power domain that already handles state locking. It will now managed directly pm_device which will active power_domain and then block pm_state. Signed-off-by: Martin Hoff --- drivers/dma/dma_silabs_siwx91x.c | 27 +++++---------- drivers/dma/dma_silabs_siwx91x_gpdma.c | 46 ++++++++++++++------------ 2 files changed, 32 insertions(+), 41 deletions(-) diff --git a/drivers/dma/dma_silabs_siwx91x.c b/drivers/dma/dma_silabs_siwx91x.c index 21b06bc598158..68792c932fa7f 100644 --- a/drivers/dma/dma_silabs_siwx91x.c +++ b/drivers/dma/dma_silabs_siwx91x.c @@ -14,7 +14,7 @@ #include #include #include -#include +#include #include #include "rsi_rom_udma.h" #include "rsi_rom_udma_wrapper.h" @@ -63,16 +63,6 @@ struct dma_siwx91x_data { */ }; -static void siwx91x_dma_pm_policy_state_lock_get(void) -{ - pm_policy_state_lock_get(PM_STATE_SUSPEND_TO_IDLE, PM_ALL_SUBSTATES); -} - -static void siwx91x_dma_pm_policy_state_lock_put(void) -{ - pm_policy_state_lock_put(PM_STATE_SUSPEND_TO_IDLE, PM_ALL_SUBSTATES); -} - static enum dma_xfer_dir siwx91x_transfer_direction(uint32_t dir) { if (dir == MEMORY_TO_MEMORY) { @@ -495,15 +485,14 @@ static int siwx91x_dma_start(const struct device *dev, uint32_t channel) return -EINVAL; } - /* Get the power management policy state lock */ if (!data->zephyr_channel_info[channel].channel_active) { - siwx91x_dma_pm_policy_state_lock_get(); + pm_device_runtime_get(dev); data->zephyr_channel_info[channel].channel_active = true; } if (RSI_UDMA_ChannelEnable(udma_handle, channel) != 0) { if (data->zephyr_channel_info[channel].channel_active) { - siwx91x_dma_pm_policy_state_lock_put(); + pm_device_runtime_put(dev); data->zephyr_channel_info[channel].channel_active = false; } return -EINVAL; @@ -534,7 +523,7 @@ static int siwx91x_dma_stop(const struct device *dev, uint32_t channel) } if (data->zephyr_channel_info[channel].channel_active) { - siwx91x_dma_pm_policy_state_lock_put(); + pm_device_runtime_put(dev); data->zephyr_channel_info[channel].channel_active = false; } @@ -680,16 +669,16 @@ static void siwx91x_dma_isr(const struct device *dev) } if (data->chan_info[channel].Cnt == data->chan_info[channel].Size) { + if (data->zephyr_channel_info[channel].channel_active) { + pm_device_runtime_put_async(dev, K_NO_WAIT); + data->zephyr_channel_info[channel].channel_active = false; + } if (data->zephyr_channel_info[channel].dma_callback) { /* Transfer complete, call user callback */ data->zephyr_channel_info[channel].dma_callback( dev, data->zephyr_channel_info[channel].cb_data, channel, 0); } sys_write32(BIT(channel), (mem_addr_t)&cfg->reg->UDMA_DONE_STATUS_REG); - if (data->zephyr_channel_info[channel].channel_active) { - siwx91x_dma_pm_policy_state_lock_put(); - data->zephyr_channel_info[channel].channel_active = false; - } } else { /* Call UDMA ROM IRQ handler. */ ROMAPI_UDMA_WRAPPER_API->uDMAx_IRQHandler(&udma_resources, udma_resources.desc, diff --git a/drivers/dma/dma_silabs_siwx91x_gpdma.c b/drivers/dma/dma_silabs_siwx91x_gpdma.c index 629377698fb70..b678519864748 100644 --- a/drivers/dma/dma_silabs_siwx91x_gpdma.c +++ b/drivers/dma/dma_silabs_siwx91x_gpdma.c @@ -12,7 +12,7 @@ #include #include #include -#include +#include #include "rsi_gpdma.h" #include "rsi_rom_gpdma.h" @@ -43,6 +43,7 @@ struct siwx91x_gpdma_channel_info { void *cb_data; RSI_GPDMA_DESC_T *desc; enum gpdma_xfer_dir xfer_direction; + bool channel_active; }; struct siwx91x_gpdma_config { @@ -62,16 +63,6 @@ struct siwx19x_gpdma_data { uint8_t reload_compatible; }; -static void siwx91x_gpdma_pm_policy_state_lock_get(void) -{ - pm_policy_state_lock_get(PM_STATE_SUSPEND_TO_IDLE, PM_ALL_SUBSTATES); -} - -static void siwx91x_gpdma_pm_policy_state_lock_put(void) -{ - pm_policy_state_lock_put(PM_STATE_SUSPEND_TO_IDLE, PM_ALL_SUBSTATES); -} - static bool siwx91x_gpdma_is_priority_valid(uint32_t channel_priority) { return (channel_priority >= GPDMA_MIN_PRIORITY && channel_priority <= GPDMA_MAX_PRIORITY); @@ -408,19 +399,22 @@ static int siwx91x_gpdma_reload(const struct device *dev, uint32_t channel, uint static int siwx91x_gpdma_start(const struct device *dev, uint32_t channel) { - const struct siwx91x_gpdma_config *cfg = dev->config; struct siwx19x_gpdma_data *data = dev->data; if (channel >= data->dma_ctx.dma_channels) { return -EINVAL; } - if (!sys_test_bit((mem_addr_t)&cfg->reg->GLOBAL.DMA_CHNL_ENABLE_REG, channel)) { - siwx91x_gpdma_pm_policy_state_lock_get(); + if (data->chan_info[channel].channel_active) { + pm_device_runtime_get(dev); + data->chan_info[channel].channel_active = true; } if (RSI_GPDMA_DMAChannelTrigger(&data->hal_ctx, channel)) { - siwx91x_gpdma_pm_policy_state_lock_put(); + if (data->chan_info[channel].channel_active) { + pm_device_runtime_put(dev); + data->chan_info[channel].channel_active = false; + } return -EINVAL; } @@ -429,7 +423,6 @@ static int siwx91x_gpdma_start(const struct device *dev, uint32_t channel) static int siwx91x_gpdma_stop(const struct device *dev, uint32_t channel) { - const struct siwx91x_gpdma_config *cfg = dev->config; struct siwx19x_gpdma_data *data = dev->data; k_spinlock_key_t key; @@ -437,10 +430,6 @@ static int siwx91x_gpdma_stop(const struct device *dev, uint32_t channel) return -EINVAL; } - if (sys_test_bit((mem_addr_t)&cfg->reg->GLOBAL.DMA_CHNL_ENABLE_REG, channel)) { - siwx91x_gpdma_pm_policy_state_lock_put(); - } - if (RSI_GPDMA_AbortChannel(&data->hal_ctx, channel)) { return -EINVAL; } @@ -449,6 +438,11 @@ static int siwx91x_gpdma_stop(const struct device *dev, uint32_t channel) siwx91x_gpdma_free_desc(data->desc_pool, data->chan_info[channel].desc); k_spin_unlock(&data->desc_pool_lock, key); + if (data->chan_info[channel].channel_active) { + pm_device_runtime_put(dev); + data->chan_info[channel].channel_active = false; + } + return 0; } @@ -552,7 +546,10 @@ static void siwx91x_gpdma_isr(const struct device *dev) if (channel_int_status & abort_mask) { RSI_GPDMA_AbortChannel(&data->hal_ctx, channel); cfg->reg->GLOBAL.INTERRUPT_STAT_REG = abort_mask; - siwx91x_gpdma_pm_policy_state_lock_put(); + if (data->chan_info[channel].channel_active) { + pm_device_runtime_put_async(dev, K_NO_WAIT); + data->chan_info[channel].channel_active = false; + } } if (channel_int_status & desc_fetch_mask) { @@ -569,11 +566,16 @@ static void siwx91x_gpdma_isr(const struct device *dev) k_spin_unlock(&data->desc_pool_lock, key); data->chan_info[channel].desc = NULL; cfg->reg->GLOBAL.INTERRUPT_STAT_REG = done_mask; + + if (data->chan_info[channel].channel_active) { + pm_device_runtime_put_async(dev, K_NO_WAIT); + data->chan_info[channel].channel_active = false; + } + if (data->chan_info[channel].cb) { data->chan_info[channel].cb(dev, data->chan_info[channel].cb_data, channel, 0); } - siwx91x_gpdma_pm_policy_state_lock_put(); } } From 0848a82c29ed1f82d77f50871cd1ad84f8960288 Mon Sep 17 00:00:00 2001 From: Martin Hoff Date: Wed, 26 Nov 2025 16:35:35 +0100 Subject: [PATCH 4/5] drivers: dma: siwx91x: fix interrupt flag reset This patch fix a reg write that needs to be done before calling the callback. Since the user can start a new dma transfer in the callback, it can miss the interrupt from the new transfer if the new transfer is done before the interrupt bit is cleared. Signed-off-by: Martin Hoff --- drivers/dma/dma_silabs_siwx91x.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/dma/dma_silabs_siwx91x.c b/drivers/dma/dma_silabs_siwx91x.c index 68792c932fa7f..be8171f4d0c96 100644 --- a/drivers/dma/dma_silabs_siwx91x.c +++ b/drivers/dma/dma_silabs_siwx91x.c @@ -669,6 +669,7 @@ static void siwx91x_dma_isr(const struct device *dev) } if (data->chan_info[channel].Cnt == data->chan_info[channel].Size) { + sys_write32(BIT(channel), (mem_addr_t)&cfg->reg->UDMA_DONE_STATUS_REG); if (data->zephyr_channel_info[channel].channel_active) { pm_device_runtime_put_async(dev, K_NO_WAIT); data->zephyr_channel_info[channel].channel_active = false; @@ -678,7 +679,6 @@ static void siwx91x_dma_isr(const struct device *dev) data->zephyr_channel_info[channel].dma_callback( dev, data->zephyr_channel_info[channel].cb_data, channel, 0); } - sys_write32(BIT(channel), (mem_addr_t)&cfg->reg->UDMA_DONE_STATUS_REG); } else { /* Call UDMA ROM IRQ handler. */ ROMAPI_UDMA_WRAPPER_API->uDMAx_IRQHandler(&udma_resources, udma_resources.desc, From 9f1da5c74b18a12764ae0e0d4742a80978a6d419 Mon Sep 17 00:00:00 2001 From: Martin Hoff Date: Wed, 26 Nov 2025 18:00:32 +0100 Subject: [PATCH 5/5] drivers: i2s: siwx91x: ensure device runtime is released Removed conditional checks for device runtime put in DMA RX and TX callbacks, ensuring that device runtime is always released asynchronously. Signed-off-by: Martin Hoff --- drivers/i2s/i2s_silabs_siwx91x.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/drivers/i2s/i2s_silabs_siwx91x.c b/drivers/i2s/i2s_silabs_siwx91x.c index 0a38ef305e247..323a82762f446 100644 --- a/drivers/i2s/i2s_silabs_siwx91x.c +++ b/drivers/i2s/i2s_silabs_siwx91x.c @@ -442,9 +442,7 @@ static void i2s_siwx91x_dma_rx_callback(const struct device *dma_dev, void *user rx_disable: i2s_siwx91x_stream_disable(stream, dma_dev); - if (stream->state == I2S_STATE_READY && stream->last_block) { - pm_device_runtime_put_async(i2s_dev, K_NO_WAIT); - } + pm_device_runtime_put_async(i2s_dev, K_NO_WAIT); } static void i2s_siwx91x_dma_tx_callback(const struct device *dma_dev, void *user_data, @@ -511,9 +509,7 @@ static void i2s_siwx91x_dma_tx_callback(const struct device *dma_dev, void *user tx_disable: i2s_siwx91x_stream_disable(stream, dma_dev); - if (stream->state == I2S_STATE_READY && stream->last_block) { - pm_device_runtime_put_async(i2s_dev, K_NO_WAIT); - } + pm_device_runtime_put_async(i2s_dev, K_NO_WAIT); } static int i2s_siwx91x_param_config(const struct device *dev, enum i2s_dir dir)