1 /*
2 * Copyright (c) 2017, NXP
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #define DT_DRV_COMPAT nxp_kinetis_lpsci
8
9 #include <errno.h>
10 #include <zephyr/device.h>
11 #include <zephyr/drivers/uart.h>
12 #include <zephyr/drivers/clock_control.h>
13 #include <zephyr/drivers/pinctrl.h>
14 #include <fsl_lpsci.h>
15 #include <soc.h>
16 #include <zephyr/irq.h>
17
18 struct mcux_lpsci_config {
19 UART0_Type *base;
20 const struct device *clock_dev;
21 clock_control_subsys_t clock_subsys;
22 uint32_t baud_rate;
23 #ifdef CONFIG_UART_INTERRUPT_DRIVEN
24 void (*irq_config_func)(const struct device *dev);
25 #endif
26 const struct pinctrl_dev_config *pincfg;
27 };
28
29 struct mcux_lpsci_data {
30 #ifdef CONFIG_UART_INTERRUPT_DRIVEN
31 uart_irq_callback_user_data_t callback;
32 void *cb_data;
33 #endif
34 };
35
mcux_lpsci_poll_in(const struct device * dev,unsigned char * c)36 static int mcux_lpsci_poll_in(const struct device *dev, unsigned char *c)
37 {
38 const struct mcux_lpsci_config *config = dev->config;
39 uint32_t flags = LPSCI_GetStatusFlags(config->base);
40 int ret = -1;
41
42 if (flags & kLPSCI_RxDataRegFullFlag) {
43 *c = LPSCI_ReadByte(config->base);
44 ret = 0;
45 }
46
47 return ret;
48 }
49
mcux_lpsci_poll_out(const struct device * dev,unsigned char c)50 static void mcux_lpsci_poll_out(const struct device *dev, unsigned char c)
51 {
52 const struct mcux_lpsci_config *config = dev->config;
53
54 while (!(LPSCI_GetStatusFlags(config->base)
55 & kLPSCI_TxDataRegEmptyFlag)) {
56 }
57
58 LPSCI_WriteByte(config->base, c);
59 }
60
mcux_lpsci_err_check(const struct device * dev)61 static int mcux_lpsci_err_check(const struct device *dev)
62 {
63 const struct mcux_lpsci_config *config = dev->config;
64 uint32_t flags = LPSCI_GetStatusFlags(config->base);
65 int err = 0;
66
67 if (flags & kLPSCI_RxOverrunFlag) {
68 err |= UART_ERROR_OVERRUN;
69 }
70
71 if (flags & kLPSCI_ParityErrorFlag) {
72 err |= UART_ERROR_PARITY;
73 }
74
75 if (flags & kLPSCI_FramingErrorFlag) {
76 err |= UART_ERROR_FRAMING;
77 }
78
79 LPSCI_ClearStatusFlags(config->base, kLPSCI_RxOverrunFlag |
80 kLPSCI_ParityErrorFlag |
81 kLPSCI_FramingErrorFlag);
82
83 return err;
84 }
85
86 #ifdef CONFIG_UART_INTERRUPT_DRIVEN
mcux_lpsci_fifo_fill(const struct device * dev,const uint8_t * tx_data,int len)87 static int mcux_lpsci_fifo_fill(const struct device *dev,
88 const uint8_t *tx_data,
89 int len)
90 {
91 const struct mcux_lpsci_config *config = dev->config;
92 uint8_t num_tx = 0U;
93
94 while ((len - num_tx > 0) &&
95 (LPSCI_GetStatusFlags(config->base)
96 & kLPSCI_TxDataRegEmptyFlag)) {
97
98 LPSCI_WriteByte(config->base, tx_data[num_tx++]);
99 }
100
101 return num_tx;
102 }
103
mcux_lpsci_fifo_read(const struct device * dev,uint8_t * rx_data,const int len)104 static int mcux_lpsci_fifo_read(const struct device *dev, uint8_t *rx_data,
105 const int len)
106 {
107 const struct mcux_lpsci_config *config = dev->config;
108 uint8_t num_rx = 0U;
109
110 while ((len - num_rx > 0) &&
111 (LPSCI_GetStatusFlags(config->base)
112 & kLPSCI_RxDataRegFullFlag)) {
113
114 rx_data[num_rx++] = LPSCI_ReadByte(config->base);
115 }
116
117 return num_rx;
118 }
119
mcux_lpsci_irq_tx_enable(const struct device * dev)120 static void mcux_lpsci_irq_tx_enable(const struct device *dev)
121 {
122 const struct mcux_lpsci_config *config = dev->config;
123 uint32_t mask = kLPSCI_TxDataRegEmptyInterruptEnable;
124
125 LPSCI_EnableInterrupts(config->base, mask);
126 }
127
mcux_lpsci_irq_tx_disable(const struct device * dev)128 static void mcux_lpsci_irq_tx_disable(const struct device *dev)
129 {
130 const struct mcux_lpsci_config *config = dev->config;
131 uint32_t mask = kLPSCI_TxDataRegEmptyInterruptEnable;
132
133 LPSCI_DisableInterrupts(config->base, mask);
134 }
135
mcux_lpsci_irq_tx_complete(const struct device * dev)136 static int mcux_lpsci_irq_tx_complete(const struct device *dev)
137 {
138 const struct mcux_lpsci_config *config = dev->config;
139 uint32_t flags = LPSCI_GetStatusFlags(config->base);
140
141 return (flags & kLPSCI_TransmissionCompleteFlag) != 0U;
142 }
143
mcux_lpsci_irq_tx_ready(const struct device * dev)144 static int mcux_lpsci_irq_tx_ready(const struct device *dev)
145 {
146 const struct mcux_lpsci_config *config = dev->config;
147 uint32_t mask = kLPSCI_TxDataRegEmptyInterruptEnable;
148 uint32_t flags = LPSCI_GetStatusFlags(config->base);
149
150 return (LPSCI_GetEnabledInterrupts(config->base) & mask)
151 && (flags & kLPSCI_TxDataRegEmptyFlag);
152 }
153
mcux_lpsci_irq_rx_enable(const struct device * dev)154 static void mcux_lpsci_irq_rx_enable(const struct device *dev)
155 {
156 const struct mcux_lpsci_config *config = dev->config;
157 uint32_t mask = kLPSCI_RxDataRegFullInterruptEnable;
158
159 LPSCI_EnableInterrupts(config->base, mask);
160 }
161
mcux_lpsci_irq_rx_disable(const struct device * dev)162 static void mcux_lpsci_irq_rx_disable(const struct device *dev)
163 {
164 const struct mcux_lpsci_config *config = dev->config;
165 uint32_t mask = kLPSCI_RxDataRegFullInterruptEnable;
166
167 LPSCI_DisableInterrupts(config->base, mask);
168 }
169
mcux_lpsci_irq_rx_full(const struct device * dev)170 static int mcux_lpsci_irq_rx_full(const struct device *dev)
171 {
172 const struct mcux_lpsci_config *config = dev->config;
173 uint32_t flags = LPSCI_GetStatusFlags(config->base);
174
175 return (flags & kLPSCI_RxDataRegFullFlag) != 0U;
176 }
177
mcux_lpsci_irq_rx_pending(const struct device * dev)178 static int mcux_lpsci_irq_rx_pending(const struct device *dev)
179 {
180 const struct mcux_lpsci_config *config = dev->config;
181 uint32_t mask = kLPSCI_RxDataRegFullInterruptEnable;
182
183 return (LPSCI_GetEnabledInterrupts(config->base) & mask)
184 && mcux_lpsci_irq_rx_full(dev);
185 }
186
mcux_lpsci_irq_err_enable(const struct device * dev)187 static void mcux_lpsci_irq_err_enable(const struct device *dev)
188 {
189 const struct mcux_lpsci_config *config = dev->config;
190 uint32_t mask = kLPSCI_NoiseErrorInterruptEnable |
191 kLPSCI_FramingErrorInterruptEnable |
192 kLPSCI_ParityErrorInterruptEnable;
193
194 LPSCI_EnableInterrupts(config->base, mask);
195 }
196
mcux_lpsci_irq_err_disable(const struct device * dev)197 static void mcux_lpsci_irq_err_disable(const struct device *dev)
198 {
199 const struct mcux_lpsci_config *config = dev->config;
200 uint32_t mask = kLPSCI_NoiseErrorInterruptEnable |
201 kLPSCI_FramingErrorInterruptEnable |
202 kLPSCI_ParityErrorInterruptEnable;
203
204 LPSCI_DisableInterrupts(config->base, mask);
205 }
206
mcux_lpsci_irq_is_pending(const struct device * dev)207 static int mcux_lpsci_irq_is_pending(const struct device *dev)
208 {
209 return (mcux_lpsci_irq_tx_ready(dev)
210 || mcux_lpsci_irq_rx_pending(dev));
211 }
212
mcux_lpsci_irq_update(const struct device * dev)213 static int mcux_lpsci_irq_update(const struct device *dev)
214 {
215 return 1;
216 }
217
mcux_lpsci_irq_callback_set(const struct device * dev,uart_irq_callback_user_data_t cb,void * cb_data)218 static void mcux_lpsci_irq_callback_set(const struct device *dev,
219 uart_irq_callback_user_data_t cb,
220 void *cb_data)
221 {
222 struct mcux_lpsci_data *data = dev->data;
223
224 data->callback = cb;
225 data->cb_data = cb_data;
226 }
227
mcux_lpsci_isr(const struct device * dev)228 static void mcux_lpsci_isr(const struct device *dev)
229 {
230 struct mcux_lpsci_data *data = dev->data;
231
232 if (data->callback) {
233 data->callback(dev, data->cb_data);
234 }
235 }
236 #endif /* CONFIG_UART_INTERRUPT_DRIVEN */
237
mcux_lpsci_init(const struct device * dev)238 static int mcux_lpsci_init(const struct device *dev)
239 {
240 const struct mcux_lpsci_config *config = dev->config;
241 lpsci_config_t uart_config;
242 uint32_t clock_freq;
243 int err;
244
245 if (!device_is_ready(config->clock_dev)) {
246 return -ENODEV;
247 }
248
249 if (clock_control_get_rate(config->clock_dev, config->clock_subsys,
250 &clock_freq)) {
251 return -EINVAL;
252 }
253
254 LPSCI_GetDefaultConfig(&uart_config);
255 uart_config.enableTx = true;
256 uart_config.enableRx = true;
257 uart_config.baudRate_Bps = config->baud_rate;
258
259 LPSCI_Init(config->base, &uart_config, clock_freq);
260
261 err = pinctrl_apply_state(config->pincfg, PINCTRL_STATE_DEFAULT);
262 if (err < 0) {
263 return err;
264 }
265
266 #ifdef CONFIG_UART_INTERRUPT_DRIVEN
267 config->irq_config_func(dev);
268 #endif
269
270 return 0;
271 }
272
273 static const struct uart_driver_api mcux_lpsci_driver_api = {
274 .poll_in = mcux_lpsci_poll_in,
275 .poll_out = mcux_lpsci_poll_out,
276 .err_check = mcux_lpsci_err_check,
277 #ifdef CONFIG_UART_INTERRUPT_DRIVEN
278 .fifo_fill = mcux_lpsci_fifo_fill,
279 .fifo_read = mcux_lpsci_fifo_read,
280 .irq_tx_enable = mcux_lpsci_irq_tx_enable,
281 .irq_tx_disable = mcux_lpsci_irq_tx_disable,
282 .irq_tx_complete = mcux_lpsci_irq_tx_complete,
283 .irq_tx_ready = mcux_lpsci_irq_tx_ready,
284 .irq_rx_enable = mcux_lpsci_irq_rx_enable,
285 .irq_rx_disable = mcux_lpsci_irq_rx_disable,
286 .irq_rx_ready = mcux_lpsci_irq_rx_full,
287 .irq_err_enable = mcux_lpsci_irq_err_enable,
288 .irq_err_disable = mcux_lpsci_irq_err_disable,
289 .irq_is_pending = mcux_lpsci_irq_is_pending,
290 .irq_update = mcux_lpsci_irq_update,
291 .irq_callback_set = mcux_lpsci_irq_callback_set,
292 #endif
293 };
294
295 #ifdef CONFIG_UART_INTERRUPT_DRIVEN
296 #define MCUX_LPSCI_CONFIG_FUNC(n) \
297 static void mcux_lpsci_config_func_##n(const struct device *dev) \
298 { \
299 IRQ_CONNECT(DT_INST_IRQN(n), \
300 DT_INST_IRQ(n, priority), \
301 mcux_lpsci_isr, DEVICE_DT_INST_GET(n), 0); \
302 \
303 irq_enable(DT_INST_IRQN(n)); \
304 }
305 #define MCUX_LPSCI_IRQ_CFG_FUNC_INIT(n) \
306 .irq_config_func = mcux_lpsci_config_func_##n
307 #define MCUX_LPSCI_INIT_CFG(n) \
308 MCUX_LPSCI_DECLARE_CFG(n, MCUX_LPSCI_IRQ_CFG_FUNC_INIT(n))
309 #else
310 #define MCUX_LPSCI_CONFIG_FUNC(n)
311 #define MCUX_LPSCI_IRQ_CFG_FUNC_INIT
312 #define MCUX_LPSCI_INIT_CFG(n) \
313 MCUX_LPSCI_DECLARE_CFG(n, MCUX_LPSCI_IRQ_CFG_FUNC_INIT)
314 #endif
315
316 #define MCUX_LPSCI_DECLARE_CFG(n, IRQ_FUNC_INIT) \
317 static const struct mcux_lpsci_config mcux_lpsci_##n##_config = { \
318 .base = (UART0_Type *)DT_INST_REG_ADDR(n), \
319 .clock_dev = DEVICE_DT_GET(DT_INST_CLOCKS_CTLR(n)), \
320 .clock_subsys = (clock_control_subsys_t)DT_INST_CLOCKS_CELL(n, name),\
321 .baud_rate = DT_INST_PROP(n, current_speed), \
322 .pincfg = PINCTRL_DT_INST_DEV_CONFIG_GET(n), \
323 IRQ_FUNC_INIT \
324 }
325
326 #define MCUX_LPSCI_INIT(n) \
327 PINCTRL_DT_INST_DEFINE(n); \
328 \
329 static struct mcux_lpsci_data mcux_lpsci_##n##_data; \
330 \
331 static const struct mcux_lpsci_config mcux_lpsci_##n##_config; \
332 \
333 DEVICE_DT_INST_DEFINE(n, \
334 &mcux_lpsci_init, \
335 NULL, \
336 &mcux_lpsci_##n##_data, \
337 &mcux_lpsci_##n##_config, \
338 PRE_KERNEL_1, \
339 CONFIG_SERIAL_INIT_PRIORITY, \
340 &mcux_lpsci_driver_api); \
341 \
342 MCUX_LPSCI_CONFIG_FUNC(n) \
343 \
344 MCUX_LPSCI_INIT_CFG(n);
345
346 DT_INST_FOREACH_STATUS_OKAY(MCUX_LPSCI_INIT)
347