From b567a7db83ae72aecd49963c1124da93d1ef1624 Mon Sep 17 00:00:00 2001 From: Guillaume Gautier Date: Mon, 5 Feb 2024 11:45:09 +0100 Subject: [PATCH] drivers: spi: stm32: add pm support Add power management support for STM32 SPI Signed-off-by: Guillaume Gautier --- drivers/spi/spi_ll_stm32.c | 97 +++++++++++++++++++++++++++++++++++++- drivers/spi/spi_ll_stm32.h | 1 + 2 files changed, 97 insertions(+), 1 deletion(-) diff --git a/drivers/spi/spi_ll_stm32.c b/drivers/spi/spi_ll_stm32.c index e24b9722d06..80ec408d540 100644 --- a/drivers/spi/spi_ll_stm32.c +++ b/drivers/spi/spi_ll_stm32.c @@ -18,6 +18,8 @@ LOG_MODULE_REGISTER(spi_ll_stm32); #include #include #include +#include +#include #ifdef CONFIG_SPI_STM32_DMA #include #include @@ -73,6 +75,36 @@ LOG_MODULE_REGISTER(spi_ll_stm32); #endif #endif /* CONFIG_SOC_SERIES_STM32MP1X */ +static void spi_stm32_pm_policy_state_lock_get(const struct device *dev) +{ + if (IS_ENABLED(CONFIG_PM)) { + struct spi_stm32_data *data = dev->data; + + if (!data->pm_policy_state_on) { + data->pm_policy_state_on = true; + pm_policy_state_lock_get(PM_STATE_SUSPEND_TO_IDLE, PM_ALL_SUBSTATES); + if (IS_ENABLED(CONFIG_PM_S2RAM)) { + pm_policy_state_lock_get(PM_STATE_SUSPEND_TO_RAM, PM_ALL_SUBSTATES); + } + } + } +} + +static void spi_stm32_pm_policy_state_lock_put(const struct device *dev) +{ + if (IS_ENABLED(CONFIG_PM)) { + struct spi_stm32_data *data = dev->data; + + if (data->pm_policy_state_on) { + data->pm_policy_state_on = false; + pm_policy_state_lock_put(PM_STATE_SUSPEND_TO_IDLE, PM_ALL_SUBSTATES); + if (IS_ENABLED(CONFIG_PM_S2RAM)) { + pm_policy_state_lock_put(PM_STATE_SUSPEND_TO_RAM, PM_ALL_SUBSTATES); + } + } + } +} + #ifdef CONFIG_SPI_STM32_DMA static uint32_t bits2bytes(uint32_t bits) { @@ -500,6 +532,8 @@ static void spi_stm32_complete(const struct device *dev, int status) #ifdef CONFIG_SPI_STM32_INTERRUPT spi_context_complete(&data->ctx, dev, status); #endif + + spi_stm32_pm_policy_state_lock_put(dev); } #ifdef CONFIG_SPI_STM32_INTERRUPT @@ -768,6 +802,8 @@ static int transceive(const struct device *dev, spi_context_lock(&data->ctx, asynchronous, cb, userdata, config); + spi_stm32_pm_policy_state_lock_get(dev); + ret = spi_stm32_configure(dev, config); if (ret) { goto end; @@ -969,6 +1005,8 @@ static int transceive_dma(const struct device *dev, spi_context_lock(&data->ctx, asynchronous, cb, userdata, config); + spi_stm32_pm_policy_state_lock_get(dev); + k_sem_reset(&data->status_sem); ret = spi_stm32_configure(dev, config); @@ -1077,6 +1115,8 @@ static int transceive_dma(const struct device *dev, end: spi_context_release(&data->ctx, ret); + spi_stm32_pm_policy_state_lock_put(dev); + return ret; } #endif /* CONFIG_SPI_STM32_DMA */ @@ -1198,6 +1238,59 @@ static int spi_stm32_init(const struct device *dev) return 0; } +#ifdef CONFIG_PM_DEVICE +static int spi_stm32_pm_action(const struct device *dev, + enum pm_device_action action) +{ + const struct spi_stm32_config *config = dev->config; + const struct device *const clk = DEVICE_DT_GET(STM32_CLOCK_CONTROL_NODE); + int err; + + + switch (action) { + case PM_DEVICE_ACTION_RESUME: + /* Set pins to active state */ + err = pinctrl_apply_state(config->pcfg, PINCTRL_STATE_DEFAULT); + if (err < 0) { + return err; + } + + /* enable clock */ + err = clock_control_on(clk, (clock_control_subsys_t)&config->pclken[0]); + if (err != 0) { + LOG_ERR("Could not enable SPI clock"); + return err; + } + break; + case PM_DEVICE_ACTION_SUSPEND: + /* Stop device clock. */ + err = clock_control_off(clk, (clock_control_subsys_t)&config->pclken[0]); + if (err != 0) { + LOG_ERR("Could not enable SPI clock"); + return err; + } + + /* Move pins to sleep state */ + err = pinctrl_apply_state(config->pcfg, PINCTRL_STATE_SLEEP); + if ((err < 0) && (err != -ENOENT)) { + /* + * If returning -ENOENT, no pins where defined for sleep mode : + * Do not output on console (might sleep already) when going to sleep, + * "SPI pinctrl sleep state not available" + * and don't block PM suspend. + * Else return the error. + */ + return err; + } + break; + default: + return -ENOTSUP; + } + + return 0; +} +#endif /* CONFIG_PM_DEVICE */ + #ifdef CONFIG_SPI_STM32_INTERRUPT #define STM32_SPI_IRQ_HANDLER_DECL(id) \ static void spi_stm32_irq_config_func_##id(const struct device *dev) @@ -1297,7 +1390,9 @@ static struct spi_stm32_data spi_stm32_dev_data_##id = { \ SPI_CONTEXT_CS_GPIOS_INITIALIZE(DT_DRV_INST(id), ctx) \ }; \ \ -DEVICE_DT_INST_DEFINE(id, &spi_stm32_init, NULL, \ +PM_DEVICE_DT_INST_DEFINE(id, spi_stm32_pm_action); \ + \ +DEVICE_DT_INST_DEFINE(id, &spi_stm32_init, PM_DEVICE_DT_INST_GET(id), \ &spi_stm32_dev_data_##id, &spi_stm32_cfg_##id, \ POST_KERNEL, CONFIG_SPI_INIT_PRIORITY, \ &api_funcs); \ diff --git a/drivers/spi/spi_ll_stm32.h b/drivers/spi/spi_ll_stm32.h index 23a08e24208..d0f6ac46dad 100644 --- a/drivers/spi/spi_ll_stm32.h +++ b/drivers/spi/spi_ll_stm32.h @@ -68,6 +68,7 @@ struct spi_stm32_data { struct stream dma_rx; struct stream dma_tx; #endif /* CONFIG_SPI_STM32_DMA */ + bool pm_policy_state_on; }; #ifdef CONFIG_SPI_STM32_DMA