drivers: spi: nrfx_spi(m/s): enable cross domain pins for nRF54L15

SPI(M/S)20 and SPIM(M/S)21 instances enable usage of pins on different
port, but require request for constant latency mode. Added
handling of such scenario in the driver. Added testcase
to cover it.

Signed-off-by: Michał Stasiak <michal.stasiak@nordicsemi.no>
This commit is contained in:
Michał Stasiak 2025-05-16 15:57:21 +02:00 committed by Fabio Baltieri
parent e8dd83b43d
commit dd8a8697e2
6 changed files with 288 additions and 22 deletions

View File

@ -43,6 +43,16 @@ LOG_MODULE_REGISTER(spi_nrfx_spim, CONFIG_SPI_LOG_LEVEL);
#define SPI_BUFFER_IN_RAM 1
#endif
/*
* We use NODELABEL here because the nrfx API requires us to call
* functions which are named according to SoC peripheral instance
* being operated on. Since DT_INST() makes no guarantees about that,
* it won't work.
*/
#define SPIM(idx) DT_NODELABEL(spi##idx)
#define SPIM_PROP(idx, prop) DT_PROP(SPIM(idx), prop)
#define SPIM_HAS_PROP(idx, prop) DT_NODE_HAS_PROP(SPIM(idx), prop)
#if defined(CONFIG_CLOCK_CONTROL_NRF_HSFLL_GLOBAL)
#define SPIM_REQUESTS_CLOCK(node) \
DT_NODE_HAS_COMPAT(DT_CLOCKS_CTLR(node), nordic_nrf_hsfll_global)
@ -59,6 +69,28 @@ BUILD_ASSERT(!IS_ENABLED(CONFIG_PM_DEVICE_SYSTEM_MANAGED));
#define SPIM_REQUESTS_CLOCK(node) 0
#endif
#define SPIM_PINS_CROSS_DOMAIN(unused, prefix, idx, _) \
COND_CODE_1(DT_NODE_HAS_STATUS_OKAY(SPIM(prefix##idx)), \
(SPIM_PROP(idx, cross_domain_pins_supported)), \
(0))
#if NRFX_FOREACH_PRESENT(SPIM, SPIM_PINS_CROSS_DOMAIN, (||), (0))
#include <hal/nrf_gpio.h>
/* Certain SPIM instances support usage of cross domain pins in form of dedicated pins on
* a port different from the default one.
*/
#define SPIM_CROSS_DOMAIN_SUPPORTED 1
#endif
#if SPIM_CROSS_DOMAIN_SUPPORTED && defined(CONFIG_NRF_SYS_EVENT)
#include <nrf_sys_event.h>
/* To use cross domain pins, constant latency mode needs to be applied, which is
* handled via nrf_sys_event requests.
*/
#define SPIM_CROSS_DOMAIN_PINS_HANDLE 1
#endif
struct spi_nrfx_data {
struct spi_context ctx;
const struct device *dev;
@ -98,6 +130,10 @@ struct spi_nrfx_config {
const struct device *clk_dev;
struct nrf_clock_spec clk_spec;
#endif
#if SPIM_CROSS_DOMAIN_SUPPORTED
bool cross_domain;
int8_t default_port;
#endif
};
static void event_handler(const nrfx_spim_evt_t *p_event, void *p_context);
@ -147,6 +183,32 @@ static inline void release_clock(const struct device *dev)
#endif
}
#if SPIM_CROSS_DOMAIN_SUPPORTED
static bool spim_has_cross_domain_connection(const struct spi_nrfx_config *config)
{
const struct pinctrl_dev_config *pcfg = config->pcfg;
const struct pinctrl_state *state;
int ret;
ret = pinctrl_lookup_state(pcfg, PINCTRL_STATE_DEFAULT, &state);
if (ret < 0) {
LOG_ERR("Unable to read pin state");
return false;
}
for (uint8_t i = 0U; i < state->pin_cnt; i++) {
uint32_t pin = NRF_GET_PIN(state->pins[i]);
if ((pin != NRF_PIN_DISCONNECTED) &&
(nrf_gpio_pin_port_number_extract(&pin) != config->default_port)) {
return true;
}
}
return false;
}
#endif
static inline void finalize_spi_transaction(const struct device *dev, bool deactivate_cs)
{
struct spi_nrfx_data *dev_data = dev->data;
@ -688,6 +750,19 @@ static int spim_resume(const struct device *dev)
#ifdef CONFIG_SOC_NRF54H20_GPD
nrf_gpd_retain_pins_set(dev_config->pcfg, false);
#endif
#if SPIM_CROSS_DOMAIN_SUPPORTED
if (dev_config->cross_domain && spim_has_cross_domain_connection(dev_config)) {
#if SPIM_CROSS_DOMAIN_PINS_HANDLE
int err;
err = nrf_sys_event_request_global_constlat();
(void)err;
__ASSERT_NO_MSG(err >= 0);
#else
__ASSERT(false, "NRF_SYS_EVENT needs to be enabled to use cross domain pins.\n");
#endif
}
#endif
return pm_device_runtime_is_enabled(dev) ? request_clock(dev) : 0;
}
@ -711,6 +786,19 @@ static void spim_suspend(const struct device *dev)
#ifdef CONFIG_SOC_NRF54H20_GPD
nrf_gpd_retain_pins_set(dev_config->pcfg, true);
#endif
#if SPIM_CROSS_DOMAIN_SUPPORTED
if (dev_config->cross_domain && spim_has_cross_domain_connection(dev_config)) {
#if SPIM_CROSS_DOMAIN_PINS_HANDLE
int err;
err = nrf_sys_event_request_global_constlat();
(void)err;
__ASSERT_NO_MSG(err >= 0);
#else
__ASSERT(false, "NRF_SYS_EVENT needs to be enabled to use cross domain pins.\n");
#endif
}
#endif
(void)pinctrl_apply_state(dev_config->pcfg, PINCTRL_STATE_SLEEP);
}
@ -790,15 +878,6 @@ static int spi_nrfx_deinit(const struct device *dev)
return 0;
}
/*
* We use NODELABEL here because the nrfx API requires us to call
* functions which are named according to SoC peripheral instance
* being operated on. Since DT_INST() makes no guarantees about that,
* it won't work.
*/
#define SPIM(idx) DT_NODELABEL(spi##idx)
#define SPIM_PROP(idx, prop) DT_PROP(SPIM(idx), prop)
#define SPIM_HAS_PROP(idx, prop) DT_NODE_HAS_PROP(SPIM(idx), prop)
#define SPIM_MEM_REGION(idx) DT_PHANDLE(SPIM(idx), memory_regions)
#define SPI_NRFX_SPIM_EXTENDED_CONFIG(idx) \
@ -890,6 +969,11 @@ static int spi_nrfx_deinit(const struct device *dev)
.clk_spec = { \
.frequency = NRF_CLOCK_CONTROL_FREQUENCY_MAX, \
},)) \
IF_ENABLED(SPIM_PINS_CROSS_DOMAIN(_, /*empty*/, idx, _), \
(.cross_domain = true, \
.default_port = \
DT_PROP_OR(DT_PHANDLE(SPIM(idx), \
default_gpio_port), port, -1),)) \
}; \
BUILD_ASSERT(!SPIM_HAS_PROP(idx, wake_gpios) || \
!(DT_GPIO_FLAGS(SPIM(idx), wake_gpios) & GPIO_ACTIVE_LOW),\

View File

@ -36,6 +36,38 @@ LOG_MODULE_REGISTER(spi_nrfx_spis, CONFIG_SPI_LOG_LEVEL);
BUILD_ASSERT(!IS_ENABLED(CONFIG_PM_DEVICE_SYSTEM_MANAGED));
#endif
/*
* Current factors requiring use of DT_NODELABEL:
*
* - HAL design (requirement of drv_inst_idx in nrfx_spis_t)
* - Name-based HAL IRQ handlers, e.g. nrfx_spis_0_irq_handler
*/
#define SPIS_NODE(idx) COND_CODE_1(SPIS_IS_FAST(idx), (spis##idx), (spi##idx))
#define SPIS(idx) DT_NODELABEL(SPIS_NODE(idx))
#define SPIS_PROP(idx, prop) DT_PROP(SPIS(idx), prop)
#define SPIS_HAS_PROP(idx, prop) DT_NODE_HAS_PROP(SPIS(idx), prop)
#define SPIS_PINS_CROSS_DOMAIN(unused, prefix, idx, _) \
COND_CODE_1(DT_NODE_HAS_STATUS_OKAY(SPIS(prefix##idx)), \
(SPIS_PROP(idx, cross_domain_pins_supported)), \
(0))
#if NRFX_FOREACH_PRESENT(SPIS, SPIS_PINS_CROSS_DOMAIN, (||), (0))
#include <hal/nrf_gpio.h>
/* Certain SPIM instances support usage of cross domain pins in form of dedicated pins on
* a port different from the default one.
*/
#define SPIS_CROSS_DOMAIN_SUPPORTED 1
#endif
#if SPIS_CROSS_DOMAIN_SUPPORTED && defined(CONFIG_NRF_SYS_EVENT)
#include <nrf_sys_event.h>
/* To use cross domain pins, constant latency mode needs to be applied, which is
* handled via nrf_sys_event requests.
*/
#define SPIS_CROSS_DOMAIN_PINS_HANDLE 1
#endif
struct spi_nrfx_data {
struct spi_context ctx;
const struct device *dev;
@ -58,8 +90,38 @@ struct spi_nrfx_config {
const struct pinctrl_dev_config *pcfg;
struct gpio_dt_spec wake_gpio;
void *mem_reg;
#if SPIS_CROSS_DOMAIN_SUPPORTED
bool cross_domain;
int8_t default_port;
#endif
};
#if SPIS_CROSS_DOMAIN_SUPPORTED
static bool spis_has_cross_domain_connection(const struct spi_nrfx_config *config)
{
const struct pinctrl_dev_config *pcfg = config->pcfg;
const struct pinctrl_state *state;
int ret;
ret = pinctrl_lookup_state(pcfg, PINCTRL_STATE_DEFAULT, &state);
if (ret < 0) {
LOG_ERR("Unable to read pin state");
return false;
}
for (uint8_t i = 0U; i < state->pin_cnt; i++) {
uint32_t pin = NRF_GET_PIN(state->pins[i]);
if ((pin != NRF_PIN_DISCONNECTED) &&
(nrf_gpio_pin_port_number_extract(&pin) != config->default_port)) {
return true;
}
}
return false;
}
#endif
static inline nrf_spis_mode_t get_nrf_spis_mode(uint16_t operation)
{
if (SPI_MODE_GET(operation) & SPI_MODE_CPOL) {
@ -384,6 +446,19 @@ static void spi_nrfx_suspend(const struct device *dev)
nrf_gpd_retain_pins_set(dev_config->pcfg, true);
}
#endif
#if SPIS_CROSS_DOMAIN_SUPPORTED
if (dev_config->cross_domain && spis_has_cross_domain_connection(dev_config)) {
#if SPIS_CROSS_DOMAIN_PINS_HANDLE
int err;
err = nrf_sys_event_request_global_constlat();
(void)err;
__ASSERT_NO_MSG(err >= 0);
#else
__ASSERT(false, "NRF_SYS_EVENT needs to be enabled to use cross domain pins.\n");
#endif
}
#endif
(void)pinctrl_apply_state(dev_config->pcfg, PINCTRL_STATE_SLEEP);
}
@ -399,6 +474,19 @@ static void spi_nrfx_resume(const struct device *dev)
nrf_gpd_retain_pins_set(dev_config->pcfg, false);
}
#endif
#if SPIS_CROSS_DOMAIN_SUPPORTED
if (dev_config->cross_domain && spis_has_cross_domain_connection(dev_config)) {
#if SPIS_CROSS_DOMAIN_PINS_HANDLE
int err;
err = nrf_sys_event_request_global_constlat();
(void)err;
__ASSERT_NO_MSG(err >= 0);
#else
__ASSERT(false, "NRF_SYS_EVENT needs to be enabled to use cross domain pins.\n");
#endif
}
#endif
if (dev_config->wake_gpio.port == NULL) {
nrf_spis_enable(dev_config->spis.p_reg);
@ -482,19 +570,6 @@ static int spi_nrfx_init(const struct device *dev)
return pm_device_driver_init(dev, spi_nrfx_pm_action);
}
/*
* Current factors requiring use of DT_NODELABEL:
*
* - HAL design (requirement of drv_inst_idx in nrfx_spis_t)
* - Name-based HAL IRQ handlers, e.g. nrfx_spis_0_irq_handler
*/
#define SPIS_NODE(idx) COND_CODE_1(SPIS_IS_FAST(idx), (spis##idx), (spi##idx))
#define SPIS(idx) DT_NODELABEL(SPIS_NODE(idx))
#define SPIS_PROP(idx, prop) DT_PROP(SPIS(idx), prop)
#define SPI_NRFX_SPIS_DEFINE(idx) \
static void irq_connect##idx(void) \
{ \
@ -533,6 +608,11 @@ static int spi_nrfx_init(const struct device *dev)
NRFX_MHZ_TO_HZ(16UL),)) \
.wake_gpio = GPIO_DT_SPEC_GET_OR(SPIS(idx), wake_gpios, {0}), \
.mem_reg = DMM_DEV_TO_REG(SPIS(idx)), \
IF_ENABLED(SPIS_PINS_CROSS_DOMAIN(_, /*empty*/, idx, _), \
(.cross_domain = true, \
.default_port = \
DT_PROP_OR(DT_PHANDLE(SPIS(idx), \
default_gpio_port), port, -1),)) \
}; \
BUILD_ASSERT(!DT_NODE_HAS_PROP(SPIS(idx), wake_gpios) || \
!(DT_GPIO_FLAGS(SPIS(idx), wake_gpios) & GPIO_ACTIVE_LOW),\

View File

@ -61,3 +61,13 @@ properties:
and SPI master again keeps the line in the low state
Please note that the line must be configured and properly handled on
both sides for the mechanism to work correctly.
default-gpio-port:
type: phandle
description: |
SPI default GPIO port.
cross-domain-pins-supported:
type: boolean
description: |
SPI allows usage of cross domain pins with constant latency mode required.

View File

@ -311,6 +311,8 @@
rx-delay-supported;
rx-delay = <1>;
status = "disabled";
default-gpio-port = <&gpio1>;
cross-domain-pins-supported;
};
uart20: uart@c6000 {
@ -352,6 +354,8 @@
rx-delay-supported;
rx-delay = <1>;
status = "disabled";
default-gpio-port = <&gpio1>;
cross-domain-pins-supported;
};
uart21: uart@c7000 {

View File

@ -0,0 +1,73 @@
/*
* Copyright (c) 2025 Nordic Semiconductor
*
* SPDX-License-Identifier: Apache-2.0
*/
&pinctrl {
spi21_default_alt: spi21_default_alt {
group1 {
psels = <NRF_PSEL(SPIM_SCK, 2, 6)>,
<NRF_PSEL(SPIM_MISO, 2, 9)>,
<NRF_PSEL(SPIM_MOSI, 2, 8)>;
};
};
spi21_sleep_alt: spi21_sleep_alt {
group1 {
psels = <NRF_PSEL(SPIM_SCK, 2, 6)>,
<NRF_PSEL(SPIM_MISO, 2, 9)>,
<NRF_PSEL(SPIM_MOSI, 2, 8)>;
low-power-enable;
};
};
spi22_default_alt: spi22_default_alt {
group1 {
psels = <NRF_PSEL(SPIS_SCK, 1, 11)>,
<NRF_PSEL(SPIS_MISO, 1, 13)>,
<NRF_PSEL(SPIS_MOSI, 1, 12)>,
<NRF_PSEL(SPIS_CSN, 1, 14)>;
};
};
spi22_sleep_alt: spi22_sleep_alt {
group1 {
psels = <NRF_PSEL(SPIS_SCK, 1, 11)>,
<NRF_PSEL(SPIS_MISO, 1, 13)>,
<NRF_PSEL(SPIS_MOSI, 1, 12)>,
<NRF_PSEL(SPIS_CSN, 1, 14)>;
low-power-enable;
};
};
};
&gpio2 {
status = "okay";
};
&spi21 {
status = "okay";
pinctrl-0 = <&spi21_default_alt>;
pinctrl-1 = <&spi21_sleep_alt>;
pinctrl-names = "default", "sleep";
overrun-character = <0x00>;
cs-gpios = <&gpio2 10 GPIO_ACTIVE_LOW>;
zephyr,pm-device-runtime-auto;
dut_spi_dt: test-spi-dev@0 {
compatible = "vnd,spi-device";
reg = <0>;
spi-max-frequency = <DT_FREQ_M(1)>;
};
};
dut_spis: &spi22 {
compatible = "nordic,nrf-spis";
status = "okay";
def-char = <0x00>;
pinctrl-0 = <&spi22_default_alt>;
pinctrl-1 = <&spi22_sleep_alt>;
pinctrl-names = "default", "sleep";
/delete-property/rx-delay-supported;
/delete-property/rx-delay;
};

View File

@ -102,3 +102,18 @@ tests:
- CONFIG_PM_DEVICE=y
- CONFIG_PM_DEVICE_RUNTIME=y
filter: CONFIG_SOC_FAMILY_NORDIC_NRF
drivers.spi.spi_cross_domain:
harness_config:
fixture: spi_p1_p2_loopback
extra_configs:
- CONFIG_TESTED_SPI_MODE=0
- CONFIG_NRF_SYS_EVENT=y
extra_args: DTC_OVERLAY_FILE="boards/nrf54l15dk_nrf54l15_cpuapp_cross_domain.overlay"
platform_exclude:
- nrf52840dk/nrf52840
- nrf54h20dk/nrf54h20/cpuapp
- nrf54h20dk/nrf54h20/cpurad
- nrf54h20dk/nrf54h20/cpuppr
- nrf54l20pdk/nrf54l20/cpuapp
- ophelia4ev/nrf54l15/cpuapp