From 877b10bef17c027a7c3b5593bab78084de1509c5 Mon Sep 17 00:00:00 2001 From: Mikhail Siomin Date: Mon, 11 Mar 2024 23:43:30 +0300 Subject: [PATCH] drivers: mcux: flexio: Added generic MCUX FlexIO driver Added FlexIO driver that distributes hardware resources between interfaces using them. Signed-off-by: Mikhail Siomin --- drivers/misc/CMakeLists.txt | 1 + drivers/misc/Kconfig | 1 + drivers/misc/mcux_flexio/CMakeLists.txt | 6 + drivers/misc/mcux_flexio/Kconfig | 26 ++ drivers/misc/mcux_flexio/mcux_flexio.c | 260 ++++++++++++++++++ .../drivers/misc/nxp_flexio/nxp_flexio.h | 88 ++++++ west.yml | 2 +- 7 files changed, 383 insertions(+), 1 deletion(-) create mode 100644 drivers/misc/mcux_flexio/CMakeLists.txt create mode 100644 drivers/misc/mcux_flexio/Kconfig create mode 100644 drivers/misc/mcux_flexio/mcux_flexio.c create mode 100644 include/zephyr/drivers/misc/nxp_flexio/nxp_flexio.h diff --git a/drivers/misc/CMakeLists.txt b/drivers/misc/CMakeLists.txt index c23bdb185de..1c0da74d998 100644 --- a/drivers/misc/CMakeLists.txt +++ b/drivers/misc/CMakeLists.txt @@ -8,3 +8,4 @@ add_subdirectory_ifdef(CONFIG_NXP_S32_EMIOS nxp_s32_emios) add_subdirectory_ifdef(CONFIG_TIMEAWARE_GPIO timeaware_gpio) add_subdirectory_ifdef(CONFIG_DEVMUX devmux) add_subdirectory_ifdef(CONFIG_NORDIC_VPR_LAUNCHER nordic_vpr_launcher) +add_subdirectory_ifdef(CONFIG_MCUX_FLEXIO mcux_flexio) diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 3511b8b6fd4..a87218c120a 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -12,5 +12,6 @@ source "drivers/misc/nxp_s32_emios/Kconfig" source "drivers/misc/timeaware_gpio/Kconfig" source "drivers/misc/devmux/Kconfig" source "drivers/misc/nordic_vpr_launcher/Kconfig" +source "drivers/misc/mcux_flexio/Kconfig" endmenu diff --git a/drivers/misc/mcux_flexio/CMakeLists.txt b/drivers/misc/mcux_flexio/CMakeLists.txt new file mode 100644 index 00000000000..fac2c9078f0 --- /dev/null +++ b/drivers/misc/mcux_flexio/CMakeLists.txt @@ -0,0 +1,6 @@ +# Copyright (c) 2024, STRIM, ALC +# SPDX-License-Identifier: Apache-2.0 + +zephyr_library() + +zephyr_library_sources(mcux_flexio.c) diff --git a/drivers/misc/mcux_flexio/Kconfig b/drivers/misc/mcux_flexio/Kconfig new file mode 100644 index 00000000000..b5d5a26d1f3 --- /dev/null +++ b/drivers/misc/mcux_flexio/Kconfig @@ -0,0 +1,26 @@ +# Copyright (c) 2024, STRIM, ALC +# SPDX-License-Identifier: Apache-2.0 + +config MCUX_FLEXIO + bool + depends on DT_HAS_NXP_FLEXIO_ENABLED + depends on CLOCK_CONTROL + help + Enable the FlexIO controller driver. + This driver is not user-selectable, + and should be enabled by other FlexIO drivers so + that they can use it to share the resources of the FlexIO device. + +if MCUX_FLEXIO + +config MCUX_FLEXIO_INIT_PRIORITY + int "FlexIO controller driver init priority" + default KERNEL_INIT_PRIORITY_DEVICE + help + MCUX FlexIO device driver initialization priority. + +module = MCUX_FLEXIO +module-str = mcux_flexio +source "subsys/logging/Kconfig.template.log_config" + +endif # MCUX_FLEXIO diff --git a/drivers/misc/mcux_flexio/mcux_flexio.c b/drivers/misc/mcux_flexio/mcux_flexio.c new file mode 100644 index 00000000000..46c39d0910d --- /dev/null +++ b/drivers/misc/mcux_flexio/mcux_flexio.c @@ -0,0 +1,260 @@ +/* + * Copyright (c) 2024, STRIM, ALC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT nxp_flexio + +#include +#include +#include +#include +#include +#include + +LOG_MODULE_REGISTER(mcux_flexio, CONFIG_MCUX_FLEXIO_LOG_LEVEL); + + +struct mcux_flexio_config { + FLEXIO_Type *base; + const struct device *clock_dev; + clock_control_subsys_t clock_subsys; + void (*irq_config_func)(const struct device *dev); + void (*irq_enable_func)(void); + void (*irq_disable_func)(void); +}; + +typedef const struct nxp_flexio_child *nxp_flexio_map_child_t; + +struct mcux_flexio_data { + struct k_mutex lock; + uint32_t shifter_indexes_used; + uint32_t timer_indexes_used; + nxp_flexio_map_child_t *map_shifter_child; + nxp_flexio_map_child_t *map_timer_child; + uint32_t map_shifter_child_count; + uint32_t map_timer_child_count; +}; + + +static int mcux_flexio_child_take_shifter_idx(const struct device *dev) +{ + struct mcux_flexio_data *data = dev->data; + + for (uint32_t i = 0; i < data->map_shifter_child_count; i++) { + if ((data->shifter_indexes_used & BIT(i)) == 0) { + WRITE_BIT(data->shifter_indexes_used, i, 1); + return i; + } + } + + return -ENOBUFS; +} + +static int mcux_flexio_child_take_timer_idx(const struct device *dev) +{ + struct mcux_flexio_data *data = dev->data; + + for (uint32_t i = 0; i < data->map_timer_child_count; i++) { + if ((data->timer_indexes_used & BIT(i)) == 0) { + WRITE_BIT(data->timer_indexes_used, i, 1); + return i; + } + } + + return -ENOBUFS; +} + +static void mcux_flexio_isr(const struct device *dev) +{ + const struct mcux_flexio_config *config = dev->config; + struct mcux_flexio_data *data = dev->data; + FLEXIO_Type *base = config->base; + + nxp_flexio_map_child_t *map_shifter_child = data->map_shifter_child; + uint32_t map_shifter_child_count = data->map_shifter_child_count; + uint32_t shifter_status_flag = FLEXIO_GetShifterStatusFlags(base); + uint32_t shifter_error_flag = FLEXIO_GetShifterErrorFlags(base); + + if (shifter_status_flag || shifter_error_flag) { + for (uint32_t idx = 0; idx < map_shifter_child_count; idx++) { + if (((shifter_status_flag | shifter_error_flag) & BIT(idx)) != 0) { + const struct nxp_flexio_child *child = map_shifter_child[idx]; + + if (child != NULL) { + nxp_flexio_child_isr_t isr = child->isr; + + if (isr != NULL) { + isr(child->user_data); + } + } + } + } + } + + nxp_flexio_map_child_t *map_timer_child = data->map_timer_child; + uint32_t map_timer_child_count = data->map_timer_child_count; + uint32_t timer_status_flag = FLEXIO_GetTimerStatusFlags(base); + + if (timer_status_flag) { + for (uint32_t idx = 0; idx < map_timer_child_count; idx++) { + if ((timer_status_flag & BIT(idx)) != 0) { + const struct nxp_flexio_child *child = map_timer_child[idx]; + + if (child != NULL) { + nxp_flexio_child_isr_t isr = child->isr; + + if (isr != NULL) { + isr(child->user_data); + } + } + } + } + } + + SDK_ISR_EXIT_BARRIER; +} + +static int mcux_flexio_init(const struct device *dev) +{ + const struct mcux_flexio_config *config = dev->config; + struct mcux_flexio_data *data = dev->data; + flexio_config_t flexio_config; + + k_mutex_init(&data->lock); + + FLEXIO_GetDefaultConfig(&flexio_config); + FLEXIO_Init(config->base, &flexio_config); + config->irq_config_func(dev); + + return 0; +} + +void nxp_flexio_irq_enable(const struct device *dev) +{ + const struct mcux_flexio_config *config = dev->config; + + config->irq_enable_func(); +} + +void nxp_flexio_irq_disable(const struct device *dev) +{ + const struct mcux_flexio_config *config = dev->config; + + config->irq_disable_func(); +} + +void nxp_flexio_lock(const struct device *dev) +{ + struct mcux_flexio_data *data = dev->data; + + k_mutex_lock(&data->lock, K_FOREVER); +} + +void nxp_flexio_unlock(const struct device *dev) +{ + struct mcux_flexio_data *data = dev->data; + + k_mutex_unlock(&data->lock); +} + +int nxp_flexio_get_rate(const struct device *dev, uint32_t *rate) +{ + const struct mcux_flexio_config *config = dev->config; + + return clock_control_get_rate(config->clock_dev, config->clock_subsys, rate); +} + +int nxp_flexio_child_attach(const struct device *dev, + const struct nxp_flexio_child *child) +{ + struct mcux_flexio_data *data = dev->data; + const struct nxp_flexio_child_res *child_res = &child->res; + + for (uint32_t i = 0; i < child_res->shifter_count; i++) { + int shifter_idx = mcux_flexio_child_take_shifter_idx(dev); + + if (shifter_idx < 0) { + LOG_ERR("Failed to take shifter index: %d", shifter_idx); + return shifter_idx; + } + child_res->shifter_index[i] = shifter_idx; + data->map_shifter_child[shifter_idx] = child; + LOG_DBG("child %p: shifter_idx[%d] is %d", child, i, shifter_idx); + } + + for (uint32_t i = 0; i < child_res->timer_count; i++) { + int timer_idx = mcux_flexio_child_take_timer_idx(dev); + + if (timer_idx < 0) { + LOG_ERR("Failed to take timer index: %d", timer_idx); + return timer_idx; + } + child_res->timer_index[i] = timer_idx; + data->map_timer_child[timer_idx] = child; + LOG_DBG("child %p: timer_idx[%d] is %d", child, i, timer_idx); + } + + return 0; +} + +#define MCUX_FLEXIO_SHIFTER_COUNT_MAX(n) \ + ARRAY_SIZE(((FLEXIO_Type *)DT_INST_REG_ADDR(n))->SHIFTCTL) + +#define MCUX_FLEXIO_TIMER_COUNT_MAX(n) \ + ARRAY_SIZE(((FLEXIO_Type *)DT_INST_REG_ADDR(n))->TIMCTL) + +#define MCUX_FLEXIO_INIT(n) \ + static void mcux_flexio_irq_config_func_##n(const struct device *dev); \ + static void mcux_flexio_irq_enable_func_##n(void); \ + static void mcux_flexio_irq_disable_func_##n(void); \ + \ + static nxp_flexio_map_child_t \ + nxp_flexio_map_shifter_child_##n[MCUX_FLEXIO_SHIFTER_COUNT_MAX(n)] = {0}; \ + static nxp_flexio_map_child_t \ + nxp_flexio_map_timer_child_##n[MCUX_FLEXIO_TIMER_COUNT_MAX(n)] = {0}; \ + \ + static struct mcux_flexio_data mcux_flexio_data_##n = { \ + .map_shifter_child = nxp_flexio_map_shifter_child_##n, \ + .map_shifter_child_count = ARRAY_SIZE(nxp_flexio_map_shifter_child_##n), \ + .map_timer_child = nxp_flexio_map_timer_child_##n, \ + .map_timer_child_count = ARRAY_SIZE(nxp_flexio_map_timer_child_##n), \ + }; \ + \ + static const struct mcux_flexio_config mcux_flexio_config_##n = { \ + .base = (FLEXIO_Type *)DT_INST_REG_ADDR(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 = mcux_flexio_irq_config_func_##n, \ + .irq_enable_func = mcux_flexio_irq_enable_func_##n, \ + .irq_disable_func = mcux_flexio_irq_disable_func_##n, \ + }; \ + \ + DEVICE_DT_INST_DEFINE(n, &mcux_flexio_init, \ + NULL, \ + &mcux_flexio_data_##n, \ + &mcux_flexio_config_##n, \ + POST_KERNEL, \ + CONFIG_MCUX_FLEXIO_INIT_PRIORITY, \ + NULL); \ + \ + static void mcux_flexio_irq_config_func_##n(const struct device *dev) \ + { \ + IRQ_CONNECT(DT_INST_IRQN(n), DT_INST_IRQ(n, priority), \ + mcux_flexio_isr, DEVICE_DT_INST_GET(n), 0); \ + irq_enable(DT_INST_IRQN(n)); \ + } \ + \ + static void mcux_flexio_irq_enable_func_##n(void) \ + { \ + irq_enable(DT_INST_IRQN(n)); \ + } \ + \ + static void mcux_flexio_irq_disable_func_##n(void) \ + { \ + irq_disable(DT_INST_IRQN(n)); \ + } + +DT_INST_FOREACH_STATUS_OKAY(MCUX_FLEXIO_INIT) diff --git a/include/zephyr/drivers/misc/nxp_flexio/nxp_flexio.h b/include/zephyr/drivers/misc/nxp_flexio/nxp_flexio.h new file mode 100644 index 00000000000..a4c3f080162 --- /dev/null +++ b/include/zephyr/drivers/misc/nxp_flexio/nxp_flexio.h @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2024, STRIM, ALC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_DRIVERS_MISC_NXP_FLEXIO_NXP_FLEXIO_H_ +#define ZEPHYR_DRIVERS_MISC_NXP_FLEXIO_NXP_FLEXIO_H_ + +#include + +/** + * @struct nxp_flexio_child_res + * @brief Structure containing information about the required + * resources for a FlexIO child. + */ +struct nxp_flexio_child_res { + uint8_t *shifter_index; + uint8_t shifter_count; + uint8_t *timer_index; + uint8_t timer_count; +}; + +/** + * @typedef nxp_flexio_child_isr_t + * @brief Callback API to inform API user that FlexIO triggered interrupt + * + * This callback is called from IRQ context. + */ +typedef int (*nxp_flexio_child_isr_t)(void *user_data); + +/** + * @struct nxp_flexio_child + * @brief Structure containing the required child data for FlexIO + */ +struct nxp_flexio_child { + nxp_flexio_child_isr_t isr; + void *user_data; + struct nxp_flexio_child_res res; +}; + +/** + * @brief Enable FlexIO IRQ + * @param dev Pointer to the device structure for the FlexIO driver instance + */ +void nxp_flexio_irq_enable(const struct device *dev); + +/** + * @brief Disable FlexIO IRQ + * @param dev Pointer to the device structure for the FlexIO driver instance + */ +void nxp_flexio_irq_disable(const struct device *dev); + +/** + * @brief Lock FlexIO mutex. + * @param dev Pointer to the device structure for the FlexIO driver instance + */ +void nxp_flexio_lock(const struct device *dev); + +/** + * @brief Unlock FlexIO mutex. + * @param dev Pointer to the device structure for the FlexIO driver instance + */ +void nxp_flexio_unlock(const struct device *dev); + +/** + * @brief Obtain the clock rate of sub-system used by the FlexIO + * @param dev Pointer to the device structure for the FlexIO driver instance + * @param[out] rate Subsystem clock rate + * @retval 0 on successful rate reading. + * @retval -EAGAIN if rate cannot be read. Some drivers do not support returning the rate when the + * clock is off. + * @retval -ENOTSUP if reading the clock rate is not supported for the given sub-system. + * @retval -ENOSYS if the interface is not implemented. + */ +int nxp_flexio_get_rate(const struct device *dev, uint32_t *rate); + +/** + * @brief Attach flexio child to flexio controller + * @param dev Pointer to the device structure for the FlexIO driver instance + * @param child Pointer to flexio child + * @retval 0 if successful + * @retval -ENOBUFS if there are not enough available resources + */ +int nxp_flexio_child_attach(const struct device *dev, + const struct nxp_flexio_child *child); + +#endif /* ZEPHYR_DRIVERS_MISC_NXP_FLEXIO_NXP_FLEXIO_H_ */ diff --git a/west.yml b/west.yml index 2a22ac86ece..6a32031c1e6 100644 --- a/west.yml +++ b/west.yml @@ -193,7 +193,7 @@ manifest: groups: - hal - name: hal_nxp - revision: ef5060421fdba7bd86f98e50dc15db9a5c4c1b53 + revision: ac24626660f96dc734714896878dd6e1157c1c29 path: modules/hal/nxp groups: - hal