diff --git a/drivers/mfd/mfd_nxp_lp_flexcomm.c b/drivers/mfd/mfd_nxp_lp_flexcomm.c index ec6a325d3d0e..76d547d859a5 100644 --- a/drivers/mfd/mfd_nxp_lp_flexcomm.c +++ b/drivers/mfd/mfd_nxp_lp_flexcomm.c @@ -81,6 +81,18 @@ void nxp_lp_flexcomm_setirqhandler(const struct device *dev, const struct device child->dev = child_dev; } +void nxp_lp_flexcomm_clearirqhandler(const struct device *dev, LP_FLEXCOMM_PERIPH_T periph) +{ + struct nxp_lp_flexcomm_data *data = dev->data; + struct nxp_lp_flexcomm_child *child; + + child = &data->children[periph]; + + /* Clear the interrupt handler and the child device node */ + child->lp_flexcomm_child_isr = NULL; + child->dev = NULL; +} + static int nxp_lp_flexcomm_init(const struct device *dev) { const struct nxp_lp_flexcomm_config *config = dev->config; diff --git a/drivers/spi/spi_nrfx_spi.c b/drivers/spi/spi_nrfx_spi.c index e0aeb6680295..e0d2c5dfdaf7 100644 --- a/drivers/spi/spi_nrfx_spi.c +++ b/drivers/spi/spi_nrfx_spi.c @@ -414,6 +414,11 @@ static int spi_nrfx_init(const struct device *dev) return 0; } +static int spi_nrfx_deinit(const struct device *dev) +{ + return 0; +} + /* * Current factors requiring use of DT_NODELABEL: * @@ -460,8 +465,9 @@ static int spi_nrfx_init(const struct device *dev) !(DT_GPIO_FLAGS(SPI(idx), wake_gpios) & GPIO_ACTIVE_LOW), \ "WAKE line must be configured as active high"); \ PM_DEVICE_DT_DEFINE(SPI(idx), spi_nrfx_pm_action); \ - SPI_DEVICE_DT_DEFINE(SPI(idx), \ + SPI_DEVICE_DT_DEINIT_DEFINE(SPI(idx), \ spi_nrfx_init, \ + spi_nrfx_deinit, \ PM_DEVICE_DT_GET(SPI(idx)), \ &spi_##idx##_data, \ &spi_##idx##z_config, \ diff --git a/drivers/spi/spi_nrfx_spim.c b/drivers/spi/spi_nrfx_spim.c index a3e734f28078..6ad0a982b44b 100644 --- a/drivers/spi/spi_nrfx_spim.c +++ b/drivers/spi/spi_nrfx_spim.c @@ -759,6 +759,12 @@ 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) +{ + return 0; +} + /* * We use NODELABEL here because the nrfx API requires us to call * functions which are named according to SoC peripheral instance @@ -862,8 +868,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, \ diff --git a/drivers/spi/spi_nxp_lpspi/spi_nxp_lpspi.c b/drivers/spi/spi_nxp_lpspi/spi_nxp_lpspi.c index 05a7c6cf3828..48a642e5e38b 100644 --- a/drivers/spi/spi_nxp_lpspi/spi_nxp_lpspi.c +++ b/drivers/spi/spi_nxp_lpspi/spi_nxp_lpspi.c @@ -397,6 +397,11 @@ static int lpspi_init(const struct device *dev) return 0; } +static int lpspi_deinit(const struct device *dev) +{ + return spi_nxp_deinit_common(dev); +} + #define LPSPI_INIT(n) \ SPI_NXP_LPSPI_COMMON_INIT(n) \ SPI_LPSPI_CONFIG_INIT(n) \ @@ -408,9 +413,9 @@ static int lpspi_init(const struct device *dev) .driver_data = &lpspi_##n##_driver_data, \ }; \ \ - SPI_DEVICE_DT_INST_DEFINE(n, lpspi_init, NULL, &lpspi_data_##n, \ - &lpspi_config_##n, POST_KERNEL, CONFIG_SPI_INIT_PRIORITY, \ - &lpspi_driver_api); + SPI_DEVICE_DT_INST_DEINIT_DEFINE(n, lpspi_init, lpspi_deinit, NULL, &lpspi_data_##n, \ + &lpspi_config_##n, POST_KERNEL, CONFIG_SPI_INIT_PRIORITY, \ + &lpspi_driver_api); #define SPI_LPSPI_INIT_IF_DMA(n) IF_DISABLED(SPI_NXP_LPSPI_HAS_DMAS(n), (LPSPI_INIT(n))) diff --git a/drivers/spi/spi_nxp_lpspi/spi_nxp_lpspi_common.c b/drivers/spi/spi_nxp_lpspi/spi_nxp_lpspi_common.c index 5cd9d1cde5f0..aae6df98a1d7 100644 --- a/drivers/spi/spi_nxp_lpspi/spi_nxp_lpspi_common.c +++ b/drivers/spi/spi_nxp_lpspi/spi_nxp_lpspi_common.c @@ -229,3 +229,23 @@ int spi_nxp_init_common(const struct device *dev) return err; } + +int spi_nxp_deinit_common(const struct device *dev) +{ + LPSPI_Type *base = (LPSPI_Type *)DEVICE_MMIO_NAMED_GET(dev, reg_base); + const struct lpspi_config *config = dev->config; + + LPSPI_Reset(base); + +#ifdef LPSPI_RSTS + RESET_SetPeripheralReset(lpspi_get_reset(base)); +#endif + +#ifdef LPSPI_CLOCKS + CLOCK_DisableClock(lpspi_get_clock(base)); +#endif + + config->irq_deinit_func(dev); + + return 0; +} diff --git a/drivers/spi/spi_nxp_lpspi/spi_nxp_lpspi_priv.h b/drivers/spi/spi_nxp_lpspi/spi_nxp_lpspi_priv.h index 2054000de2e6..29a1be065fe6 100644 --- a/drivers/spi/spi_nxp_lpspi/spi_nxp_lpspi_priv.h +++ b/drivers/spi/spi_nxp_lpspi/spi_nxp_lpspi_priv.h @@ -34,6 +34,7 @@ struct lpspi_config { const struct device *clock_dev; clock_control_subsys_t clock_subsys; void (*irq_config_func)(const struct device *dev); + void (*irq_deinit_func)(const struct device *dev); uint32_t pcs_sck_delay; uint32_t sck_pcs_delay; uint32_t transfer_delay; @@ -65,6 +66,13 @@ int spi_mcux_configure(const struct device *dev, const struct spi_config *spi_cf */ int spi_nxp_init_common(const struct device *dev); +/* Does these things: + * Reset module + * Disable clock if present + * Clear or simply disable IRQ + */ +int spi_nxp_deinit_common(const struct device *dev); + /* common api function for now */ int spi_lpspi_release(const struct device *dev, const struct spi_config *spi_cfg); @@ -87,12 +95,27 @@ void lpspi_wait_tx_fifo_empty(const struct device *dev); #define LPSPI_IRQN(n) COND_CODE_1(DT_NODE_HAS_COMPAT(DT_INST_PARENT(n), nxp_lp_flexcomm), \ (DT_IRQN(DT_INST_PARENT(n))), (DT_INST_IRQN(n))) +#define SPI_LPSPI_IRQ_DEINIT_FUNC_LP_FLEXCOMM(n) \ + nxp_lp_flexcomm_clearirqhandler(DEVICE_DT_GET(DT_INST_PARENT(n)), \ + LP_FLEXCOMM_PERIPH_LPSPI); + +#define SPI_LPSPI_IRQ_DEINIT_FUNC_DISTINCT(n) \ + irq_disable(DT_INST_IRQN(n)); + +#define SPI_LPSPI_IRQ_DEINIT_FUNC(n) \ + COND_CODE_1( \ + DT_NODE_HAS_COMPAT(DT_INST_PARENT(n), nxp_lp_flexcomm), \ + (SPI_LPSPI_IRQ_DEINIT_FUNC_LP_FLEXCOMM(n)), \ + (SPI_LPSPI_IRQ_DEINIT_FUNC_DISTINCT(n)) \ + ) + #define SPI_LPSPI_CONFIG_INIT(n) \ static const struct lpspi_config lpspi_config_##n = { \ DEVICE_MMIO_NAMED_ROM_INIT(reg_base, DT_DRV_INST(n)), \ .clock_dev = DEVICE_DT_GET(DT_INST_CLOCKS_CTLR(n)), \ .clock_subsys = (clock_control_subsys_t)DT_INST_CLOCKS_CELL(n, name), \ .irq_config_func = lpspi_config_func_##n, \ + .irq_deinit_func = lpspi_deinit_func_##n, \ .pcs_sck_delay = UTIL_AND(DT_INST_NODE_HAS_PROP(n, pcs_sck_delay), \ DT_INST_PROP(n, pcs_sck_delay)), \ .sck_pcs_delay = UTIL_AND(DT_INST_NODE_HAS_PROP(n, sck_pcs_delay), \ @@ -112,7 +135,12 @@ void lpspi_wait_tx_fifo_empty(const struct device *dev); \ static void lpspi_config_func_##n(const struct device *dev) \ { \ - SPI_LPSPI_IRQ_FUNC(n) \ + SPI_LPSPI_IRQ_FUNC(n) \ + } \ + \ + static void lpspi_deinit_func_##n(const struct device *dev) \ + { \ + SPI_LPSPI_IRQ_DEINIT_FUNC(n) \ } #define SPI_NXP_LPSPI_COMMON_DATA_INIT(n) \ diff --git a/include/zephyr/drivers/mfd/nxp_lp_flexcomm.h b/include/zephyr/drivers/mfd/nxp_lp_flexcomm.h index 0faed3c9ddfa..90afc7edb98f 100644 --- a/include/zephyr/drivers/mfd/nxp_lp_flexcomm.h +++ b/include/zephyr/drivers/mfd/nxp_lp_flexcomm.h @@ -13,4 +13,6 @@ typedef void (*child_isr_t)(const struct device *dev); void nxp_lp_flexcomm_setirqhandler(const struct device *dev, const struct device *child_dev, LP_FLEXCOMM_PERIPH_T periph, child_isr_t handler); +void nxp_lp_flexcomm_clearirqhandler(const struct device *dev, LP_FLEXCOMM_PERIPH_T periph); + #endif /* ZEPHYR_DRIVERS_NXP_LP_FLEXCOMM_H_ */ diff --git a/include/zephyr/drivers/spi.h b/include/zephyr/drivers/spi.h index 8e8cdb5a9941..08a3f03137bc 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` diff --git a/samples/application_development/deinit/CMakeLists.txt b/samples/application_development/deinit/CMakeLists.txt new file mode 100644 index 000000000000..a14b9bc3fa01 --- /dev/null +++ b/samples/application_development/deinit/CMakeLists.txt @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.20.0) +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(deinit) + +target_sources(app PRIVATE src/main.c) diff --git a/samples/application_development/deinit/README.rst b/samples/application_development/deinit/README.rst new file mode 100644 index 000000000000..bb75e9a7b43d --- /dev/null +++ b/samples/application_development/deinit/README.rst @@ -0,0 +1,93 @@ +.. zephyr:code-sample:: deinit + :name: Device deinit + :relevant-api: gpio_interface spi_interface device_model + +Overview +******** + +This sample demonstrates how to initialize and deinitialize device +drivers at runtime, which allows switching between different devices +and device drivers which share pins or share hardware. + +Detailed description +******************** + +This sample demonstrates how to share a SPI CS (chip-select) pin +between a SPI peripheral and a GPIO port. The SPI CS pin chosen is +connected to an LED on the board used for the sample. This sample +blinks the LED, by either manually toggling the pin using the GPIO +port, or performing a mock SPI transaction with ``SPI_HOLD_ON_CS`` +set which keeps the SPI CS pin asserted from the start of the SPI +transaction until we call :c:func:`spi_release`. + +We use ``zephyr,deferred-init`` to prevent initializing the SPI +device driver during boot. The sample does the following after +booting: + +1. Configure the CS pin as an output in active state with + :c:func:`gpio_pin_configure_dt` +2. Wait +3. Configure the CS pin as an input with + :c:func:`gpio_pin_configure_dt` + +This is the first blink + +4. Initialize the SPI device driver with :c:func:`device_init` +5. Perform mock SPI transaction with :c:func:`spi_transceive` +6. Wait +7. Release SPI with :c:func:`spi_release` + +This is the second blink + +8. Deinitialize SPI device driver with :c:func:`device_deinit` +9. Configure the CS pin as an output in active state with + :c:func:`gpio_pin_configure_dt` +10. Wait +11. Configure the CS pin as an input + :c:func:`gpio_pin_configure_dt` + +This is the third blink + +12. Initialize the SPI device driver with :c:func:`device_init` +13. Perform mock SPI transaction with :c:func:`spi_transceive` +14. Wait +15. Release SPI with :c:func:`spi_release` + +This is the last blink. + +16. print ``sample complete`` to console + +If any errors occur during the sample, the error is printed to +the console and the sample is stopped. + +Porting guide +************* + +Use the following devicetree overlay example to create an +overlay for your board: + +.. code-block:: devicetree + + &port0 { + status = "okay"; + }; + + &spi0 { + status = "okay"; + zephyr,deferred-init; + cs-gpios = <&port0 0 GPIO_ACTIVE_LOW>; + }; + + / { + zephyr,user { + spi = <&spi0>; + + /* + * Must match &spi0 cs-gpios and should + * preferably be a pin connected to an LED + * on the board, which illuminates when + * active. + */ + cs-gpios = <&port0 0 GPIO_ACTIVE_LOW>; + }; + }; diff --git a/samples/application_development/deinit/boards/frdm_ke15z.overlay b/samples/application_development/deinit/boards/frdm_ke15z.overlay new file mode 100644 index 000000000000..2a917b23005d --- /dev/null +++ b/samples/application_development/deinit/boards/frdm_ke15z.overlay @@ -0,0 +1,37 @@ +/* + * Copyright 2025 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +&pinctrl { + lpuart0_default: lpuart0_default { + group0 { + pinmux = , + , + ; + bias-pull-up; + drive-strength = "low"; + slew-rate = "slow"; + }; + }; +}; + +&lpspi0 { + pinctrl-0 = <&lpuart0_default>; + pinctrl-names = "default"; + status = "okay"; + zephyr,deferred-init; + cs-gpios = <&gpiod 0 GPIO_ACTIVE_LOW>; +}; + +&gpiod { + status = "okay"; +}; + +/ { + zephyr,user { + spi = <&lpspi0>; + cs-gpios = <&gpiod 0 GPIO_ACTIVE_LOW>; + }; +}; diff --git a/samples/application_development/deinit/boards/nrf5340dk_nrf5340_cpuapp.overlay b/samples/application_development/deinit/boards/nrf5340dk_nrf5340_cpuapp.overlay new file mode 100644 index 000000000000..635c7e7de1ca --- /dev/null +++ b/samples/application_development/deinit/boards/nrf5340dk_nrf5340_cpuapp.overlay @@ -0,0 +1,18 @@ +/* + * Copyright 2025 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +&spi4 { + status = "okay"; + zephyr,deferred-init; + cs-gpios = <&gpio0 30 GPIO_ACTIVE_LOW>; +}; + +/ { + zephyr,user { + spi = <&spi4>; + cs-gpios = <&gpio0 30 GPIO_ACTIVE_LOW>; + }; +}; diff --git a/samples/application_development/deinit/prj.conf b/samples/application_development/deinit/prj.conf new file mode 100644 index 000000000000..710a28e868bb --- /dev/null +++ b/samples/application_development/deinit/prj.conf @@ -0,0 +1,2 @@ +CONFIG_SPI=y +CONFIG_GPIO=y diff --git a/samples/application_development/deinit/sample.yaml b/samples/application_development/deinit/sample.yaml new file mode 100644 index 000000000000..a3cb110840d8 --- /dev/null +++ b/samples/application_development/deinit/sample.yaml @@ -0,0 +1,11 @@ +sample: + name: Device deinit +tests: + sample.application_development.deinit: + platform_allow: + - nrf5340dk/nrf5340/cpuapp + harness: console + harness_config: + type: one_line + regex: + - "sample complete" diff --git a/samples/application_development/deinit/src/main.c b/samples/application_development/deinit/src/main.c new file mode 100644 index 000000000000..598a79edecce --- /dev/null +++ b/samples/application_development/deinit/src/main.c @@ -0,0 +1,158 @@ +/* + * Copyright 2025 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include + +#define SPI_DEV_NODE DT_PROP(DT_PATH(zephyr_user), spi) + +static const struct device *const spi_dev = DEVICE_DT_GET(SPI_DEV_NODE); +static const struct gpio_dt_spec cs_pin = GPIO_DT_SPEC_GET(DT_PATH(zephyr_user), cs_gpios); + +static uint8_t tx_buf[8]; + +static const struct spi_buf tx_buf_pool[1] = { + { + .buf = tx_buf, + .len = sizeof(tx_buf), + }, +}; + +static const struct spi_buf_set spi_tx_buf = { + .buffers = tx_buf_pool, + .count = ARRAY_SIZE(tx_buf_pool), +}; + +static const struct spi_config config = { + .frequency = DT_PROP_OR(SPI_DEV_NODE, max_frequency, 1000000), + .operation = SPI_OP_MODE_MASTER | SPI_HOLD_ON_CS | SPI_WORD_SET(8), + .slave = 0, + .cs = { + .gpio = GPIO_DT_SPEC_GET_OR(SPI_DEV_NODE, cs_gpios, {0}), + .delay = 0, + }, +}; + +static int control_cs_pin_with_gpio(void) +{ + int ret; + + ret = gpio_pin_configure_dt(&cs_pin, GPIO_OUTPUT_ACTIVE); + if (ret) { + printk("failed to configure CS pin\n"); + return 0; + } + + k_msleep(1000); + + ret = gpio_pin_configure_dt(&cs_pin, GPIO_INPUT); + if (ret) { + printk("failed to configure CS pin\n"); + return 0; + } + + k_msleep(1000); + return 0; +} + +static int control_cs_pin_with_spi(void) +{ + int ret; + + /* + * Perform SPI transaction and keep CS pin asserted after + * after transaction (see config.operation). + */ + ret = spi_transceive(spi_dev, &config, &spi_tx_buf, NULL); + if (ret) { + printk("failed to perform SPI transaction (ret %d)\n", ret); + return 0; + } + + k_msleep(1000); + + /* Release SPI device and thus CS pin shall be deasserted */ + ret = spi_release(spi_dev, &config); + if (ret) { + printk("failed to release SPI device (ret %d)\n", ret); + return 0; + } + + k_msleep(1000); + return 0; +} + +int main(void) +{ + int ret; + + /* + * As we have specified deferred init for the SPI device in the devicetree, the driver + * will not be initialized at this stage. We should have full control of the CS GPIO, + * let's test that. + */ + ret = control_cs_pin_with_gpio(); + if (ret) { + printk("failed to control CS pin with GPIO device driver (ret %d)\n", ret); + return 0; + } + + /* + * Now lets initialize the SPI device driver. + */ + ret = device_init(spi_dev); + if (ret) { + printk("failed to init SPI device driver (ret %d)\n", ret); + return 0; + } + + /* + * To control the CS pin in "human speed", lets perform a mock + * transaction, holding the CS pin until we release the SPI device. + */ + ret = control_cs_pin_with_spi(); + if (ret) { + printk("failed to control CS pin with SPI device driver (ret %d)\n", ret); + return 0; + } + + /* + * Lets deinit the SPI device driver an manually control the + * CS pin again. + */ + ret = device_deinit(spi_dev); + if (ret) { + printk("failed to deinit SPI device driver (ret %d)\n", ret); + return 0; + } + + ret = control_cs_pin_with_gpio(); + if (ret) { + printk("failed to control CS pin with GPIO device driver (ret %d)\n", ret); + return 0; + } + + /* + * Lastly, lets check if we can bring back the SPI device driver. + */ + ret = device_init(spi_dev); + if (ret) { + printk("failed to init SPI device driver (ret %d)\n", ret); + return 0; + } + + /* One final blink */ + ret = control_cs_pin_with_spi(); + if (ret) { + printk("failed to control CS pin with SPI device driver (ret %d)\n", ret); + return 0; + } + + printk("sample complete\n"); + return 0; +}