diff --git a/drivers/gpio/gpio_nrfx.c b/drivers/gpio/gpio_nrfx.c index 3d2c1a12f46d7..749dbcd4002f6 100644 --- a/drivers/gpio/gpio_nrfx.c +++ b/drivers/gpio/gpio_nrfx.c @@ -11,6 +11,8 @@ #include #include #include +#include +#include #include @@ -62,53 +64,37 @@ static nrf_gpio_pin_pull_t get_pull(gpio_flags_t flags) return NRF_GPIO_PIN_NOPULL; } -static int gpio_nrfx_gpd_retain_set(const struct device *port, uint32_t mask, gpio_flags_t flags) +static void gpio_nrfx_gpd_retain_set(const struct device *port, uint32_t mask, gpio_flags_t flags) { #ifdef CONFIG_SOC_NRF54H20_GPD const struct gpio_nrfx_cfg *cfg = get_port_cfg(port); - if (cfg->pad_pd == NRF_GPD_FAST_ACTIVE1) { - int ret; - - if (flags & GPIO_OUTPUT) { - nrf_gpio_port_retain_enable(cfg->port, mask); - } - - ret = nrf_gpd_release(NRF_GPD_FAST_ACTIVE1); - if (ret < 0) { - return ret; - } + if (cfg->pad_pd != NRF_GPD_FAST_ACTIVE1 || !(flags & GPIO_OUTPUT)) { + return; } + + nrf_gpio_port_retain_enable(cfg->port, mask); #else ARG_UNUSED(port); ARG_UNUSED(mask); ARG_UNUSED(flags); #endif - - return 0; } -static int gpio_nrfx_gpd_retain_clear(const struct device *port, uint32_t mask) +static void gpio_nrfx_gpd_retain_clear(const struct device *port, uint32_t mask) { #ifdef CONFIG_SOC_NRF54H20_GPD const struct gpio_nrfx_cfg *cfg = get_port_cfg(port); - if (cfg->pad_pd == NRF_GPD_FAST_ACTIVE1) { - int ret; - - ret = nrf_gpd_request(NRF_GPD_FAST_ACTIVE1); - if (ret < 0) { - return ret; - } - - nrf_gpio_port_retain_disable(cfg->port, mask); + if (cfg->pad_pd != NRF_GPD_FAST_ACTIVE1) { + return; } + + nrf_gpio_port_retain_disable(cfg->port, mask); #else ARG_UNUSED(port); ARG_UNUSED(mask); #endif - - return 0; } static int gpio_nrfx_pin_configure(const struct device *port, gpio_pin_t pin, @@ -152,11 +138,13 @@ static int gpio_nrfx_pin_configure(const struct device *port, gpio_pin_t pin, return -EINVAL; } - ret = gpio_nrfx_gpd_retain_clear(port, BIT(pin)); + ret = pm_device_runtime_get(port); if (ret < 0) { return ret; } + gpio_nrfx_gpd_retain_clear(port, BIT(pin)); + if (flags & GPIO_OUTPUT_INIT_HIGH) { nrf_gpio_port_out_set(cfg->port, BIT(pin)); } else if (flags & GPIO_OUTPUT_INIT_LOW) { @@ -237,8 +225,8 @@ static int gpio_nrfx_pin_configure(const struct device *port, gpio_pin_t pin, } end: - (void)gpio_nrfx_gpd_retain_set(port, BIT(pin), flags); - return ret; + gpio_nrfx_gpd_retain_set(port, BIT(pin), flags); + return pm_device_runtime_put(port); } #ifdef CONFIG_GPIO_GET_CONFIG @@ -328,15 +316,16 @@ static int gpio_nrfx_port_set_masked_raw(const struct device *port, const uint32_t set_mask = value & mask; const uint32_t clear_mask = (~set_mask) & mask; - ret = gpio_nrfx_gpd_retain_clear(port, mask); + ret = pm_device_runtime_get(port); if (ret < 0) { return ret; } + gpio_nrfx_gpd_retain_clear(port, mask); nrf_gpio_port_out_set(reg, set_mask); nrf_gpio_port_out_clear(reg, clear_mask); - - return gpio_nrfx_gpd_retain_set(port, mask, GPIO_OUTPUT); + gpio_nrfx_gpd_retain_set(port, mask, GPIO_OUTPUT); + return pm_device_runtime_put(port); } static int gpio_nrfx_port_set_bits_raw(const struct device *port, @@ -345,14 +334,15 @@ static int gpio_nrfx_port_set_bits_raw(const struct device *port, NRF_GPIO_Type *reg = get_port_cfg(port)->port; int ret; - ret = gpio_nrfx_gpd_retain_clear(port, mask); + ret = pm_device_runtime_get(port); if (ret < 0) { return ret; } + gpio_nrfx_gpd_retain_clear(port, mask); nrf_gpio_port_out_set(reg, mask); - - return gpio_nrfx_gpd_retain_set(port, mask, GPIO_OUTPUT); + gpio_nrfx_gpd_retain_set(port, mask, GPIO_OUTPUT); + return pm_device_runtime_put(port); } static int gpio_nrfx_port_clear_bits_raw(const struct device *port, @@ -361,14 +351,15 @@ static int gpio_nrfx_port_clear_bits_raw(const struct device *port, NRF_GPIO_Type *reg = get_port_cfg(port)->port; int ret; - ret = gpio_nrfx_gpd_retain_clear(port, mask); + ret = pm_device_runtime_get(port); if (ret < 0) { return ret; } + gpio_nrfx_gpd_retain_clear(port, mask); nrf_gpio_port_out_clear(reg, mask); - - return gpio_nrfx_gpd_retain_set(port, mask, GPIO_OUTPUT); + gpio_nrfx_gpd_retain_set(port, mask, GPIO_OUTPUT); + return pm_device_runtime_put(port); } static int gpio_nrfx_port_toggle_bits(const struct device *port, @@ -380,15 +371,16 @@ static int gpio_nrfx_port_toggle_bits(const struct device *port, const uint32_t clear_mask = (~value) & mask; int ret; - ret = gpio_nrfx_gpd_retain_clear(port, mask); + ret = pm_device_runtime_get(port); if (ret < 0) { return ret; } + gpio_nrfx_gpd_retain_clear(port, mask); nrf_gpio_port_out_set(reg, set_mask); nrf_gpio_port_out_clear(reg, clear_mask); - - return gpio_nrfx_gpd_retain_set(port, mask, GPIO_OUTPUT); + gpio_nrfx_gpd_retain_set(port, mask, GPIO_OUTPUT); + return pm_device_runtime_put(port); } #ifdef CONFIG_GPIO_NRFX_INTERRUPT @@ -547,17 +539,68 @@ static void nrfx_gpio_handler(nrfx_gpiote_pin_t abs_pin, IRQ_CONNECT(DT_IRQN(node_id), DT_IRQ(node_id, priority), nrfx_isr, \ NRFX_CONCAT(nrfx_gpiote_, DT_PROP(node_id, instance), _irq_handler), 0); +static int gpio_nrfx_pm_suspend(const struct device *port) +{ +#ifdef CONFIG_SOC_NRF54H20_GPD + const struct gpio_nrfx_cfg *cfg = get_port_cfg(port); + + if (cfg->pad_pd != NRF_GPD_FAST_ACTIVE1) { + return 0; + } + + return nrf_gpd_release(NRF_GPD_FAST_ACTIVE1); +#else + ARG_UNUSED(port); + return 0; +#endif +} + +static int gpio_nrfx_pm_resume(const struct device *port) +{ +#ifdef CONFIG_SOC_NRF54H20_GPD + const struct gpio_nrfx_cfg *cfg = get_port_cfg(port); + + if (cfg->pad_pd != NRF_GPD_FAST_ACTIVE1) { + return 0; + } + + return nrf_gpd_request(NRF_GPD_FAST_ACTIVE1); +#else + ARG_UNUSED(port); + return 0; +#endif +} + +static int gpio_nrfx_pm_hook(const struct device *port, enum pm_device_action action) +{ + int ret; + + switch (action) { + case PM_DEVICE_ACTION_SUSPEND: + ret = gpio_nrfx_pm_suspend(port); + break; + case PM_DEVICE_ACTION_RESUME: + ret = gpio_nrfx_pm_resume(port); + break; + default: + ret = -ENOTSUP; + break; + } + + return ret; +} + static int gpio_nrfx_init(const struct device *port) { const struct gpio_nrfx_cfg *cfg = get_port_cfg(port); nrfx_err_t err; if (!has_gpiote(cfg)) { - return 0; + goto pm_init; } if (nrfx_gpiote_init_check(&cfg->gpiote)) { - return 0; + goto pm_init; } err = nrfx_gpiote_init(&cfg->gpiote, 0 /*not used*/); @@ -570,7 +613,8 @@ static int gpio_nrfx_init(const struct device *port) DT_FOREACH_STATUS_OKAY(nordic_nrf_gpiote, GPIOTE_IRQ_HANDLER_CONNECT); #endif /* CONFIG_GPIO_NRFX_INTERRUPT */ - return 0; +pm_init: + return pm_device_driver_init(port, gpio_nrfx_pm_hook); } static DEVICE_API(gpio, gpio_nrfx_drv_api_funcs) = { @@ -635,8 +679,10 @@ static DEVICE_API(gpio, gpio_nrfx_drv_api_funcs) = { \ static struct gpio_nrfx_data gpio_nrfx_p##id##_data; \ \ + PM_DEVICE_DT_INST_DEFINE(id, gpio_nrfx_pm_hook); \ + \ DEVICE_DT_INST_DEFINE(id, gpio_nrfx_init, \ - NULL, \ + PM_DEVICE_DT_INST_GET(id), \ &gpio_nrfx_p##id##_data, \ &gpio_nrfx_p##id##_cfg, \ PRE_KERNEL_1, \ diff --git a/drivers/spi/spi_context.h b/drivers/spi/spi_context.h index e86fb8ba9c912..17bebed545609 100644 --- a/drivers/spi/spi_context.h +++ b/drivers/spi/spi_context.h @@ -15,6 +15,7 @@ #include #include #include +#include #ifdef __cplusplus extern "C" { @@ -274,6 +275,45 @@ static inline int spi_context_cs_configure_all(struct spi_context *ctx) return 0; } +/* Helper function to power manage the GPIO CS pins, not meant to be used directly by drivers */ +static inline int _spi_context_cs_pm_all(struct spi_context *ctx, bool get) +{ + const struct gpio_dt_spec *cs_gpio; + int ret; + + for (cs_gpio = ctx->cs_gpios; cs_gpio < &ctx->cs_gpios[ctx->num_cs_gpios]; cs_gpio++) { + if (get) { + ret = pm_device_runtime_get(cs_gpio->port); + } else { + ret = pm_device_runtime_put(cs_gpio->port); + } + + if (ret < 0) { + return ret; + } + } + + return 0; +} + +/* This function should be called by drivers to pm get all the chip select lines in + * master mode in the case of any CS being a GPIO. This should be called from the + * drivers pm action hook on pm resume. + */ +static inline int spi_context_cs_get_all(struct spi_context *ctx) +{ + return _spi_context_cs_pm_all(ctx, true); +} + +/* This function should be called by drivers to pm put all the chip select lines in + * master mode in the case of any CS being a GPIO. This should be called from the + * drivers pm action hook on pm suspend. + */ +static inline int spi_context_cs_put_all(struct spi_context *ctx) +{ + return _spi_context_cs_pm_all(ctx, false); +} + /* Helper function to control the GPIO CS, not meant to be used directly by drivers */ static inline void _spi_context_cs_control(struct spi_context *ctx, bool on, bool force_off) diff --git a/drivers/spi/spi_nrfx_spim.c b/drivers/spi/spi_nrfx_spim.c index 20456b2a247c3..aa20548af34c1 100644 --- a/drivers/spi/spi_nrfx_spim.c +++ b/drivers/spi/spi_nrfx_spim.c @@ -153,7 +153,12 @@ static inline void finalize_spi_transaction(const struct device *dev, bool deact void *reg = dev_config->spim.p_reg; if (deactivate_cs) { + /* + * We may suspend SPI only if we don't have to keep CS asserted, as we + * need to keep the CS GPIO port resumed until spi_release() in this case. + */ spi_context_cs_control(&dev_data->ctx, false); + pm_device_runtime_put_async(dev, K_NO_WAIT); } if (NRF_SPIM_IS_320MHZ_SPIM(reg) && !(dev_data->ctx.config->operation & SPI_HOLD_ON_CS)) { @@ -163,8 +168,6 @@ static inline void finalize_spi_transaction(const struct device *dev, bool deact if (!IS_ENABLED(CONFIG_PM_DEVICE_RUNTIME)) { release_clock(dev); } - - pm_device_runtime_put_async(dev, K_NO_WAIT); } static inline uint32_t get_nrf_spim_frequency(uint32_t frequency) @@ -402,7 +405,7 @@ static void finish_transaction(const struct device *dev, int error) spi_context_complete(ctx, dev, error); dev_data->busy = false; - finalize_spi_transaction(dev, true); + finalize_spi_transaction(dev, (dev_data->ctx.config->operation & SPI_HOLD_ON_CS) > 0); } static void transfer_next_chunk(const struct device *dev) @@ -661,12 +664,17 @@ static DEVICE_API(spi, spi_nrfx_driver_api) = { static int spim_resume(const struct device *dev) { const struct spi_nrfx_config *dev_config = dev->config; + struct spi_nrfx_data *dev_data = dev->data; (void)pinctrl_apply_state(dev_config->pcfg, PINCTRL_STATE_DEFAULT); /* nrfx_spim_init() will be called at configuration before * the next transfer. */ + if (spi_context_cs_get_all(&dev_data->ctx)) { + return -EAGAIN; + } + #ifdef CONFIG_SOC_NRF54H20_GPD nrf_gpd_retain_pins_set(dev_config->pcfg, false); #endif @@ -688,6 +696,8 @@ static void spim_suspend(const struct device *dev) release_clock(dev); } + spi_context_cs_put_all(&dev_data->ctx); + #ifdef CONFIG_SOC_NRF54H20_GPD nrf_gpd_retain_pins_set(dev_config->pcfg, true); #endif diff --git a/tests/drivers/spi/spi_loopback/boards/nrf54h20dk_nrf54h20_common.dtsi b/tests/drivers/spi/spi_loopback/boards/nrf54h20dk_nrf54h20_common.dtsi index 7b40db16deb3c..33170beda44ea 100644 --- a/tests/drivers/spi/spi_loopback/boards/nrf54h20dk_nrf54h20_common.dtsi +++ b/tests/drivers/spi/spi_loopback/boards/nrf54h20dk_nrf54h20_common.dtsi @@ -39,12 +39,18 @@ reg = <0>; spi-max-frequency = ; }; + cs-gpios = <&gpio7 0 0>; }; &gpio0 { status = "okay"; }; +&gpio7 { + status = "okay"; + zephyr,pm-device-runtime-auto; +}; + &gpiote130 { status = "okay"; }; diff --git a/tests/drivers/spi/spi_loopback/src/spi.c b/tests/drivers/spi/spi_loopback/src/spi.c index 1bfcfe4ca82c4..7f1c8e72277ee 100644 --- a/tests/drivers/spi/spi_loopback/src/spi.c +++ b/tests/drivers/spi/spi_loopback/src/spi.c @@ -15,6 +15,7 @@ #include #include +#include #include #include #include @@ -104,9 +105,12 @@ static void spi_loopback_transceive(struct spi_dt_spec *const spec, const struct spi_buf_set *const tx, const struct spi_buf_set *const rx) { - int ret = spi_transceive_dt(spec, tx, rx); + int ret; - zassert_false(ret, "SPI transceive failed, code %d", ret); + zassert_ok(pm_device_runtime_get(spec->bus)); + ret = spi_transceive_dt(spec, tx, rx); + zassert_ok(ret, "SPI transceive failed, code %d", ret); + zassert_ok(pm_device_runtime_put(spec->bus)); } /* The most spi buf currently used by any test case is 4, change if needed */ @@ -443,9 +447,10 @@ ZTEST(spi_extra_api_features, test_spi_lock_release) lock_spec->config.operation |= SPI_LOCK_ON; + zassert_ok(pm_device_runtime_get(lock_spec->bus)); spi_loopback_transceive(lock_spec, &tx, &rx); - zassert_false(spi_release_dt(lock_spec), "SPI release failed"); + zassert_ok(pm_device_runtime_put(lock_spec->bus)); spi_loopback_transceive(try_spec, &tx, &rx);