diff --git a/drivers/gpio/gpio_nrfx.c b/drivers/gpio/gpio_nrfx.c index d9eb9129937..2811cdd907a 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) { - cfg->port->RETAINSET = 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; - } - - cfg->port->RETAINCLR = 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,9 +225,79 @@ 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 +static int gpio_nrfx_pin_get_config(const struct device *port, gpio_pin_t pin, + gpio_flags_t *flags) +{ + const struct gpio_nrfx_cfg *cfg = get_port_cfg(port); + nrfx_gpiote_pin_t abs_pin = NRF_GPIO_PIN_MAP(cfg->port_num, pin); + + *flags = 0U; + + nrf_gpio_pin_dir_t dir = nrf_gpio_pin_dir_get(abs_pin); + + if (dir == NRF_GPIO_PIN_DIR_OUTPUT) { + *flags |= GPIO_OUTPUT; + *flags |= nrf_gpio_pin_out_read(abs_pin) + ? GPIO_OUTPUT_INIT_HIGH + : GPIO_OUTPUT_INIT_LOW; + } + + nrf_gpio_pin_input_t input = nrf_gpio_pin_input_get(abs_pin); + + if (input == NRF_GPIO_PIN_INPUT_CONNECT) { + *flags |= GPIO_INPUT; + } + + nrf_gpio_pin_pull_t pull = nrf_gpio_pin_pull_get(abs_pin); + + switch (pull) { + case NRF_GPIO_PIN_PULLUP: + *flags |= GPIO_PULL_UP; + case NRF_GPIO_PIN_PULLDOWN: + *flags |= GPIO_PULL_DOWN; + default: + break; + } + + nrf_gpio_pin_drive_t drive = nrf_gpio_pin_drive_get(abs_pin); + + switch (drive) { + case NRF_GPIO_PIN_S0S1: + *flags |= NRF_GPIO_DRIVE_S0S1; + break; + case NRF_GPIO_PIN_S0H1: + *flags |= NRF_GPIO_DRIVE_S0H1; + break; + case NRF_GPIO_PIN_H0S1: + *flags |= NRF_GPIO_DRIVE_H0S1; + break; + case NRF_GPIO_PIN_H0H1: + *flags |= NRF_GPIO_DRIVE_H0H1; + break; + case NRF_GPIO_PIN_S0D1: + *flags |= NRF_GPIO_DRIVE_S0 | GPIO_OPEN_DRAIN; + break; + case NRF_GPIO_PIN_H0D1: + *flags |= NRF_GPIO_DRIVE_H0 | GPIO_OPEN_DRAIN; + break; + case NRF_GPIO_PIN_D0S1: + *flags |= NRF_GPIO_DRIVE_S1 | GPIO_OPEN_SOURCE; + break; + case NRF_GPIO_PIN_D0H1: + *flags |= NRF_GPIO_DRIVE_H1 | GPIO_OPEN_SOURCE; + break; + default: + break; + } + + return 0; } +#endif static int gpio_nrfx_port_get_raw(const struct device *port, gpio_port_value_t *value) @@ -261,15 +319,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, @@ -278,14 +337,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, @@ -294,14 +354,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, @@ -313,15 +374,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 @@ -480,17 +542,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*/); @@ -503,7 +616,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 const struct gpio_driver_api gpio_nrfx_drv_api_funcs = { @@ -520,6 +634,9 @@ static const struct gpio_driver_api gpio_nrfx_drv_api_funcs = { #ifdef CONFIG_GPIO_GET_DIRECTION .port_get_direction = gpio_nrfx_port_get_direction, #endif +#ifdef CONFIG_GPIO_GET_CONFIG + .pin_get_config = gpio_nrfx_pin_get_config, +#endif }; #define GPIOTE_PHANDLE(id) DT_INST_PHANDLE(id, gpiote_instance) @@ -565,8 +682,10 @@ static const struct gpio_driver_api 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 1aed41173ee..17bebed5456 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" { @@ -75,31 +76,44 @@ struct spi_context { }, \ ._ctx_name.num_cs_gpios = DT_PROP_LEN_OR(_node_id, cs_gpios, 0), +/* + * Checks if a spi config is the same as the one stored in the spi_context + * The intention of this function is to be used to check if a driver can skip + * some reconfiguration for a transfer in a fast code path. + */ static inline bool spi_context_configured(struct spi_context *ctx, const struct spi_config *config) { return !!(ctx->config == config); } +/* Returns true if the spi configuration stored for this context + * specifies a slave mode configuration, returns false otherwise + */ static inline bool spi_context_is_slave(struct spi_context *ctx) { return (ctx->config->operation & SPI_OP_MODE_SLAVE); } +/* + * The purpose of the context lock is to synchronize the usage of the driver/hardware. + * The driver should call this function to claim or wait for ownership of the spi resource. + * Usually the appropriate time to call this is at the start of the transceive API implementation. + */ static inline void spi_context_lock(struct spi_context *ctx, bool asynchronous, spi_callback_t callback, void *callback_data, const struct spi_config *spi_cfg) { - if ((spi_cfg->operation & SPI_LOCK_ON) && - (k_sem_count_get(&ctx->lock) == 0) && - (ctx->owner == spi_cfg)) { - return; - } + bool already_locked = (spi_cfg->operation & SPI_LOCK_ON) && + (k_sem_count_get(&ctx->lock) == 0) && + (ctx->owner == spi_cfg); - k_sem_take(&ctx->lock, K_FOREVER); - ctx->owner = spi_cfg; + if (!already_locked) { + k_sem_take(&ctx->lock, K_FOREVER); + ctx->owner = spi_cfg; + } #ifdef CONFIG_SPI_ASYNC ctx->asynchronous = asynchronous; @@ -108,6 +122,13 @@ static inline void spi_context_lock(struct spi_context *ctx, #endif /* CONFIG_SPI_ASYNC */ } +/* + * This function must be called by a driver which has called spi_context_lock in order + * to release the ownership of the spi resource. + * Usually the appropriate time to call this would be at the end of a transfer that was + * initiated by a transceive API call, except in the case that the SPI_LOCK_ON bit was set + * in the configuration. + */ static inline void spi_context_release(struct spi_context *ctx, int status) { #ifdef CONFIG_SPI_SLAVE @@ -132,6 +153,13 @@ static inline void spi_context_release(struct spi_context *ctx, int status) static inline size_t spi_context_total_tx_len(struct spi_context *ctx); static inline size_t spi_context_total_rx_len(struct spi_context *ctx); +/* This function essentially is a way for a driver to seamlessly implement both the + * synchronous transceive API and the asynchronous transceive_async API in the same way. + * + * The exact way this function is used may depend on driver implementation, but + * essentially this will block waiting for a signal from spi_context_complete, + * unless the transfer is asynchronous, in which case it does nothing in master mode. + */ static inline int spi_context_wait_for_completion(struct spi_context *ctx) { int status = 0; @@ -180,6 +208,12 @@ static inline int spi_context_wait_for_completion(struct spi_context *ctx) return status; } +/* For synchronous transfers, this will signal to a thread waiting + * on spi_context_wait for completion. + * + * For asynchronous tranfers, this will call the async callback function + * with the user data. + */ static inline void spi_context_complete(struct spi_context *ctx, const struct device *dev, int status) @@ -212,6 +246,14 @@ static inline void spi_context_complete(struct spi_context *ctx, #endif /* CONFIG_SPI_ASYNC */ } +/* + * This function initializes all the chip select GPIOs associated with a spi controller. + * The context first must be initialized using the SPI_CONTEXT_CS_GPIOS_INITIALIZE macro. + * This function should be called during the device init sequence so that + * all the CS lines are configured properly before the first transfer begins. + * Note: If a controller has native CS control in SPI hardware, they should also be initialized + * during device init by the driver with hardware-specific code. + */ static inline int spi_context_cs_configure_all(struct spi_context *ctx) { int ret; @@ -233,6 +275,46 @@ 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) { @@ -252,11 +334,22 @@ static inline void _spi_context_cs_control(struct spi_context *ctx, } } +/* This function should be called by drivers to control the chip select line in master mode + * in the case of the CS being a GPIO. The de facto usage of the zephyr SPI API expects that the + * chip select be asserted throughout the entire transfer specified by a transceive call, + * ie all buffers in a spi_buf_set should be finished before deasserting CS. And usually + * the deassertion is at the end of the transfer, except in the case that the + * SPI_HOLD_ON_CS bit was set in the configuration. + */ static inline void spi_context_cs_control(struct spi_context *ctx, bool on) { _spi_context_cs_control(ctx, on, false); } +/* Forcefully releases the spi context and removes the owner, allowing taking the lock + * with spi_context_lock without the previous owner releasing the lock. + * This is usually used to aid in implementation of the spi_release driver API. + */ static inline void spi_context_unlock_unconditionally(struct spi_context *ctx) { /* Forcing CS to go to inactive status */ @@ -268,6 +361,11 @@ static inline void spi_context_unlock_unconditionally(struct spi_context *ctx) } } +/* + * Helper function for incrementing buffer pointer. + * Generally not needed to be used directly by drivers. + * Use spi_context_update_(tx/rx) instead. + */ static inline void *spi_context_get_next_buf(const struct spi_buf **current, size_t *count, size_t *buf_len, @@ -287,6 +385,17 @@ static inline void *spi_context_get_next_buf(const struct spi_buf **current, return NULL; } +/* + * The spi context private api works with the driver by providing code to + * keep track of how much of the transfer has been completed. The driver + * calls functions to report when some tx or rx has finished, and the driver + * then can use the spi context to keep track of how much is left to do. + */ + +/* + * This function must be called at the start of a transfer by the driver + * to initialize the spi context fields for tracking the progress. + */ static inline void spi_context_buffers_setup(struct spi_context *ctx, const struct spi_buf_set *tx_bufs, @@ -321,6 +430,12 @@ void spi_context_buffers_setup(struct spi_context *ctx, (void *)ctx->rx_buf, ctx->rx_len); } +/* + * Should be called to update the tracking of TX being completed. + * + * Parameter "dfs" is the number of bytes needed to store a data frame. + * Parameter "len" is the number of data frames of TX that were sent. + */ static ALWAYS_INLINE void spi_context_update_tx(struct spi_context *ctx, uint8_t dfs, uint32_t len) { @@ -349,18 +464,30 @@ void spi_context_update_tx(struct spi_context *ctx, uint8_t dfs, uint32_t len) LOG_DBG("tx buf/len %p/%zu", (void *)ctx->tx_buf, ctx->tx_len); } +/* Returns true if there is still TX buffers left in the spi_buf_set + * even if they are "null" (nop) buffers. + */ static ALWAYS_INLINE bool spi_context_tx_on(struct spi_context *ctx) { return !!(ctx->tx_len); } +/* Similar to spi_context_tx_on, but only returns true if the current buffer is + * not a null/NOP placeholder. + */ static ALWAYS_INLINE bool spi_context_tx_buf_on(struct spi_context *ctx) { return !!(ctx->tx_buf && ctx->tx_len); } +/* + * Should be called to update the tracking of RX being completed. + * + * @param dfs is the number of bytes needed to store a data frame. + * @param len is the number of data frames of RX that were received. + */ static ALWAYS_INLINE void spi_context_update_rx(struct spi_context *ctx, uint8_t dfs, uint32_t len) { @@ -396,12 +523,18 @@ void spi_context_update_rx(struct spi_context *ctx, uint8_t dfs, uint32_t len) LOG_DBG("rx buf/len %p/%zu", (void *)ctx->rx_buf, ctx->rx_len); } +/* Returns true if there is still RX buffers left in the spi_buf_set + * even if they are "null" (nop) buffers. + */ static ALWAYS_INLINE bool spi_context_rx_on(struct spi_context *ctx) { return !!(ctx->rx_len); } +/* Similar to spi_context_rx_on, but only returns true if the current buffer is + * not a null/NOP placeholder. + */ static ALWAYS_INLINE bool spi_context_rx_buf_on(struct spi_context *ctx) { @@ -412,6 +545,10 @@ bool spi_context_rx_buf_on(struct spi_context *ctx) * Returns the maximum length of a transfer for which all currently active * directions have a continuous buffer, i.e. the maximum SPI transfer that * can be done with DMA that handles only non-scattered buffers. + * + * In other words, returns the length of the smaller of the current RX or current TX buffer. + * Except if either RX or TX buf length is 0, returns the length of the other. + * And if both are 0 then will return 0 and should indicate transfer completion. */ static inline size_t spi_context_max_continuous_chunk(struct spi_context *ctx) { @@ -424,35 +561,71 @@ static inline size_t spi_context_max_continuous_chunk(struct spi_context *ctx) return MIN(ctx->tx_len, ctx->rx_len); } +/* Returns the length of the longer of the current RX or current TX buffer. */ static inline size_t spi_context_longest_current_buf(struct spi_context *ctx) { return ctx->tx_len > ctx->rx_len ? ctx->tx_len : ctx->rx_len; } -static inline size_t spi_context_total_tx_len(struct spi_context *ctx) +/* Helper function, not intended to be used by drivers directly */ +static size_t spi_context_count_tx_buf_lens(struct spi_context *ctx, size_t start_index) { size_t n; size_t total_len = 0; - for (n = 0; n < ctx->tx_count; ++n) { + for (n = start_index; n < ctx->tx_count; ++n) { total_len += ctx->current_tx[n].len; } return total_len; } -static inline size_t spi_context_total_rx_len(struct spi_context *ctx) +/* Helper function, not intended to be used by drivers directly */ +static size_t spi_context_count_rx_buf_lens(struct spi_context *ctx, size_t start_index) { size_t n; size_t total_len = 0; - for (n = 0; n < ctx->rx_count; ++n) { + for (n = start_index; n < ctx->rx_count; ++n) { total_len += ctx->current_rx[n].len; } return total_len; } + +/* Returns the length of the sum of the remaining TX buffers in the buf set, including + * the current buffer in the total. + */ +static inline size_t spi_context_total_tx_len(struct spi_context *ctx) +{ + return spi_context_count_tx_buf_lens(ctx, 0); +} + +/* Returns the length of the sum of the remaining RX buffers in the buf set, including + * the current buffer in the total. + */ +static inline size_t spi_context_total_rx_len(struct spi_context *ctx) +{ + return spi_context_count_rx_buf_lens(ctx, 0); +} + +/* Similar to spi_context_total_tx_len, except does not count words that have been finished + * in the current buffer, ie only including what is remaining in the current buffer in the sum. + */ +static inline size_t spi_context_tx_len_left(struct spi_context *ctx) +{ + return ctx->tx_len + spi_context_count_tx_buf_lens(ctx, 1); +} + +/* Similar to spi_context_total_rx_len, except does not count words that have been finished + * in the current buffer, ie only including what is remaining in the current buffer in the sum. + */ +static inline size_t spi_context_rx_len_left(struct spi_context *ctx) +{ + return ctx->rx_len + spi_context_count_rx_buf_lens(ctx, 1); +} + #ifdef __cplusplus } #endif diff --git a/drivers/spi/spi_nrfx_spi.c b/drivers/spi/spi_nrfx_spi.c index 019cd481ea9..a9e4ad7c7b5 100644 --- a/drivers/spi/spi_nrfx_spi.c +++ b/drivers/spi/spi_nrfx_spi.c @@ -92,6 +92,7 @@ static int configure(const struct device *dev, struct spi_context *ctx = &dev_data->ctx; nrfx_spi_config_t config; nrfx_err_t result; + uint32_t sck_pin; if (dev_data->initialized && spi_context_configured(ctx, spi_cfg)) { /* Already configured. No need to do it again. */ @@ -135,8 +136,11 @@ static int configure(const struct device *dev, config.mode = get_nrf_spi_mode(spi_cfg->operation); config.bit_order = get_nrf_spi_bit_order(spi_cfg->operation); - nrf_gpio_pin_write(nrf_spi_sck_pin_get(dev_config->spi.p_reg), - spi_cfg->operation & SPI_MODE_CPOL ? 1 : 0); + sck_pin = nrf_spi_sck_pin_get(dev_config->spi.p_reg); + + if (sck_pin != NRF_SPI_PIN_NOT_CONNECTED) { + nrf_gpio_pin_write(sck_pin, spi_cfg->operation & SPI_MODE_CPOL ? 1 : 0); + } if (dev_data->initialized) { nrfx_spi_uninit(&dev_config->spi); diff --git a/drivers/spi/spi_nrfx_spim.c b/drivers/spi/spi_nrfx_spim.c index 47c682a6939..f0be599e27d 100644 --- a/drivers/spi/spi_nrfx_spim.c +++ b/drivers/spi/spi_nrfx_spim.c @@ -43,12 +43,18 @@ 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)) +#if defined(CONFIG_CLOCK_CONTROL_NRF2_GLOBAL_HSFLL) +#define SPIM_REQUESTS_CLOCK(node) \ + DT_NODE_HAS_COMPAT(DT_NODELABEL(DT_CLOCKS_CTLR(node)), nordic_nrf_hsfll_global) +#define SPIM_REQUESTS_CLOCK_OR(node) SPIM_REQUESTS_CLOCK(node) || +#if (DT_FOREACH_STATUS_OKAY(nordic_nrf_spim, SPIM_REQUESTS_CLOCK_OR) 0) #define USE_CLOCK_REQUESTS 1 +/* If fast instances are used then system managed device PM cannot be used because + * it may call PM actions from locked context and fast SPIM PM actions can only be + * called from a thread context. + */ +BUILD_ASSERT(!IS_ENABLED(CONFIG_PM_DEVICE_SYSTEM_MANAGED)); +#endif #else #define SPIM_REQUESTS_CLOCK(idx) 0 #endif @@ -218,6 +224,7 @@ static int configure(const struct device *dev, uint32_t max_freq = dev_config->max_freq; nrfx_spim_config_t config; nrfx_err_t result; + uint32_t sck_pin; if (dev_data->initialized && spi_context_configured(ctx, spi_cfg)) { /* Already configured. No need to do it again. */ @@ -274,8 +281,11 @@ static int configure(const struct device *dev, config.mode = get_nrf_spim_mode(spi_cfg->operation); config.bit_order = get_nrf_spim_bit_order(spi_cfg->operation); - nrfy_gpio_pin_write(nrfy_spim_sck_pin_get(dev_config->spim.p_reg), - spi_cfg->operation & SPI_MODE_CPOL ? 1 : 0); + sck_pin = nrfy_spim_sck_pin_get(dev_config->spim.p_reg); + + if (sck_pin != NRF_SPIM_PIN_NOT_CONNECTED) { + nrfy_gpio_pin_write(sck_pin, spi_cfg->operation & SPI_MODE_CPOL ? 1 : 0); + } if (dev_data->initialized) { nrfx_spim_uninit(&dev_config->spim); @@ -389,6 +399,11 @@ static void finish_transaction(const struct device *dev, int error) spi_context_complete(ctx, dev, error); dev_data->busy = false; + if (dev_data->ctx.config->operation & SPI_LOCK_ON) { + /* Keep device resumed until call to spi_release() */ + (void)pm_device_runtime_get(dev); + } + finalize_spi_transaction(dev, true); } @@ -648,12 +663,17 @@ static const struct spi_driver_api 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 @@ -675,6 +695,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 @@ -849,82 +871,7 @@ static int spi_nrfx_init(const struct device *dev) SPIM_MEM_REGION(idx)))))), \ ()) -#ifdef CONFIG_HAS_HW_NRF_SPIM0 -SPI_NRFX_SPIM_DEFINE(0); -#endif - -#ifdef CONFIG_HAS_HW_NRF_SPIM1 -SPI_NRFX_SPIM_DEFINE(1); -#endif - -#ifdef CONFIG_HAS_HW_NRF_SPIM2 -SPI_NRFX_SPIM_DEFINE(2); -#endif - -#ifdef CONFIG_HAS_HW_NRF_SPIM3 -SPI_NRFX_SPIM_DEFINE(3); -#endif - -#ifdef CONFIG_HAS_HW_NRF_SPIM4 -SPI_NRFX_SPIM_DEFINE(4); -#endif - -#ifdef CONFIG_HAS_HW_NRF_SPIM00 -SPI_NRFX_SPIM_DEFINE(00); -#endif - -#ifdef CONFIG_HAS_HW_NRF_SPIM20 -SPI_NRFX_SPIM_DEFINE(20); -#endif - -#ifdef CONFIG_HAS_HW_NRF_SPIM21 -SPI_NRFX_SPIM_DEFINE(21); -#endif - -#ifdef CONFIG_HAS_HW_NRF_SPIM22 -SPI_NRFX_SPIM_DEFINE(22); -#endif - -#ifdef CONFIG_HAS_HW_NRF_SPIM30 -SPI_NRFX_SPIM_DEFINE(30); -#endif - -#ifdef CONFIG_HAS_HW_NRF_SPIM120 -SPI_NRFX_SPIM_DEFINE(120); -#endif - -#ifdef CONFIG_HAS_HW_NRF_SPIM121 -SPI_NRFX_SPIM_DEFINE(121); -#endif - -#ifdef CONFIG_HAS_HW_NRF_SPIM130 -SPI_NRFX_SPIM_DEFINE(130); -#endif - -#ifdef CONFIG_HAS_HW_NRF_SPIM131 -SPI_NRFX_SPIM_DEFINE(131); -#endif - -#ifdef CONFIG_HAS_HW_NRF_SPIM132 -SPI_NRFX_SPIM_DEFINE(132); -#endif - -#ifdef CONFIG_HAS_HW_NRF_SPIM133 -SPI_NRFX_SPIM_DEFINE(133); -#endif - -#ifdef CONFIG_HAS_HW_NRF_SPIM134 -SPI_NRFX_SPIM_DEFINE(134); -#endif - -#ifdef CONFIG_HAS_HW_NRF_SPIM135 -SPI_NRFX_SPIM_DEFINE(135); -#endif +#define COND_NRF_SPIM_DEVICE(unused, prefix, i, _) \ + IF_ENABLED(CONFIG_HAS_HW_NRF_SPIM##prefix##i, (SPI_NRFX_SPIM_DEFINE(prefix##i);)) -#ifdef CONFIG_HAS_HW_NRF_SPIM136 -SPI_NRFX_SPIM_DEFINE(136); -#endif - -#ifdef CONFIG_HAS_HW_NRF_SPIM137 -SPI_NRFX_SPIM_DEFINE(137); -#endif +NRFX_FOREACH_PRESENT(SPIM, COND_NRF_SPIM_DEVICE, (), (), _) diff --git a/include/zephyr/drivers/spi.h b/include/zephyr/drivers/spi.h index 958211e8294..09800a400cb 100644 --- a/include/zephyr/drivers/spi.h +++ b/include/zephyr/drivers/spi.h @@ -100,10 +100,10 @@ extern "C" { #define SPI_WORD_SIZE_SHIFT (5U) #define SPI_WORD_SIZE_MASK (0x3FU << SPI_WORD_SIZE_SHIFT) /** @endcond */ -/** Get SPI word size. */ +/** Get SPI word size (data frame size) in bits. */ #define SPI_WORD_SIZE_GET(_operation_) \ (((_operation_) & SPI_WORD_SIZE_MASK) >> SPI_WORD_SIZE_SHIFT) -/** Set SPI word size. */ +/** Set SPI word size (data frame size) in bits. */ #define SPI_WORD_SET(_word_size_) \ ((_word_size_) << SPI_WORD_SIZE_SHIFT) /** @} */ @@ -309,7 +309,7 @@ struct spi_config { * - 0: Master or slave. * - 1..3: Polarity, phase and loop mode. * - 4: LSB or MSB first. - * - 5..10: Size of a data frame in bits. + * - 5..10: Size of a data frame (word) in bits. * - 11: Full/half duplex. * - 12: Hold on the CS line if possible. * - 13: Keep resource locked for the caller. @@ -458,7 +458,7 @@ struct spi_dt_spec { struct spi_buf { /** Valid pointer to a data buffer, or NULL otherwise */ void *buf; - /** Length of the buffer @a buf. + /** Length of the buffer @a buf in bytes. * If @a buf is NULL, length which as to be sent as dummy bytes (as TX * buffer) or the length of bytes that should be skipped (as RX buffer). */ @@ -471,7 +471,7 @@ struct spi_buf { struct spi_buf_set { /** Pointer to an array of spi_buf, or NULL */ const struct spi_buf *buffers; - /** Length of the array pointed by @a buffers */ + /** Length of the array (number of buffers) pointed by @a buffers */ size_t count; }; diff --git a/samples/boards/nordic/nrfx_prs/src/main.c b/samples/boards/nordic/nrfx_prs/src/main.c index cf989b4c4c9..1b9411d2da4 100644 --- a/samples/boards/nordic/nrfx_prs/src/main.c +++ b/samples/boards/nordic/nrfx_prs/src/main.c @@ -122,6 +122,7 @@ static bool switch_to_spim(void) { int ret; nrfx_err_t err; + uint32_t sck_pin; PINCTRL_DT_DEFINE(SPIM_NODE); @@ -155,8 +156,11 @@ static bool switch_to_spim(void) } /* Set initial state of SCK according to the SPI mode. */ - nrfy_gpio_pin_write(nrfy_spim_sck_pin_get(spim.p_reg), - (spim_config.mode <= NRF_SPIM_MODE_1) ? 0 : 1); + sck_pin = nrfy_spim_sck_pin_get(spim.p_reg); + + if (sck_pin != NRF_SPIM_PIN_NOT_CONNECTED) { + nrfy_gpio_pin_write(sck_pin, (spim_config.mode <= NRF_SPIM_MODE_1) ? 0 : 1); + } err = nrfx_spim_init(&spim, &spim_config, spim_handler, NULL); if (err != NRFX_SUCCESS) { 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 0d31bf6e66e..5b0f52a1acb 100644 --- a/tests/drivers/spi/spi_loopback/boards/nrf54h20dk_nrf54h20_common.dtsi +++ b/tests/drivers/spi/spi_loopback/boards/nrf54h20dk_nrf54h20_common.dtsi @@ -28,6 +28,8 @@ pinctrl-1 = <&spi130_sleep>; pinctrl-names = "default", "sleep"; overrun-character = <0x00>; + zephyr,pm-device-runtime-auto; + cs-gpios = <&gpio7 0 GPIO_ACTIVE_LOW>; slow@0 { compatible = "test-spi-loopback-slow"; reg = <0>; @@ -40,8 +42,14 @@ }; }; +&gpio7 { + status = "okay"; + zephyr,pm-device-runtime-auto; +}; + &gpio0 { status = "okay"; + zephyr,pm-device-runtime-auto; }; &gpiote130 { diff --git a/tests/drivers/spi/spi_loopback/boards/nrf54h20dk_nrf54h20_cpuapp_fast.overlay b/tests/drivers/spi/spi_loopback/boards/nrf54h20dk_nrf54h20_cpuapp_fast.overlay index 2857c48f6fb..c945a194e5b 100644 --- a/tests/drivers/spi/spi_loopback/boards/nrf54h20dk_nrf54h20_cpuapp_fast.overlay +++ b/tests/drivers/spi/spi_loopback/boards/nrf54h20dk_nrf54h20_cpuapp_fast.overlay @@ -4,6 +4,8 @@ * SPDX-License-Identifier: Apache-2.0 */ + + &pinctrl { spi120_default: spi120_default { group1 { @@ -23,6 +25,11 @@ }; }; +&gpio7 { + status = "okay"; + zephyr,pm-device-runtime-auto; +}; + &spi120 { status = "okay"; pinctrl-0 = <&spi120_default>; @@ -31,6 +38,7 @@ overrun-character = <0x00>; memory-regions = <&dma_fast_region>; zephyr,pm-device-runtime-auto; + cs-gpios = <&gpio7 0 GPIO_ACTIVE_LOW>; slow@0 { compatible = "test-spi-loopback-slow"; reg = <0>; @@ -39,6 +47,6 @@ fast@0 { compatible = "test-spi-loopback-fast"; reg = <0>; - spi-max-frequency = ; + spi-max-frequency = ; }; };