From 59c94db6f052c9d848b4cec2008152d32d8ee597 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20G=C5=82=C4=85bek?= Date: Sat, 14 Dec 2024 15:35:55 +0100 Subject: [PATCH] drivers: spi_nrfx_spim: Add clock requests for fast SPIM instances MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fast SPIM instances (SPIM120 and SPIM121) for correct operation require the highest frequency from the global HSFLL. This commit adds needed clock controller requests to the driver. When the runtime device power management is enabled, the frequency is requested as long as the SPIM is resumed, otherwise it is requested for the duration of transfers. This commit also adds a missing call to `pm_device_runtime_put()` when SPIM reconfiguration fails. Signed-off-by: Andrzej Głąbek --- drivers/spi/spi_nrfx_spim.c | 108 +++++++++++++++++++++++++++++++++++- 1 file changed, 105 insertions(+), 3 deletions(-) diff --git a/drivers/spi/spi_nrfx_spim.c b/drivers/spi/spi_nrfx_spim.c index f8d5b60330ee56..12430be9b3330c 100644 --- a/drivers/spi/spi_nrfx_spim.c +++ b/drivers/spi/spi_nrfx_spim.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -42,6 +43,16 @@ LOG_MODULE_REGISTER(spi_nrfx_spim, CONFIG_SPI_LOG_LEVEL); #define SPI_BUFFER_IN_RAM 1 #endif +#if defined(CONFIG_CLOCK_CONTROL_NRF2_GLOBAL_HSFLL) && \ + (defined(CONFIG_HAS_HW_NRF_SPIM120) || \ + defined(CONFIG_HAS_HW_NRF_SPIM121)) +#define SPIM_REQUESTS_CLOCK(idx) UTIL_OR(IS_EQ(idx, 120), \ + IS_EQ(idx, 121)) +#define USE_CLOCK_REQUESTS 1 +#else +#define SPIM_REQUESTS_CLOCK(idx) 0 +#endif + struct spi_nrfx_data { struct spi_context ctx; const struct device *dev; @@ -57,6 +68,9 @@ struct spi_nrfx_data { uint8_t ppi_ch; uint8_t gpiote_ch; #endif +#ifdef USE_CLOCK_REQUESTS + bool clock_requested; +#endif }; struct spi_nrfx_config { @@ -74,10 +88,59 @@ struct spi_nrfx_config { #ifdef CONFIG_DCACHE uint32_t mem_attr; #endif +#ifdef USE_CLOCK_REQUESTS + const struct device *clk_dev; + struct nrf_clock_spec clk_spec; +#endif }; static void event_handler(const nrfx_spim_evt_t *p_event, void *p_context); +static inline int request_clock(const struct device *dev) +{ +#ifdef USE_CLOCK_REQUESTS + struct spi_nrfx_data *dev_data = dev->data; + const struct spi_nrfx_config *dev_config = dev->config; + int error; + + if (!dev_config->clk_dev) { + return 0; + } + + error = nrf_clock_control_request_sync( + dev_config->clk_dev, &dev_config->clk_spec, + K_MSEC(CONFIG_SPI_COMPLETION_TIMEOUT_TOLERANCE)); + if (error < 0) { + LOG_ERR("Failed to request clock: %d", error); + return error; + } + + dev_data->clock_requested = true; +#else + ARG_UNUSED(dev); +#endif + + return 0; +} + +static inline void release_clock(const struct device *dev) +{ +#ifdef USE_CLOCK_REQUESTS + struct spi_nrfx_data *dev_data = dev->data; + const struct spi_nrfx_config *dev_config = dev->config; + + if (!dev_data->clock_requested) { + return; + } + + dev_data->clock_requested = false; + + nrf_clock_control_release(dev_config->clk_dev, &dev_config->clk_spec); +#else + ARG_UNUSED(dev); +#endif +} + static inline void finalize_spi_transaction(const struct device *dev, bool deactivate_cs) { struct spi_nrfx_data *dev_data = dev->data; @@ -92,6 +155,10 @@ static inline void finalize_spi_transaction(const struct device *dev, bool deact nrfy_spim_disable(reg); } + if (!IS_ENABLED(CONFIG_PM_DEVICE_RUNTIME)) { + release_clock(dev); + } + pm_device_runtime_put_async(dev, K_NO_WAIT); } @@ -467,6 +534,11 @@ static int transceive(const struct device *dev, spi_context_lock(&dev_data->ctx, asynchronous, cb, userdata, spi_cfg); error = configure(dev, spi_cfg); + + if (error == 0 && !IS_ENABLED(CONFIG_PM_DEVICE_RUNTIME)) { + error = request_clock(dev); + } + if (error == 0) { dev_data->busy = true; @@ -518,6 +590,8 @@ static int transceive(const struct device *dev, } else if (error) { finalize_spi_transaction(dev, true); } + } else { + pm_device_runtime_put(dev); } spi_context_release(&dev_data->ctx, error); @@ -575,7 +649,7 @@ static DEVICE_API(spi, spi_nrfx_driver_api) = { .release = spi_nrfx_release, }; -static void spim_resume(const struct device *dev) +static int spim_resume(const struct device *dev) { const struct spi_nrfx_config *dev_config = dev->config; @@ -587,6 +661,8 @@ static void spim_resume(const struct device *dev) #ifdef CONFIG_SOC_NRF54H20_GPD nrf_gpd_retain_pins_set(dev_config->pcfg, false); #endif + + return IS_ENABLED(CONFIG_PM_DEVICE_RUNTIME) ? request_clock(dev) : 0; } static void spim_suspend(const struct device *dev) @@ -599,6 +675,10 @@ static void spim_suspend(const struct device *dev) dev_data->initialized = false; } + if (IS_ENABLED(CONFIG_PM_DEVICE_RUNTIME)) { + release_clock(dev); + } + #ifdef CONFIG_SOC_NRF54H20_GPD nrf_gpd_retain_pins_set(dev_config->pcfg, true); #endif @@ -609,7 +689,7 @@ static void spim_suspend(const struct device *dev) static int spim_nrfx_pm_action(const struct device *dev, enum pm_device_action action) { if (action == PM_DEVICE_ACTION_RESUME) { - spim_resume(dev); + return spim_resume(dev); } else if (IS_ENABLED(CONFIG_PM_DEVICE) && (action == PM_DEVICE_ACTION_SUSPEND)) { spim_suspend(dev); } else { @@ -685,6 +765,21 @@ static int spi_nrfx_init(const struct device *dev) (0))), \ (0)) +/* Fast instances depend on the global HSFLL clock controller (as they need + * to request the highest frequency from it to operate correctly), so they + * must be initialized after that controller driver, hence the default SPI + * initialization priority may be too early for them. + */ +#if defined(CONFIG_CLOCK_CONTROL_NRF2_GLOBAL_HSFLL_INIT_PRIORITY) && \ + CONFIG_SPI_INIT_PRIORITY < CONFIG_CLOCK_CONTROL_NRF2_GLOBAL_HSFLL_INIT_PRIORITY +#define SPIM_INIT_PRIORITY(idx) \ + COND_CODE_1(SPIM_REQUESTS_CLOCK(idx), \ + (UTIL_INC(CONFIG_CLOCK_CONTROL_NRF2_GLOBAL_HSFLL_INIT_PRIORITY)), \ + (CONFIG_SPI_INIT_PRIORITY)) +#else +#define SPIM_INIT_PRIORITY(idx) CONFIG_SPI_INIT_PRIORITY +#endif + #define SPI_NRFX_SPIM_DEFINE(idx) \ NRF_DT_CHECK_NODE_HAS_PINCTRL_SLEEP(SPIM(idx)); \ static void irq_connect##idx(void) \ @@ -735,6 +830,13 @@ static int spi_nrfx_init(const struct device *dev) .wake_gpiote = WAKE_GPIOTE_INSTANCE(SPIM(idx)), \ IF_ENABLED(CONFIG_DCACHE, \ (.mem_attr = SPIM_GET_MEM_ATTR(idx),)) \ + IF_ENABLED(USE_CLOCK_REQUESTS, \ + (.clk_dev = SPIM_REQUESTS_CLOCK(idx) \ + ? DEVICE_DT_GET(DT_CLOCKS_CTLR(SPIM(idx))) \ + : NULL, \ + .clk_spec = { \ + .frequency = NRF_CLOCK_CONTROL_FREQUENCY_MAX, \ + },)) \ }; \ BUILD_ASSERT(!SPIM_HAS_PROP(idx, wake_gpios) || \ !(DT_GPIO_FLAGS(SPIM(idx), wake_gpios) & GPIO_ACTIVE_LOW),\ @@ -745,7 +847,7 @@ static int spi_nrfx_init(const struct device *dev) PM_DEVICE_DT_GET(SPIM(idx)), \ &spi_##idx##_data, \ &spi_##idx##z_config, \ - POST_KERNEL, CONFIG_SPI_INIT_PRIORITY, \ + POST_KERNEL, SPIM_INIT_PRIORITY(idx), \ &spi_nrfx_driver_api) #define SPIM_MEMORY_SECTION(idx) \