1 /*
2  * Copyright 2024 NXP
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #define DT_DRV_COMPAT nxp_lp_flexcomm
8 
9 #include <errno.h>
10 #include <zephyr/device.h>
11 #include <zephyr/irq.h>
12 #include <zephyr/kernel.h>
13 #include <zephyr/logging/log.h>
14 #include <zephyr/drivers/mfd/nxp_lp_flexcomm.h>
15 
16 LOG_MODULE_REGISTER(mfd_nxp_lp_flexcomm, CONFIG_MFD_LOG_LEVEL);
17 
18 struct nxp_lp_flexcomm_child {
19 	const struct device *dev;
20 	uint8_t periph;
21 	child_isr_t lp_flexcomm_child_isr;
22 };
23 
24 struct nxp_lp_flexcomm_data {
25 	struct nxp_lp_flexcomm_child *children;
26 	size_t num_children;
27 };
28 
29 struct nxp_lp_flexcomm_config {
30 	LP_FLEXCOMM_Type *base;
31 	void (*irq_config_func)(const struct device *dev);
32 };
33 
nxp_lp_flexcomm_isr(const struct device * dev)34 void nxp_lp_flexcomm_isr(const struct device *dev)
35 {
36 	uint32_t interrupt_status;
37 	const struct nxp_lp_flexcomm_config *config = dev->config;
38 	struct nxp_lp_flexcomm_data *data = dev->data;
39 	uint32_t instance = LP_FLEXCOMM_GetInstance(config->base);
40 	struct nxp_lp_flexcomm_child *child;
41 
42 	interrupt_status = LP_FLEXCOMM_GetInterruptStatus(instance);
43 	if ((interrupt_status &
44 	    ((uint32_t)kLPFLEXCOMM_I2cSlaveInterruptFlag |
45 	     (uint32_t)kLPFLEXCOMM_I2cMasterInterruptFlag)) != 0U) {
46 		child = &data->children[LP_FLEXCOMM_PERIPH_LPI2C];
47 
48 		if (child->lp_flexcomm_child_isr != NULL) {
49 			child->lp_flexcomm_child_isr(child->dev);
50 		}
51 	}
52 	if ((interrupt_status &
53 	     ((uint32_t)kLPFLEXCOMM_UartRxInterruptFlag |
54 	      (uint32_t)kLPFLEXCOMM_UartTxInterruptFlag)) != 0U) {
55 		child = &data->children[LP_FLEXCOMM_PERIPH_LPUART];
56 
57 		if (child->lp_flexcomm_child_isr != NULL) {
58 			child->lp_flexcomm_child_isr(child->dev);
59 		}
60 	}
61 	if (((interrupt_status &
62 	     (uint32_t)kLPFLEXCOMM_SpiInterruptFlag)) != 0U) {
63 		child = &data->children[LP_FLEXCOMM_PERIPH_LPSPI];
64 
65 		if (child->lp_flexcomm_child_isr != NULL) {
66 			child->lp_flexcomm_child_isr(child->dev);
67 		}
68 	}
69 }
70 
nxp_lp_flexcomm_setirqhandler(const struct device * dev,const struct device * child_dev,LP_FLEXCOMM_PERIPH_T periph,child_isr_t handler)71 void nxp_lp_flexcomm_setirqhandler(const struct device *dev, const struct device *child_dev,
72 				   LP_FLEXCOMM_PERIPH_T periph, child_isr_t handler)
73 {
74 	struct nxp_lp_flexcomm_data *data = dev->data;
75 	struct nxp_lp_flexcomm_child *child;
76 
77 	child = &data->children[periph];
78 
79 	/* Store the interrupt handler and the child device node */
80 	child->lp_flexcomm_child_isr = handler;
81 	child->dev = child_dev;
82 }
83 
nxp_lp_flexcomm_init(const struct device * dev)84 static int nxp_lp_flexcomm_init(const struct device *dev)
85 {
86 	const struct nxp_lp_flexcomm_config *config = dev->config;
87 	struct nxp_lp_flexcomm_data *data = dev->data;
88 	uint32_t instance;
89 	struct nxp_lp_flexcomm_child *child = NULL;
90 	bool spi_found = false;
91 	bool uart_found = false;
92 	bool i2c_found = false;
93 
94 	for (int i = 1; i < data->num_children; i++) {
95 		child = &data->children[i];
96 		if (child->periph == LP_FLEXCOMM_PERIPH_LPSPI) {
97 			spi_found = true;
98 		}
99 		if (child->periph == LP_FLEXCOMM_PERIPH_LPI2C) {
100 			i2c_found = true;
101 		}
102 		if (child->periph == LP_FLEXCOMM_PERIPH_LPUART) {
103 			uart_found = true;
104 		}
105 	}
106 
107 	/* If SPI is enabled with another interface type return an error */
108 	if (spi_found && (i2c_found || uart_found)) {
109 		return -EINVAL;
110 	}
111 
112 	instance = LP_FLEXCOMM_GetInstance(config->base);
113 
114 	if (uart_found && i2c_found) {
115 		LP_FLEXCOMM_Init(instance, LP_FLEXCOMM_PERIPH_LPI2CAndLPUART);
116 	} else if (uart_found) {
117 		LP_FLEXCOMM_Init(instance, LP_FLEXCOMM_PERIPH_LPUART);
118 	} else if (i2c_found) {
119 		LP_FLEXCOMM_Init(instance, LP_FLEXCOMM_PERIPH_LPI2C);
120 	} else if (spi_found) {
121 		LP_FLEXCOMM_Init(instance, LP_FLEXCOMM_PERIPH_LPSPI);
122 	}
123 
124 	config->irq_config_func(dev);
125 
126 	return 0;
127 }
128 
129 #define MCUX_FLEXCOMM_CHILD_INIT(child_node_id)					\
130 	[DT_NODE_CHILD_IDX(child_node_id) + 1] = {				\
131 		.periph = DT_NODE_CHILD_IDX(child_node_id) + 1,			\
132 	},
133 
134 #define NXP_LP_FLEXCOMM_INIT(n)						\
135 										\
136 	static struct nxp_lp_flexcomm_child					\
137 		nxp_lp_flexcomm_children_##n[LP_FLEXCOMM_PERIPH_LPI2C + 1] = {	\
138 		DT_INST_FOREACH_CHILD_STATUS_OKAY(n, MCUX_FLEXCOMM_CHILD_INIT)	\
139 	};									\
140 										\
141 	static void nxp_lp_flexcomm_config_func_##n(const struct device *dev);	\
142 										\
143 	static const struct nxp_lp_flexcomm_config nxp_lp_flexcomm_config_##n = { \
144 		.base = (LP_FLEXCOMM_Type *)DT_INST_REG_ADDR(n),		\
145 		.irq_config_func = nxp_lp_flexcomm_config_func_##n,		\
146 	};									\
147 										\
148 	static struct nxp_lp_flexcomm_data nxp_lp_flexcomm_data_##n = {		\
149 		.children = nxp_lp_flexcomm_children_##n,			\
150 		.num_children = ARRAY_SIZE(nxp_lp_flexcomm_children_##n),	\
151 	};									\
152 										\
153 	DEVICE_DT_INST_DEFINE(n,						\
154 			    &nxp_lp_flexcomm_init,				\
155 			    NULL,						\
156 			    &nxp_lp_flexcomm_data_##n,				\
157 			    &nxp_lp_flexcomm_config_##n,			\
158 			    PRE_KERNEL_1,					\
159 			    CONFIG_KERNEL_INIT_PRIORITY_DEFAULT,		\
160 			    NULL);						\
161 										\
162 	static void nxp_lp_flexcomm_config_func_##n(const struct device *dev)	\
163 	{									\
164 		IRQ_CONNECT(DT_INST_IRQN(n), DT_INST_IRQ(n, priority),		\
165 			    nxp_lp_flexcomm_isr, DEVICE_DT_INST_GET(n), 0);	\
166 		irq_enable(DT_INST_IRQN(n));					\
167 	}
168 
169 DT_INST_FOREACH_STATUS_OKAY(NXP_LP_FLEXCOMM_INIT)
170