Skip to content

Samples: Introduce device_deinit() sample #89927

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions drivers/mfd/mfd_nxp_lp_flexcomm.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
8 changes: 7 additions & 1 deletion drivers/spi/spi_nrfx_spi.c
Original file line number Diff line number Diff line change
Expand Up @@ -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:
*
Expand Down Expand Up @@ -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, \
Expand Down
9 changes: 8 additions & 1 deletion drivers/spi/spi_nrfx_spim.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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, \
Expand Down
11 changes: 8 additions & 3 deletions drivers/spi/spi_nxp_lpspi/spi_nxp_lpspi.c
Original file line number Diff line number Diff line change
Expand Up @@ -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) \
Expand All @@ -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)))

Expand Down
20 changes: 20 additions & 0 deletions drivers/spi/spi_nxp_lpspi/spi_nxp_lpspi_common.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
30 changes: 29 additions & 1 deletion drivers/spi/spi_nxp_lpspi/spi_nxp_lpspi_priv.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);

Expand All @@ -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), \
Expand All @@ -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) \
Expand Down
2 changes: 2 additions & 0 deletions include/zephyr/drivers/mfd/nxp_lp_flexcomm.h
Original file line number Diff line number Diff line change
Expand Up @@ -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_ */
56 changes: 46 additions & 10 deletions include/zephyr/drivers/spi.h
Original file line number Diff line number Diff line change
Expand Up @@ -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__)
Expand Down Expand Up @@ -700,15 +700,17 @@ 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
* 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 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
Expand All @@ -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_)
Expand All @@ -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`
Expand Down
7 changes: 7 additions & 0 deletions samples/application_development/deinit/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -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)
93 changes: 93 additions & 0 deletions samples/application_development/deinit/README.rst
Original file line number Diff line number Diff line change
@@ -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>;
};
};
Loading