From 0ecc122cf0b2554c7e3c5b28897e16dad88eea7f Mon Sep 17 00:00:00 2001 From: Bjarki Arge Andreasen Date: Tue, 24 Jun 2025 12:37:25 +0200 Subject: [PATCH 01/11] [nrf fromtree] drivers: gpio: nrfx: extend pin retain to every pin The usage of nrf_gpio_port_retain_disable/_enable, in cases where the soc pins support retention, every pin must be retained/unretained regardless of what power domain the pad is in. This patch ensures retain is applied to all pins in all domains by the gpio_nrfx device driver, not only pins specifically in the fast_active_1 domain. Without this patch, pinctrl will correctly retain pins, while gpio_nrfx will fail to unretain them when again. We no longer check the output state either, which was passed with the flags arg of gpio_nrfx_gpd_retain_set() so this arg has been removed. Signed-off-by: Bjarki Arge Andreasen (cherry picked from commit 691b3356f8fceb5d58600e43140dba55dbe1581d) --- drivers/gpio/gpio_nrfx.c | 21 ++++++--------------- 1 file changed, 6 insertions(+), 15 deletions(-) diff --git a/drivers/gpio/gpio_nrfx.c b/drivers/gpio/gpio_nrfx.c index 39377d7b777..d5480c9149b 100644 --- a/drivers/gpio/gpio_nrfx.c +++ b/drivers/gpio/gpio_nrfx.c @@ -64,20 +64,15 @@ static nrf_gpio_pin_pull_t get_pull(gpio_flags_t flags) return NRF_GPIO_PIN_NOPULL; } -static void 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) { #ifdef CONFIG_SOC_NRF54H20_GPD const struct gpio_nrfx_cfg *cfg = get_port_cfg(port); - 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 } @@ -86,10 +81,6 @@ 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) { - return; - } - nrf_gpio_port_retain_disable(cfg->port, mask); #else ARG_UNUSED(port); @@ -225,7 +216,7 @@ static int gpio_nrfx_pin_configure(const struct device *port, gpio_pin_t pin, } end: - gpio_nrfx_gpd_retain_set(port, BIT(pin), flags); + gpio_nrfx_gpd_retain_set(port, BIT(pin)); return pm_device_runtime_put(port); } @@ -329,7 +320,7 @@ static int gpio_nrfx_port_set_masked_raw(const struct device *port, gpio_nrfx_gpd_retain_clear(port, mask); nrf_gpio_port_out_set(reg, set_mask); nrf_gpio_port_out_clear(reg, clear_mask); - gpio_nrfx_gpd_retain_set(port, mask, GPIO_OUTPUT); + gpio_nrfx_gpd_retain_set(port, mask); return pm_device_runtime_put(port); } @@ -346,7 +337,7 @@ static int gpio_nrfx_port_set_bits_raw(const struct device *port, gpio_nrfx_gpd_retain_clear(port, mask); nrf_gpio_port_out_set(reg, mask); - gpio_nrfx_gpd_retain_set(port, mask, GPIO_OUTPUT); + gpio_nrfx_gpd_retain_set(port, mask); return pm_device_runtime_put(port); } @@ -363,7 +354,7 @@ static int gpio_nrfx_port_clear_bits_raw(const struct device *port, gpio_nrfx_gpd_retain_clear(port, mask); nrf_gpio_port_out_clear(reg, mask); - gpio_nrfx_gpd_retain_set(port, mask, GPIO_OUTPUT); + gpio_nrfx_gpd_retain_set(port, mask); return pm_device_runtime_put(port); } @@ -384,7 +375,7 @@ static int gpio_nrfx_port_toggle_bits(const struct device *port, gpio_nrfx_gpd_retain_clear(port, mask); nrf_gpio_port_out_set(reg, set_mask); nrf_gpio_port_out_clear(reg, clear_mask); - gpio_nrfx_gpd_retain_set(port, mask, GPIO_OUTPUT); + gpio_nrfx_gpd_retain_set(port, mask); return pm_device_runtime_put(port); } From 3834c31ff446e13395859d7df1eb3d6151aef0fb Mon Sep 17 00:00:00 2001 From: Bjarki Arge Andreasen Date: Wed, 25 Jun 2025 15:55:03 +0200 Subject: [PATCH 02/11] [nrf fromtree] drivers: pinctrl: nrf: patch pin retain to follow lp mode Pins can be configured to retain their config even when the power domain they belong to is suspended. Update pinctrl_nrf to enable retain only if the pin has been configured and is not in use (pincnf is low-power/sleep), disable retain otherwise. Signed-off-by: Bjarki Arge Andreasen (cherry picked from commit 652635fab9d41d763716cd188414b45f4049c7a9) --- drivers/pinctrl/pinctrl_nrf.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/drivers/pinctrl/pinctrl_nrf.c b/drivers/pinctrl/pinctrl_nrf.c index 05c0ed9ac0b..34c0107b591 100644 --- a/drivers/pinctrl/pinctrl_nrf.c +++ b/drivers/pinctrl/pinctrl_nrf.c @@ -516,9 +516,13 @@ int pinctrl_configure_pins(const pinctrl_soc_pin_t *pins, uint8_t pin_cnt, } gpd_requested = true; } - - nrf_gpio_pin_retain_disable(pin); } + + /* + * Pad power domain now on, retain no longer needed + * as pad config will be persists as pad is powered. + */ + nrf_gpio_pin_retain_disable(pin); #endif /* CONFIG_SOC_NRF54H20_GPD */ if (write != NO_WRITE) { @@ -537,7 +541,13 @@ int pinctrl_configure_pins(const pinctrl_soc_pin_t *pins, uint8_t pin_cnt, nrf_gpio_pin_clock_set(pin, NRF_GET_CLOCKPIN_ENABLE(pins[i])); #endif #ifdef CONFIG_SOC_NRF54H20_GPD - if (NRF_GET_GPD_FAST_ACTIVE1(pins[i]) == 1U) { + if (NRF_GET_LP(pins[i]) == NRF_LP_ENABLE) { + /* + * Pad power domain may be turned off, and pad is not + * actively used as pincnf is low-power. Enable retain + * to ensure pad output and config persists if pad + * power domain is suspended. + */ nrf_gpio_pin_retain_enable(pin); } #endif /* CONFIG_SOC_NRF54H20_GPD */ From 62167f1ef97e8e42db90996b8621a44f731455e8 Mon Sep 17 00:00:00 2001 From: Bjarki Arge Andreasen Date: Fri, 9 May 2025 12:44:01 +0200 Subject: [PATCH 03/11] [nrf fromtree] drivers: spi: add DEINIT_ variants of SPI_DEVICE_ macros Add DEVICE_DEINIT variants of SPI_DEVICE_ macros. These include - SPI_DEVICE_DT_DEINIT_DEFINE() - SPI_DEVICE_DT_INST_DEINIT_DEFINE() Signed-off-by: Bjarki Arge Andreasen (cherry picked from commit 1663253de65a9c0196581685c53de1f4b4e244a5) --- include/zephyr/drivers/spi.h | 56 +++++++++++++++++++++++++++++------- 1 file changed, 46 insertions(+), 10 deletions(-) diff --git a/include/zephyr/drivers/spi.h b/include/zephyr/drivers/spi.h index 8e8cdb5a994..08a3f03137b 100644 --- a/include/zephyr/drivers/spi.h +++ b/include/zephyr/drivers/spi.h @@ -657,16 +657,16 @@ struct spi_device_state { } /** @endcond */ -#define SPI_DEVICE_DT_DEFINE(node_id, init_fn, pm_device, \ - data_ptr, cfg_ptr, level, prio, \ - api_ptr, ...) \ +#define SPI_DEVICE_DT_DEINIT_DEFINE(node_id, init_fn, deinit_fn, \ + pm_device, data_ptr, cfg_ptr, \ + level, prio, api_ptr, ...) \ Z_SPI_DEVICE_STATE_DEFINE(Z_DEVICE_DT_DEV_ID(node_id)); \ Z_SPI_INIT_FN(Z_DEVICE_DT_DEV_ID(node_id), init_fn) \ Z_DEVICE_DEFINE(node_id, Z_DEVICE_DT_DEV_ID(node_id), \ DEVICE_DT_NAME(node_id), \ &UTIL_CAT(Z_DEVICE_DT_DEV_ID(node_id), _init), \ - NULL, Z_DEVICE_DT_FLAGS(node_id), pm_device, \ - data_ptr, cfg_ptr, level, prio, \ + deinit_fn, Z_DEVICE_DT_FLAGS(node_id), \ + pm_device, data_ptr, cfg_ptr, level, prio, \ api_ptr, \ &(Z_DEVICE_STATE_NAME(Z_DEVICE_DT_DEV_ID(node_id)).devstate), \ __VA_ARGS__) @@ -700,8 +700,9 @@ static inline void spi_transceive_stats(const struct device *dev, int error, * @name SPI DT Device Macros * @{ */ + /** - * @brief Like DEVICE_DT_DEFINE() with SPI specifics. + * @brief Like DEVICE_DT_DEINIT_DEFINE() with SPI specifics. * * @details Defines a device which implements the SPI API. May * generate a custom device_state container struct and init_fn @@ -709,6 +710,7 @@ static inline void spi_transceive_stats(const struct device *dev, int error, * * @param node_id The devicetree node identifier. * @param init_fn Name of the init function of the driver. + * @param deinit_fn Name of the deinit function of the driver. * @param pm PM device resources reference (NULL if device does not use PM). * @param data Pointer to the device's private data. * @param config The address to the structure containing the configuration @@ -719,16 +721,16 @@ static inline void spi_transceive_stats(const struct device *dev, int error, * @param api Provides an initial pointer to the API function struct used by * the driver. Can be NULL. */ -#define SPI_DEVICE_DT_DEFINE(node_id, init_fn, pm, \ - data, config, level, prio, \ - api, ...) \ +#define SPI_DEVICE_DT_DEINIT_DEFINE(node_id, init_fn, deinit_fn, pm, data, \ + config, level, prio, api, ...) \ Z_DEVICE_STATE_DEFINE(Z_DEVICE_DT_DEV_ID(node_id)); \ Z_DEVICE_DEFINE(node_id, Z_DEVICE_DT_DEV_ID(node_id), \ - DEVICE_DT_NAME(node_id), init_fn, NULL, \ + DEVICE_DT_NAME(node_id), init_fn, deinit_fn, \ Z_DEVICE_DT_FLAGS(node_id), pm, data, config, \ level, prio, api, \ &Z_DEVICE_STATE_NAME(Z_DEVICE_DT_DEV_ID(node_id)), \ __VA_ARGS__) + /** @} */ #define SPI_STATS_RX_BYTES_INC(dev_) @@ -739,6 +741,40 @@ static inline void spi_transceive_stats(const struct device *dev, int error, #endif /*CONFIG_SPI_STATS*/ +/** + * @brief Like DEVICE_DT_DEINIT_DEFINE() without deinit function. + * + * @details Defines a device which implements the SPI API. May + * generate a custom device_state container struct and init_fn + * wrapper when needed depending on SPI @kconfig{CONFIG_SPI_STATS}. + * + * @param node_id The devicetree node identifier. + * @param init_fn Name of the init function of the driver. + * @param pm PM device resources reference (NULL if device does not use PM). + * @param data Pointer to the device's private data. + * @param config The address to the structure containing the configuration + * information for this instance of the driver. + * @param level The initialization level. See SYS_INIT() for details. + * @param prio Priority within the selected initialization level. See SYS_INIT() + * for details. + * @param api Provides an initial pointer to the API function struct used by + * the driver. Can be NULL. + */ +#define SPI_DEVICE_DT_DEFINE(node_id, init_fn, pm, data, config, level, prio, \ + api, ...) \ + SPI_DEVICE_DT_DEINIT_DEFINE(node_id, init_fn, NULL, pm, data, config, \ + level, prio, api, __VA_ARGS__) + +/** + * @brief Like SPI_DEVICE_DT_DEINIT_DEFINE(), but uses an instance of a `DT_DRV_COMPAT` + * compatible instead of a node identifier. + * + * @param inst Instance number. The `node_id` argument to SPI_DEVICE_DT_DEINIT_DEFINE() is + * set to `DT_DRV_INST(inst)`. + * @param ... Other parameters as expected by SPI_DEVICE_DT_DEFINE(). + */ +#define SPI_DEVICE_DT_INST_DEINIT_DEFINE(inst, ...) \ + SPI_DEVICE_DT_DEINIT_DEFINE(DT_DRV_INST(inst), __VA_ARGS__) /** * @brief Like SPI_DEVICE_DT_DEFINE(), but uses an instance of a `DT_DRV_COMPAT` From 37980bebb8e33aebb36ee7d119799703556b00b7 Mon Sep 17 00:00:00 2001 From: Bjarki Arge Andreasen Date: Fri, 9 May 2025 16:18:33 +0200 Subject: [PATCH 04/11] [nrf fromtree] drivers: spi: nrfx spim: impl device deinit Implement device deinit for nRF SPIM device driver. Signed-off-by: Bjarki Arge Andreasen (cherry picked from commit 2609cfbf4a9558146d6970cf3222dde7480cb4ad) --- drivers/spi/spi_nrfx_spim.c | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/drivers/spi/spi_nrfx_spim.c b/drivers/spi/spi_nrfx_spim.c index dcfee1c5ede..4faf29934c5 100644 --- a/drivers/spi/spi_nrfx_spim.c +++ b/drivers/spi/spi_nrfx_spim.c @@ -765,6 +765,28 @@ static int spi_nrfx_init(const struct device *dev) #endif return pm_device_driver_init(dev, spim_nrfx_pm_action); } + +static int spi_nrfx_deinit(const struct device *dev) +{ +#if defined(CONFIG_PM_DEVICE) + enum pm_device_state state; + + /* + * PM must have suspended the device before driver can + * be deinitialized + */ + (void)pm_device_state_get(dev, &state); + return state == PM_DEVICE_STATE_SUSPENDED || + state == PM_DEVICE_STATE_OFF ? + 0 : -EBUSY; +#else + /* PM suspend implementation does everything we need */ + spim_suspend(dev); +#endif + + return 0; +} + /* * We use NODELABEL here because the nrfx API requires us to call * functions which are named according to SoC peripheral instance @@ -870,8 +892,9 @@ static int spi_nrfx_init(const struct device *dev) !(DT_GPIO_FLAGS(SPIM(idx), wake_gpios) & GPIO_ACTIVE_LOW),\ "WAKE line must be configured as active high"); \ PM_DEVICE_DT_DEFINE(SPIM(idx), spim_nrfx_pm_action); \ - SPI_DEVICE_DT_DEFINE(SPIM(idx), \ + SPI_DEVICE_DT_DEINIT_DEFINE(SPIM(idx), \ spi_nrfx_init, \ + spi_nrfx_deinit, \ PM_DEVICE_DT_GET(SPIM(idx)), \ &spi_##idx##_data, \ &spi_##idx##z_config, \ From d462473032fe2d2c4ca19ccdc177f1bc423bb15c Mon Sep 17 00:00:00 2001 From: Bjarki Arge Andreasen Date: Tue, 24 Jun 2025 14:02:57 +0200 Subject: [PATCH 05/11] [nrf fromtree] tests: drivers: spi: loopback: test device_deinit Introduce test for device_deinit() which deinitializes the spi bus, then configures miso as input, mosi as output using gpio, utilizing the loopback to test directly controlling the pins. Then reinit the spi device. Signed-off-by: Bjarki Arge Andreasen (cherry picked from commit ecb5457898d748dbd906778044d058df2a15b53c) --- tests/drivers/spi/spi_loopback/src/spi.c | 33 +++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/tests/drivers/spi/spi_loopback/src/spi.c b/tests/drivers/spi/spi_loopback/src/spi.c index 64649ce7566..b6728db9dae 100644 --- a/tests/drivers/spi/spi_loopback/src/spi.c +++ b/tests/drivers/spi/spi_loopback/src/spi.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -56,7 +57,8 @@ static int spec_idx; */ struct spi_dt_spec spec_copies[5]; - +const struct gpio_dt_spec miso_pin = GPIO_DT_SPEC_GET_OR(DT_PATH(zephyr_user), miso_gpios, {}); +const struct gpio_dt_spec mosi_pin = GPIO_DT_SPEC_GET_OR(DT_PATH(zephyr_user), mosi_gpios, {}); /* ******************** @@ -806,6 +808,35 @@ ZTEST(spi_loopback, test_spi_concurrent_transfer_different_spec) test_spi_concurrent_transfer_helper(specs); } +ZTEST(spi_loopback, test_spi_deinit) +{ + struct spi_dt_spec *spec = loopback_specs[0]; + const struct device *dev = spec->bus; + int ret; + + if (miso_pin.port == NULL || mosi_pin.port == NULL) { + TC_PRINT(" zephyr,user miso-gpios or mosi-gpios are not defined\n"); + ztest_test_skip(); + } + + ret = device_deinit(dev); + if (ret == -ENOTSUP) { + TC_PRINT(" device deinit not supported\n"); + ztest_test_skip(); + } + + zassert_ok(ret); + zassert_ok(gpio_pin_configure_dt(&miso_pin, GPIO_INPUT)); + zassert_ok(gpio_pin_configure_dt(&mosi_pin, GPIO_OUTPUT_INACTIVE)); + zassert_equal(gpio_pin_get_dt(&miso_pin), 0); + zassert_ok(gpio_pin_set_dt(&mosi_pin, 1)); + zassert_equal(gpio_pin_get_dt(&miso_pin), 1); + zassert_ok(gpio_pin_set_dt(&mosi_pin, 0)); + zassert_equal(gpio_pin_get_dt(&miso_pin), 0); + zassert_ok(gpio_pin_configure_dt(&mosi_pin, GPIO_INPUT)); + zassert_ok(device_init(dev)); +} + #if (CONFIG_SPI_ASYNC) static struct k_poll_signal async_sig = K_POLL_SIGNAL_INITIALIZER(async_sig); static struct k_poll_event async_evt = From d71381cb740761b1d4b782258e79b3594f85c851 Mon Sep 17 00:00:00 2001 From: Bjarki Arge Andreasen Date: Tue, 24 Jun 2025 15:00:30 +0200 Subject: [PATCH 06/11] [nrf fromtree] tests: drivers: spi: loopback: extend nrf overlays with miso/mosi Add miso-gpios and mosi-gpios to nrf overlays required for device_deinit testing. Signed-off-by: Bjarki Arge Andreasen (cherry picked from commit 160c196435b4e38dc97d49ad4fd64b1505915a61) --- .../spi_loopback/boards/nrf54h20dk_nrf54h20_common.dtsi | 9 +++++++++ 1 file changed, 9 insertions(+) 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 33170beda44..5111257a557 100644 --- a/tests/drivers/spi/spi_loopback/boards/nrf54h20dk_nrf54h20_common.dtsi +++ b/tests/drivers/spi/spi_loopback/boards/nrf54h20dk_nrf54h20_common.dtsi @@ -4,6 +4,15 @@ * SPDX-License-Identifier: Apache-2.0 */ +#include + +/ { + zephyr,user { + miso-gpios = <&gpio0 6 GPIO_ACTIVE_HIGH>; + mosi-gpios = <&gpio0 7 GPIO_ACTIVE_HIGH>; + }; +}; + &pinctrl { spi130_default: spi130_default { group1 { From 9459af9ee5b3a6ff0f3994cd9aa8c45f3523fd3a Mon Sep 17 00:00:00 2001 From: Bjarki Arge Andreasen Date: Thu, 19 Jun 2025 15:46:40 +0200 Subject: [PATCH 07/11] [nrf fromtree] drivers: i2c: add I2C_DEVICE_DT_DEINIT macros Add device deinit variants of the I2C_DEVICE_DT_*_DEFINE macros. Signed-off-by: Bjarki Arge Andreasen (cherry picked from commit 8a928ea1d7f57880a63693619cac99a6e673d18f) --- include/zephyr/drivers/i2c.h | 37 ++++++++++++++++++++++++++++-------- 1 file changed, 29 insertions(+), 8 deletions(-) diff --git a/include/zephyr/drivers/i2c.h b/include/zephyr/drivers/i2c.h index 09640e3275d..c1972782660 100644 --- a/include/zephyr/drivers/i2c.h +++ b/include/zephyr/drivers/i2c.h @@ -635,7 +635,7 @@ static inline void i2c_xfer_stats(const struct device *dev, struct i2c_msg *msgs /** @endcond */ /** - * @brief Like DEVICE_DT_DEFINE() with I2C specifics. + * @brief Like DEVICE_DT_DEINIT_DEFINE() with I2C specifics. * * @details Defines a device which implements the I2C API. May * generate a custom device_state container struct and init_fn @@ -645,6 +645,8 @@ static inline void i2c_xfer_stats(const struct device *dev, struct i2c_msg *msgs * * @param init_fn Name of the init function of the driver. Can be `NULL`. * + * @param deinit_fn Name of the deinit function of the driver. Can be `NULL`. + * * @param pm PM device resources reference (NULL if device does not use PM). * * @param data Pointer to the device's private data. @@ -661,14 +663,14 @@ static inline void i2c_xfer_stats(const struct device *dev, struct i2c_msg *msgs * @param api Provides an initial pointer to the API function struct * used by the driver. Can be NULL. */ -#define I2C_DEVICE_DT_DEFINE(node_id, init_fn, pm, data, config, level, \ - prio, api, ...) \ +#define I2C_DEVICE_DT_DEINIT_DEFINE(node_id, init_fn, deinit_fn, pm, \ + data, config, level, prio, api, ...)\ Z_I2C_DEVICE_STATE_DEFINE(Z_DEVICE_DT_DEV_ID(node_id)); \ Z_I2C_INIT_FN(Z_DEVICE_DT_DEV_ID(node_id), init_fn) \ Z_DEVICE_DEFINE(node_id, Z_DEVICE_DT_DEV_ID(node_id), \ DEVICE_DT_NAME(node_id), \ &UTIL_CAT(Z_DEVICE_DT_DEV_ID(node_id), _init), \ - NULL, Z_DEVICE_DT_FLAGS(node_id), pm, data, \ + deinit_fn, Z_DEVICE_DT_FLAGS(node_id), pm, data,\ config, level, prio, api, \ &(Z_DEVICE_STATE_NAME(Z_DEVICE_DT_DEV_ID(node_id)).devstate), \ __VA_ARGS__) @@ -683,12 +685,32 @@ static inline void i2c_xfer_stats(const struct device *dev, struct i2c_msg *msgs ARG_UNUSED(num_msgs); } +#define I2C_DEVICE_DT_DEINIT_DEFINE(node_id, init_fn, deinit_fn, pm, \ + data, config, level, prio, api, ...)\ + DEVICE_DT_DEINIT_DEFINE(node_id, init_fn, deinit_fn, pm, data, \ + config, level, prio, api, __VA_ARGS__) + +#endif /* CONFIG_I2C_STATS */ + +/** + * @brief Like I2C_DEVICE_DT_DEINIT_DEFINE() but without deinit_fn + */ #define I2C_DEVICE_DT_DEFINE(node_id, init_fn, pm, data, config, level, \ prio, api, ...) \ - DEVICE_DT_DEFINE(node_id, init_fn, pm, data, config, level, \ - prio, api, __VA_ARGS__) + I2C_DEVICE_DT_DEINIT_DEFINE(node_id, init_fn, NULL, pm, data, \ + config, level, prio, api, \ + __VA_ARGS__) -#endif /* CONFIG_I2C_STATS */ +/** + * @brief Like I2C_DEVICE_DT_DEINIT_DEFINE() for an instance of a DT_DRV_COMPAT compatible + * + * @param inst instance number. This is replaced by + * DT_DRV_COMPAT(inst) in the call to I2C_DEVICE_DT_DEINIT_DEFINE(). + * + * @param ... other parameters as expected by I2C_DEVICE_DT_DEINIT_DEFINE(). + */ +#define I2C_DEVICE_DT_INST_DEINIT_DEFINE(inst, ...) \ + I2C_DEVICE_DT_DEINIT_DEFINE(DT_DRV_INST(inst), __VA_ARGS__) /** * @brief Like I2C_DEVICE_DT_DEFINE() for an instance of a DT_DRV_COMPAT compatible @@ -701,7 +723,6 @@ static inline void i2c_xfer_stats(const struct device *dev, struct i2c_msg *msgs #define I2C_DEVICE_DT_INST_DEFINE(inst, ...) \ I2C_DEVICE_DT_DEFINE(DT_DRV_INST(inst), __VA_ARGS__) - /** * @brief Configure operation of a host controller. * From ba9f53e5ff5814daea80d4baf060339d2682fae5 Mon Sep 17 00:00:00 2001 From: Bjarki Arge Andreasen Date: Thu, 19 Jun 2025 15:49:20 +0200 Subject: [PATCH 08/11] [nrf fromtree] drivers: i2c: nrfx_twim: impl device deinit Implement device deinit for the nRF TWIM device drivers. Signed-off-by: Bjarki Arge Andreasen (cherry picked from commit bd73c739b4d22fcb2bc6d09bc988941d8b962568) --- drivers/i2c/i2c_nrfx_twim.c | 8 ++++- drivers/i2c/i2c_nrfx_twim_common.c | 50 +++++++++++++++++++++++++++--- drivers/i2c/i2c_nrfx_twim_common.h | 1 + drivers/i2c/i2c_nrfx_twim_rtio.c | 14 ++++++--- 4 files changed, 63 insertions(+), 10 deletions(-) diff --git a/drivers/i2c/i2c_nrfx_twim.c b/drivers/i2c/i2c_nrfx_twim.c index d5b619bfc5a..b6c37f455dc 100644 --- a/drivers/i2c/i2c_nrfx_twim.c +++ b/drivers/i2c/i2c_nrfx_twim.c @@ -224,6 +224,11 @@ static int i2c_nrfx_twim_init(const struct device *dev) return i2c_nrfx_twim_common_init(dev); } +static int i2c_nrfx_twim_deinit(const struct device *dev) +{ + return i2c_nrfx_twim_common_deinit(dev); +} + static DEVICE_API(i2c, i2c_nrfx_twim_driver_api) = { .configure = i2c_nrfx_twim_configure, .transfer = i2c_nrfx_twim_transfer, @@ -280,8 +285,9 @@ static DEVICE_API(i2c, i2c_nrfx_twim_driver_api) = { }; \ PM_DEVICE_DT_DEFINE(I2C(idx), twim_nrfx_pm_action, \ PM_DEVICE_ISR_SAFE); \ - I2C_DEVICE_DT_DEFINE(I2C(idx), \ + I2C_DEVICE_DT_DEINIT_DEFINE(I2C(idx), \ i2c_nrfx_twim_init, \ + i2c_nrfx_twim_deinit, \ PM_DEVICE_DT_GET(I2C(idx)), \ &twim_##idx##_data, \ &twim_##idx##z_config, \ diff --git a/drivers/i2c/i2c_nrfx_twim_common.c b/drivers/i2c/i2c_nrfx_twim_common.c index 1539d7c90dd..f7fd0d097b0 100644 --- a/drivers/i2c/i2c_nrfx_twim_common.c +++ b/drivers/i2c/i2c_nrfx_twim_common.c @@ -102,18 +102,30 @@ int i2c_nrfx_twim_msg_transfer(const struct device *dev, uint8_t flags, uint8_t return ret; } -int twim_nrfx_pm_action(const struct device *dev, enum pm_device_action action) +void twim_nrfx_pm_resume(const struct device *dev) { const struct i2c_nrfx_twim_common_config *config = dev->config; + (void)pinctrl_apply_state(config->pcfg, PINCTRL_STATE_DEFAULT); + nrfx_twim_enable(&config->twim); +} + +void twim_nrfx_pm_suspend(const struct device *dev) +{ + const struct i2c_nrfx_twim_common_config *config = dev->config; + + nrfx_twim_disable(&config->twim); + (void)pinctrl_apply_state(config->pcfg, PINCTRL_STATE_SLEEP); +} + +int twim_nrfx_pm_action(const struct device *dev, enum pm_device_action action) +{ switch (action) { case PM_DEVICE_ACTION_RESUME: - (void)pinctrl_apply_state(config->pcfg, PINCTRL_STATE_DEFAULT); - nrfx_twim_enable(&config->twim); + twim_nrfx_pm_resume(dev); break; case PM_DEVICE_ACTION_SUSPEND: - nrfx_twim_disable(&config->twim); - (void)pinctrl_apply_state(config->pcfg, PINCTRL_STATE_SLEEP); + twim_nrfx_pm_suspend(dev); break; default: return -ENOTSUP; @@ -138,3 +150,31 @@ int i2c_nrfx_twim_common_init(const struct device *dev) return pm_device_driver_init(dev, twim_nrfx_pm_action); } + +int i2c_nrfx_twim_common_deinit(const struct device *dev) +{ + const struct i2c_nrfx_twim_common_config *config = dev->config; +#if CONFIG_PM_DEVICE + enum pm_device_state state; +#endif + +#if CONFIG_PM_DEVICE + /* + * PM must have suspended the device before driver can + * be deinitialized + */ + (void)pm_device_state_get(dev, &state); + if (state != PM_DEVICE_STATE_SUSPENDED && + state != PM_DEVICE_STATE_OFF) { + LOG_ERR("device active"); + return -EBUSY; + } +#else + /* Suspend device */ + twim_nrfx_pm_suspend(dev); +#endif + + /* Uninit device hardware */ + nrfx_twim_uninit(&config->twim); + return 0; +} diff --git a/drivers/i2c/i2c_nrfx_twim_common.h b/drivers/i2c/i2c_nrfx_twim_common.h index ba7fa72f019..3c5c82311ba 100644 --- a/drivers/i2c/i2c_nrfx_twim_common.h +++ b/drivers/i2c/i2c_nrfx_twim_common.h @@ -43,6 +43,7 @@ struct i2c_nrfx_twim_common_config { }; int i2c_nrfx_twim_common_init(const struct device *dev); +int i2c_nrfx_twim_common_deinit(const struct device *dev); int i2c_nrfx_twim_configure(const struct device *dev, uint32_t i2c_config); int i2c_nrfx_twim_recover_bus(const struct device *dev); int i2c_nrfx_twim_msg_transfer(const struct device *dev, uint8_t flags, uint8_t *buf, diff --git a/drivers/i2c/i2c_nrfx_twim_rtio.c b/drivers/i2c/i2c_nrfx_twim_rtio.c index 6a7504dbd62..5eef549ba2e 100644 --- a/drivers/i2c/i2c_nrfx_twim_rtio.c +++ b/drivers/i2c/i2c_nrfx_twim_rtio.c @@ -203,7 +203,7 @@ static DEVICE_API(i2c, i2c_nrfx_twim_driver_api) = { .iodev_submit = i2c_nrfx_twim_rtio_submit, }; -int i2c_nrfx_twim_rtio_init(const struct device *dev) +static int i2c_nrfx_twim_rtio_init(const struct device *dev) { const struct i2c_nrfx_twim_rtio_config *config = dev->config; @@ -211,6 +211,11 @@ int i2c_nrfx_twim_rtio_init(const struct device *dev) return i2c_nrfx_twim_common_init(dev); } +static int i2c_nrfx_twim_rtio_deinit(const struct device *dev) +{ + return i2c_nrfx_twim_common_deinit(dev); +} + #define CONCAT_BUF_SIZE(idx) \ COND_CODE_1(DT_NODE_HAS_PROP(I2C(idx), zephyr_concat_buf_size), \ (DT_PROP(I2C(idx), zephyr_concat_buf_size)), (0)) @@ -282,9 +287,10 @@ int i2c_nrfx_twim_rtio_init(const struct device *dev) .ctx = &_i2c##idx##_twim_rtio, \ }; \ PM_DEVICE_DT_DEFINE(I2C(idx), twim_nrfx_pm_action, PM_DEVICE_ISR_SAFE); \ - I2C_DEVICE_DT_DEFINE(I2C(idx), i2c_nrfx_twim_rtio_init, PM_DEVICE_DT_GET(I2C(idx)), \ - &twim_##idx##z_data, &twim_##idx##z_config, POST_KERNEL, \ - CONFIG_I2C_INIT_PRIORITY, &i2c_nrfx_twim_driver_api); + I2C_DEVICE_DT_DEINIT_DEFINE(I2C(idx), i2c_nrfx_twim_rtio_init, i2c_nrfx_twim_rtio_deinit, \ + PM_DEVICE_DT_GET(I2C(idx)), &twim_##idx##z_data, \ + &twim_##idx##z_config, POST_KERNEL, CONFIG_I2C_INIT_PRIORITY, \ + &i2c_nrfx_twim_driver_api); #ifdef CONFIG_HAS_HW_NRF_TWIM0 I2C_NRFX_TWIM_RTIO_DEVICE(0); From ce923d4683a2a7dd2793a277c7dddb467cb0e99d Mon Sep 17 00:00:00 2001 From: Bjarki Arge Andreasen Date: Thu, 19 Jun 2025 15:48:31 +0200 Subject: [PATCH 09/11] [nrf fromtree] drivers: i2c: nrfx_twis: impl device deinit Implement device deinit hooks for nRF TWIS device driver. Signed-off-by: Bjarki Arge Andreasen (cherry picked from commit f22ffee5c3e48dddeb2f0631ffe84e0d6c988770) --- drivers/i2c/i2c_nrfx_twis.c | 43 +++++++++++++++++++++++++++++++++++-- 1 file changed, 41 insertions(+), 2 deletions(-) diff --git a/drivers/i2c/i2c_nrfx_twis.c b/drivers/i2c/i2c_nrfx_twis.c index c37c51ddd74..dca3a457cf2 100644 --- a/drivers/i2c/i2c_nrfx_twis.c +++ b/drivers/i2c/i2c_nrfx_twis.c @@ -73,6 +73,15 @@ static bool shim_nrf_twis_is_resumed(const struct device *dev) (void)pm_device_state_get(dev, &state); return state == PM_DEVICE_STATE_ACTIVE; } + +static bool shim_nrf_twis_is_suspended(const struct device *dev) +{ + enum pm_device_state state; + + (void)pm_device_state_get(dev, &state); + return state == PM_DEVICE_STATE_SUSPENDED || + state == PM_DEVICE_STATE_OFF; +} #else static bool shim_nrf_twis_is_resumed(const struct device *dev) { @@ -199,7 +208,7 @@ static int shim_nrf_twis_pm_action_cb(const struct device *dev, #if CONFIG_PM_DEVICE case PM_DEVICE_ACTION_SUSPEND: - shim_nrf_twis_disable(); + shim_nrf_twis_disable(dev); break; #endif @@ -283,6 +292,35 @@ static int shim_nrf_twis_init(const struct device *dev) return pm_device_driver_init(dev, shim_nrf_twis_pm_action_cb); } +static int shim_nrf_twis_deinit(const struct device *dev) +{ + const struct shim_nrf_twis_config *dev_config = dev->config; + struct shim_nrf_twis_data *dev_data = dev->data; + + if (dev_data->target_config != NULL) { + LOG_ERR("target registered"); + return -EPERM; + } + +#if CONFIG_PM_DEVICE + /* + * PM must have suspended the device before driver can + * be deinitialized + */ + if (!shim_nrf_twis_is_suspended(dev)) { + LOG_ERR("device active"); + return -EBUSY; + } +#else + /* Suspend device */ + shim_nrf_twis_disable(dev); +#endif + + /* Uninit device hardware */ + nrfx_twis_uninit(&dev_config->twis); + return 0; +} + #define SHIM_NRF_TWIS_NAME(id, name) \ _CONCAT_4(shim_nrf_twis_, name, _, id) @@ -323,9 +361,10 @@ static int shim_nrf_twis_init(const struct device *dev) shim_nrf_twis_pm_action_cb, \ ); \ \ - DEVICE_DT_DEFINE( \ + DEVICE_DT_DEINIT_DEFINE( \ SHIM_NRF_TWIS_NODE(id), \ shim_nrf_twis_init, \ + shim_nrf_twis_deinit, \ PM_DEVICE_DT_GET(SHIM_NRF_TWIS_NODE(id)), \ &SHIM_NRF_TWIS_NAME(id, data), \ &SHIM_NRF_TWIS_NAME(id, config), \ From 929fe3a0b8de087c6f657fc2486152afd5a98a22 Mon Sep 17 00:00:00 2001 From: Bjarki Arge Andreasen Date: Tue, 24 Jun 2025 16:09:46 +0200 Subject: [PATCH 10/11] [nrf fromtree] tests: drivers: i2c: target_api: add device_deinit test case Add device_deinit test case which, in case there are two devices using gpio loopbacks, deinits both controller and target devices, then uses gpio to configure and toggle the pins of the bus, then reinitializes both devices. Signed-off-by: Bjarki Arge Andreasen (cherry picked from commit db1f14e79a8bd427b7873f85ddb4b9981b3d889e) --- tests/drivers/i2c/i2c_target_api/src/main.c | 57 +++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/tests/drivers/i2c/i2c_target_api/src/main.c b/tests/drivers/i2c/i2c_target_api/src/main.c index 5c1540e3e79..b249daae90b 100644 --- a/tests/drivers/i2c/i2c_target_api/src/main.c +++ b/tests/drivers/i2c/i2c_target_api/src/main.c @@ -16,6 +16,7 @@ #include #include +#include #include @@ -166,6 +167,62 @@ static int run_program_read(const struct device *i2c, uint8_t addr, return 0; } +ZTEST(i2c_eeprom_target, test_deinit) +{ + const struct device *const i2c_0 = DEVICE_DT_GET(DT_BUS(NODE_EP0)); + const struct device *const i2c_1 = DEVICE_DT_GET(DT_BUS(NODE_EP1)); + const struct gpio_dt_spec sda_pin_0 = + GPIO_DT_SPEC_GET_OR(DT_PATH(zephyr_user), sda0_gpios, {}); + const struct gpio_dt_spec scl_pin_0 = + GPIO_DT_SPEC_GET_OR(DT_PATH(zephyr_user), scl0_gpios, {}); + const struct gpio_dt_spec sda_pin_1 = + GPIO_DT_SPEC_GET_OR(DT_PATH(zephyr_user), sda1_gpios, {}); + const struct gpio_dt_spec scl_pin_1 = + GPIO_DT_SPEC_GET_OR(DT_PATH(zephyr_user), scl1_gpios, {}); + int ret; + + if (i2c_0 == i2c_1) { + TC_PRINT(" gpio loopback required for test\n"); + ztest_test_skip(); + } + + if (scl_pin_0.port == NULL || sda_pin_0.port == NULL || + scl_pin_1.port == NULL || sda_pin_1.port == NULL) { + TC_PRINT(" bus gpios not specified in zephyr,path\n"); + ztest_test_skip(); + } + + ret = device_deinit(i2c_0); + if (ret == -ENOTSUP) { + TC_PRINT(" device deinit not supported\n"); + ztest_test_skip(); + } + + zassert_ok(ret); + + ret = device_deinit(i2c_1); + if (ret == -ENOTSUP) { + TC_PRINT(" device deinit not supported\n"); + zassert_ok(device_init(i2c_0)); + ztest_test_skip(); + } + + zassert_ok(gpio_pin_configure_dt(&sda_pin_0, GPIO_INPUT)); + zassert_ok(gpio_pin_configure_dt(&sda_pin_1, GPIO_OUTPUT_INACTIVE)); + zassert_ok(gpio_pin_configure_dt(&scl_pin_0, GPIO_INPUT)); + zassert_ok(gpio_pin_configure_dt(&scl_pin_1, GPIO_OUTPUT_INACTIVE)); + zassert_equal(gpio_pin_get_dt(&sda_pin_0), 0); + zassert_equal(gpio_pin_get_dt(&scl_pin_0), 0); + zassert_ok(gpio_pin_set_dt(&sda_pin_1, 1)); + zassert_ok(gpio_pin_set_dt(&scl_pin_1, 1)); + zassert_equal(gpio_pin_get_dt(&sda_pin_0), 1); + zassert_equal(gpio_pin_get_dt(&scl_pin_0), 1); + zassert_ok(gpio_pin_configure_dt(&sda_pin_1, GPIO_INPUT)); + zassert_ok(gpio_pin_configure_dt(&scl_pin_1, GPIO_INPUT)); + zassert_ok(device_init(i2c_0)); + zassert_ok(device_init(i2c_1)); +} + ZTEST(i2c_eeprom_target, test_eeprom_target) { const struct device *const eeprom_0 = DEVICE_DT_GET(NODE_EP0); From c6effa3f667e4859b031943d5101c4518efd8d95 Mon Sep 17 00:00:00 2001 From: Bjarki Arge Andreasen Date: Tue, 24 Jun 2025 16:11:29 +0200 Subject: [PATCH 11/11] [nrf fromtree] tests: drivers: i2c: target_api: extend nrf overlays with bus gpios Add bus gpios to zephyr,user node in nrf overlays to enable testing device_deinit test case. Signed-off-by: Bjarki Arge Andreasen (cherry picked from commit e0fa444b8dbff03a05a7942825dca8c5bd0109a9) --- .../boards/nrf54h20dk_nrf54h20_cpuapp.overlay | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/tests/drivers/i2c/i2c_target_api/boards/nrf54h20dk_nrf54h20_cpuapp.overlay b/tests/drivers/i2c/i2c_target_api/boards/nrf54h20dk_nrf54h20_cpuapp.overlay index 2fc033d534a..9390af2b5cd 100644 --- a/tests/drivers/i2c/i2c_target_api/boards/nrf54h20dk_nrf54h20_cpuapp.overlay +++ b/tests/drivers/i2c/i2c_target_api/boards/nrf54h20dk_nrf54h20_cpuapp.overlay @@ -9,6 +9,23 @@ * SCL = P1.2 and P1.3 */ +/ { + zephyr,user { + sda0-gpios = <&gpio2 8 0>; + scl0-gpios = <&gpio1 2 0>; + sda1-gpios = <&gpio2 9 0>; + scl1-gpios = <&gpio1 3 0>; + }; +}; + +&gpio1 { + status = "okay"; +}; + +&gpio2 { + status = "okay"; +}; + &pinctrl { i2c130_default: i2c130_default { group1 {