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:
parent
e8dd83b43d
commit
dd8a8697e2
@ -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),\
|
||||
|
||||
@ -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),\
|
||||
|
||||
@ -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.
|
||||
|
||||
4
dts/vendor/nordic/nrf54l_05_10_15.dtsi
vendored
4
dts/vendor/nordic/nrf54l_05_10_15.dtsi
vendored
@ -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 {
|
||||
|
||||
@ -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;
|
||||
};
|
||||
@ -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
|
||||
|
||||
Loading…
Reference in New Issue
Block a user