diff --git a/drivers/mfd/CMakeLists.txt b/drivers/mfd/CMakeLists.txt index 5141a86c2ee..8c45cb181bf 100644 --- a/drivers/mfd/CMakeLists.txt +++ b/drivers/mfd/CMakeLists.txt @@ -9,3 +9,4 @@ zephyr_library_sources_ifdef(CONFIG_MFD_NPM1300 mfd_npm1300.c) zephyr_library_sources_ifdef(CONFIG_MFD_NPM6001 mfd_npm6001.c) zephyr_library_sources_ifdef(CONFIG_MFD_AXP192 mfd_axp192.c) zephyr_library_sources_ifdef(CONFIG_MFD_AD5592 mfd_ad5592.c) +zephyr_library_sources_ifdef(CONFIG_NXP_LP_FLEXCOMM mfd_nxp_lp_flexcomm.c) diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 1a400d6383e..2ffdcbcca52 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -24,5 +24,6 @@ source "drivers/mfd/Kconfig.max20335" source "drivers/mfd/Kconfig.nct38xx" source "drivers/mfd/Kconfig.npm1300" source "drivers/mfd/Kconfig.npm6001" +source "drivers/mfd/Kconfig.lpflexcomm" endif # MFD diff --git a/drivers/mfd/Kconfig.lpflexcomm b/drivers/mfd/Kconfig.lpflexcomm new file mode 100644 index 00000000000..d59faa9b89b --- /dev/null +++ b/drivers/mfd/Kconfig.lpflexcomm @@ -0,0 +1,14 @@ +# Copyright 2024 NXP +# +# SPDX-License-Identifier: Apache-2.0 + +config NXP_LP_FLEXCOMM + bool "Driver for the NXP Low Power FlexComm Interface" + default y + depends on DT_HAS_NXP_LP_FLEXCOMM_ENABLED + help + Enabled the Low Power FlexComm shim driver. + LP FLexcomm allows enablement of LPUART and LPI2C + at the same time with reduced interface. This driver + checks concurrent enablement and returns and error for + unsupported concurrent enablement. diff --git a/drivers/mfd/mfd_nxp_lp_flexcomm.c b/drivers/mfd/mfd_nxp_lp_flexcomm.c new file mode 100644 index 00000000000..ec6a325d3d0 --- /dev/null +++ b/drivers/mfd/mfd_nxp_lp_flexcomm.c @@ -0,0 +1,169 @@ +/* + * Copyright 2024 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT nxp_lp_flexcomm + +#include +#include +#include +#include +#include +#include + +LOG_MODULE_REGISTER(mfd_nxp_lp_flexcomm, CONFIG_MFD_LOG_LEVEL); + +struct nxp_lp_flexcomm_child { + const struct device *dev; + uint8_t periph; + child_isr_t lp_flexcomm_child_isr; +}; + +struct nxp_lp_flexcomm_data { + struct nxp_lp_flexcomm_child *children; + size_t num_children; +}; + +struct nxp_lp_flexcomm_config { + LP_FLEXCOMM_Type *base; + void (*irq_config_func)(const struct device *dev); +}; + +void nxp_lp_flexcomm_isr(const struct device *dev) +{ + uint32_t interrupt_status; + const struct nxp_lp_flexcomm_config *config = dev->config; + struct nxp_lp_flexcomm_data *data = dev->data; + uint32_t instance = LP_FLEXCOMM_GetInstance(config->base); + struct nxp_lp_flexcomm_child *child; + + interrupt_status = LP_FLEXCOMM_GetInterruptStatus(instance); + if ((interrupt_status & + ((uint32_t)kLPFLEXCOMM_I2cSlaveInterruptFlag | + (uint32_t)kLPFLEXCOMM_I2cMasterInterruptFlag)) != 0U) { + child = &data->children[LP_FLEXCOMM_PERIPH_LPI2C]; + + if (child->lp_flexcomm_child_isr != NULL) { + child->lp_flexcomm_child_isr(child->dev); + } + } + if ((interrupt_status & + ((uint32_t)kLPFLEXCOMM_UartRxInterruptFlag | + (uint32_t)kLPFLEXCOMM_UartTxInterruptFlag)) != 0U) { + child = &data->children[LP_FLEXCOMM_PERIPH_LPUART]; + + if (child->lp_flexcomm_child_isr != NULL) { + child->lp_flexcomm_child_isr(child->dev); + } + } + if (((interrupt_status & + (uint32_t)kLPFLEXCOMM_SpiInterruptFlag)) != 0U) { + child = &data->children[LP_FLEXCOMM_PERIPH_LPSPI]; + + if (child->lp_flexcomm_child_isr != NULL) { + child->lp_flexcomm_child_isr(child->dev); + } + } +} + +void nxp_lp_flexcomm_setirqhandler(const struct device *dev, const struct device *child_dev, + LP_FLEXCOMM_PERIPH_T periph, child_isr_t handler) +{ + struct nxp_lp_flexcomm_data *data = dev->data; + struct nxp_lp_flexcomm_child *child; + + child = &data->children[periph]; + + /* Store the interrupt handler and the child device node */ + child->lp_flexcomm_child_isr = handler; + child->dev = child_dev; +} + +static int nxp_lp_flexcomm_init(const struct device *dev) +{ + const struct nxp_lp_flexcomm_config *config = dev->config; + struct nxp_lp_flexcomm_data *data = dev->data; + uint32_t instance; + struct nxp_lp_flexcomm_child *child = NULL; + bool spi_found = false; + bool uart_found = false; + bool i2c_found = false; + + for (int i = 1; i < data->num_children; i++) { + child = &data->children[i]; + if (child->periph == LP_FLEXCOMM_PERIPH_LPSPI) { + spi_found = true; + } + if (child->periph == LP_FLEXCOMM_PERIPH_LPI2C) { + i2c_found = true; + } + if (child->periph == LP_FLEXCOMM_PERIPH_LPUART) { + uart_found = true; + } + } + + /* If SPI is enabled with another interface type return an error */ + if (spi_found && (i2c_found || uart_found)) { + return -EINVAL; + } + + instance = LP_FLEXCOMM_GetInstance(config->base); + + if (uart_found && i2c_found) { + LP_FLEXCOMM_Init(instance, LP_FLEXCOMM_PERIPH_LPI2CAndLPUART); + } else if (uart_found) { + LP_FLEXCOMM_Init(instance, LP_FLEXCOMM_PERIPH_LPUART); + } else if (i2c_found) { + LP_FLEXCOMM_Init(instance, LP_FLEXCOMM_PERIPH_LPI2C); + } else if (spi_found) { + LP_FLEXCOMM_Init(instance, LP_FLEXCOMM_PERIPH_LPSPI); + } + + config->irq_config_func(dev); + + return 0; +} + +#define MCUX_FLEXCOMM_CHILD_INIT(child_node_id) \ + [DT_NODE_CHILD_IDX(child_node_id) + 1] = { \ + .periph = DT_NODE_CHILD_IDX(child_node_id) + 1, \ + }, + +#define NXP_LP_FLEXCOMM_INIT(n) \ + \ + static struct nxp_lp_flexcomm_child \ + nxp_lp_flexcomm_children_##n[LP_FLEXCOMM_PERIPH_LPI2C + 1] = { \ + DT_INST_FOREACH_CHILD_STATUS_OKAY(n, MCUX_FLEXCOMM_CHILD_INIT) \ + }; \ + \ + static void nxp_lp_flexcomm_config_func_##n(const struct device *dev); \ + \ + static const struct nxp_lp_flexcomm_config nxp_lp_flexcomm_config_##n = { \ + .base = (LP_FLEXCOMM_Type *)DT_INST_REG_ADDR(n), \ + .irq_config_func = nxp_lp_flexcomm_config_func_##n, \ + }; \ + \ + static struct nxp_lp_flexcomm_data nxp_lp_flexcomm_data_##n = { \ + .children = nxp_lp_flexcomm_children_##n, \ + .num_children = ARRAY_SIZE(nxp_lp_flexcomm_children_##n), \ + }; \ + \ + DEVICE_DT_INST_DEFINE(n, \ + &nxp_lp_flexcomm_init, \ + NULL, \ + &nxp_lp_flexcomm_data_##n, \ + &nxp_lp_flexcomm_config_##n, \ + PRE_KERNEL_1, \ + CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, \ + NULL); \ + \ + static void nxp_lp_flexcomm_config_func_##n(const struct device *dev) \ + { \ + IRQ_CONNECT(DT_INST_IRQN(n), DT_INST_IRQ(n, priority), \ + nxp_lp_flexcomm_isr, DEVICE_DT_INST_GET(n), 0); \ + irq_enable(DT_INST_IRQN(n)); \ + } + +DT_INST_FOREACH_STATUS_OKAY(NXP_LP_FLEXCOMM_INIT) diff --git a/include/zephyr/drivers/mfd/nxp_lp_flexcomm.h b/include/zephyr/drivers/mfd/nxp_lp_flexcomm.h new file mode 100644 index 00000000000..0faed3c9ddf --- /dev/null +++ b/include/zephyr/drivers/mfd/nxp_lp_flexcomm.h @@ -0,0 +1,16 @@ +/* + * Copyright 2024 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef ZEPHYR_DRIVERS_NXP_LP_FLEXCOMM_H_ +#define ZEPHYR_DRIVERS_NXP_LP_FLEXCOMM_H_ + +#include "fsl_lpflexcomm.h" + +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); + +#endif /* ZEPHYR_DRIVERS_NXP_LP_FLEXCOMM_H_ */