1 /*
2  * Copyright (c) 2024, Yishai Jaffe
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #define DT_DRV_COMPAT silabs_eusart_uart
8 
9 #include <errno.h>
10 #include <zephyr/drivers/clock_control.h>
11 #include <zephyr/drivers/clock_control/clock_control_silabs.h>
12 #include <zephyr/drivers/pinctrl.h>
13 #include <zephyr/drivers/uart.h>
14 #include <zephyr/irq.h>
15 #include <zephyr/pm/device.h>
16 #include <em_eusart.h>
17 
18 struct uart_silabs_eusart_config {
19 	EUSART_TypeDef *eusart;
20 	const struct pinctrl_dev_config *pcfg;
21 	const struct device *clock_dev;
22 	const struct silabs_clock_control_cmu_config clock_cfg;
23 #ifdef CONFIG_UART_INTERRUPT_DRIVEN
24 	void (*irq_config_func)(const struct device *dev);
25 #endif
26 };
27 
28 struct uart_silabs_eusart_data {
29 	struct uart_config uart_cfg;
30 #ifdef CONFIG_UART_INTERRUPT_DRIVEN
31 	uart_irq_callback_user_data_t callback;
32 	void *cb_data;
33 #endif
34 };
35 
uart_silabs_eusart_poll_in(const struct device * dev,unsigned char * c)36 static int uart_silabs_eusart_poll_in(const struct device *dev, unsigned char *c)
37 {
38 	const struct uart_silabs_eusart_config *config = dev->config;
39 
40 	if (EUSART_StatusGet(config->eusart) & EUSART_STATUS_RXFL) {
41 		*c = EUSART_Rx(config->eusart);
42 		return 0;
43 	}
44 
45 	return -1;
46 }
47 
uart_silabs_eusart_poll_out(const struct device * dev,unsigned char c)48 static void uart_silabs_eusart_poll_out(const struct device *dev, unsigned char c)
49 {
50 	const struct uart_silabs_eusart_config *config = dev->config;
51 
52 	/* EUSART_Tx function already waits for the transmit buffer being empty
53 	 * and waits for the bus to be free to transmit.
54 	 */
55 	EUSART_Tx(config->eusart, c);
56 }
57 
uart_silabs_eusart_err_check(const struct device * dev)58 static int uart_silabs_eusart_err_check(const struct device *dev)
59 {
60 	const struct uart_silabs_eusart_config *config = dev->config;
61 	uint32_t flags = EUSART_IntGet(config->eusart);
62 	int err = 0;
63 
64 	if (flags & EUSART_IF_RXOF) {
65 		err |= UART_ERROR_OVERRUN;
66 	}
67 
68 	if (flags & EUSART_IF_PERR) {
69 		err |= UART_ERROR_PARITY;
70 	}
71 
72 	if (flags & EUSART_IF_FERR) {
73 		err |= UART_ERROR_FRAMING;
74 	}
75 
76 	EUSART_IntClear(config->eusart, EUSART_IF_RXOF | EUSART_IF_PERR | EUSART_IF_FERR);
77 
78 	return err;
79 }
80 
81 #ifdef CONFIG_UART_INTERRUPT_DRIVEN
uart_silabs_eusart_fifo_fill(const struct device * dev,const uint8_t * tx_data,int len)82 static int uart_silabs_eusart_fifo_fill(const struct device *dev,
83 				  const uint8_t *tx_data,
84 				  int len)
85 {
86 	const struct uart_silabs_eusart_config *config = dev->config;
87 	int num_tx = 0;
88 
89 	while ((len - num_tx > 0) &&
90 	       (EUSART_StatusGet(config->eusart) & EUSART_STATUS_TXFL)) {
91 
92 		config->eusart->TXDATA = (uint32_t)tx_data[num_tx++];
93 	}
94 
95 	if (!(EUSART_StatusGet(config->eusart) & EUSART_STATUS_TXFL)) {
96 		EUSART_IntClear(config->eusart, EUSART_IF_TXFL);
97 	}
98 
99 	return num_tx;
100 }
101 
uart_silabs_eusart_fifo_read(const struct device * dev,uint8_t * rx_data,const int len)102 static int uart_silabs_eusart_fifo_read(const struct device *dev, uint8_t *rx_data,
103 				  const int len)
104 {
105 	const struct uart_silabs_eusart_config *config = dev->config;
106 	int num_rx = 0;
107 
108 	while ((len - num_rx > 0) &&
109 	       (EUSART_StatusGet(config->eusart) & EUSART_STATUS_RXFL)) {
110 		rx_data[num_rx++] = (uint8_t)config->eusart->RXDATA;
111 	}
112 
113 	if (!(EUSART_StatusGet(config->eusart) & EUSART_STATUS_RXFL)) {
114 		EUSART_IntClear(config->eusart, EUSART_IF_RXFL);
115 	}
116 
117 	return num_rx;
118 }
119 
uart_silabs_eusart_irq_tx_enable(const struct device * dev)120 static void uart_silabs_eusart_irq_tx_enable(const struct device *dev)
121 {
122 	const struct uart_silabs_eusart_config *config = dev->config;
123 
124 	EUSART_IntClear(config->eusart, EUSART_IEN_TXFL | EUSART_IEN_TXC);
125 	EUSART_IntEnable(config->eusart, EUSART_IEN_TXFL | EUSART_IEN_TXC);
126 }
127 
uart_silabs_eusart_irq_tx_disable(const struct device * dev)128 static void uart_silabs_eusart_irq_tx_disable(const struct device *dev)
129 {
130 	const struct uart_silabs_eusart_config *config = dev->config;
131 
132 	EUSART_IntDisable(config->eusart, EUSART_IEN_TXFL | EUSART_IEN_TXC);
133 	EUSART_IntClear(config->eusart, EUSART_IEN_TXFL | EUSART_IEN_TXC);
134 }
135 
uart_silabs_eusart_irq_tx_complete(const struct device * dev)136 static int uart_silabs_eusart_irq_tx_complete(const struct device *dev)
137 {
138 	const struct uart_silabs_eusart_config *config = dev->config;
139 	uint32_t flags = EUSART_IntGet(config->eusart);
140 
141 	EUSART_IntClear(config->eusart, EUSART_IF_TXC);
142 
143 	return (flags & EUSART_IF_TXC) != 0;
144 }
145 
uart_silabs_eusart_irq_tx_ready(const struct device * dev)146 static int uart_silabs_eusart_irq_tx_ready(const struct device *dev)
147 {
148 	const struct uart_silabs_eusart_config *config = dev->config;
149 
150 	return (config->eusart->IEN & EUSART_IEN_TXFL)
151 		&& (EUSART_IntGet(config->eusart) & EUSART_IF_TXFL);
152 }
153 
uart_silabs_eusart_irq_rx_enable(const struct device * dev)154 static void uart_silabs_eusart_irq_rx_enable(const struct device *dev)
155 {
156 	const struct uart_silabs_eusart_config *config = dev->config;
157 
158 	EUSART_IntClear(config->eusart, EUSART_IEN_RXFL);
159 	EUSART_IntEnable(config->eusart, EUSART_IEN_RXFL);
160 }
161 
uart_silabs_eusart_irq_rx_disable(const struct device * dev)162 static void uart_silabs_eusart_irq_rx_disable(const struct device *dev)
163 {
164 	const struct uart_silabs_eusart_config *config = dev->config;
165 
166 	EUSART_IntDisable(config->eusart, EUSART_IEN_RXFL);
167 	EUSART_IntClear(config->eusart, EUSART_IEN_RXFL);
168 }
169 
uart_silabs_eusart_irq_rx_ready(const struct device * dev)170 static int uart_silabs_eusart_irq_rx_ready(const struct device *dev)
171 {
172 	const struct uart_silabs_eusart_config *config = dev->config;
173 
174 	return (config->eusart->IEN & EUSART_IEN_RXFL)
175 		&& (EUSART_IntGet(config->eusart) & EUSART_IF_RXFL);
176 }
177 
uart_silabs_eusart_irq_err_enable(const struct device * dev)178 static void uart_silabs_eusart_irq_err_enable(const struct device *dev)
179 {
180 	const struct uart_silabs_eusart_config *config = dev->config;
181 
182 	EUSART_IntClear(config->eusart, EUSART_IF_RXOF | EUSART_IF_PERR | EUSART_IF_FERR);
183 	EUSART_IntEnable(config->eusart, EUSART_IF_RXOF | EUSART_IF_PERR | EUSART_IF_FERR);
184 }
185 
uart_silabs_eusart_irq_err_disable(const struct device * dev)186 static void uart_silabs_eusart_irq_err_disable(const struct device *dev)
187 {
188 	const struct uart_silabs_eusart_config *config = dev->config;
189 
190 	EUSART_IntDisable(config->eusart, EUSART_IF_RXOF | EUSART_IF_PERR | EUSART_IF_FERR);
191 	EUSART_IntClear(config->eusart, EUSART_IF_RXOF | EUSART_IF_PERR | EUSART_IF_FERR);
192 }
193 
uart_silabs_eusart_irq_is_pending(const struct device * dev)194 static int uart_silabs_eusart_irq_is_pending(const struct device *dev)
195 {
196 	return uart_silabs_eusart_irq_tx_ready(dev) || uart_silabs_eusart_irq_rx_ready(dev);
197 }
198 
uart_silabs_eusart_irq_update(const struct device * dev)199 static int uart_silabs_eusart_irq_update(const struct device *dev)
200 {
201 	return 1;
202 }
203 
uart_silabs_eusart_irq_callback_set(const struct device * dev,uart_irq_callback_user_data_t cb,void * cb_data)204 static void uart_silabs_eusart_irq_callback_set(const struct device *dev,
205 					  uart_irq_callback_user_data_t cb,
206 					  void *cb_data)
207 {
208 	struct uart_silabs_eusart_data *data = dev->data;
209 
210 	data->callback = cb;
211 	data->cb_data = cb_data;
212 }
213 
uart_silabs_eusart_isr(const struct device * dev)214 static void uart_silabs_eusart_isr(const struct device *dev)
215 {
216 	struct uart_silabs_eusart_data *data = dev->data;
217 
218 	if (data->callback) {
219 		data->callback(dev, data->cb_data);
220 	}
221 }
222 #endif /* CONFIG_UART_INTERRUPT_DRIVEN */
223 
uart_silabs_eusart_cfg2ll_parity(enum uart_config_parity parity)224 static inline EUSART_Parity_TypeDef uart_silabs_eusart_cfg2ll_parity(
225 	enum uart_config_parity parity)
226 {
227 	switch (parity) {
228 	case UART_CFG_PARITY_ODD:
229 		return eusartOddParity;
230 	case UART_CFG_PARITY_EVEN:
231 		return eusartEvenParity;
232 	case UART_CFG_PARITY_NONE:
233 	default:
234 		return eusartNoParity;
235 	}
236 }
237 
uart_silabs_eusart_ll2cfg_parity(EUSART_Parity_TypeDef parity)238 static inline enum uart_config_parity uart_silabs_eusart_ll2cfg_parity(
239 	EUSART_Parity_TypeDef parity)
240 {
241 	switch (parity) {
242 	case eusartOddParity:
243 		return UART_CFG_PARITY_ODD;
244 	case eusartEvenParity:
245 		return UART_CFG_PARITY_EVEN;
246 	case eusartNoParity:
247 	default:
248 		return UART_CFG_PARITY_NONE;
249 	}
250 }
251 
uart_silabs_eusart_cfg2ll_stopbits(enum uart_config_stop_bits sb)252 static inline EUSART_Stopbits_TypeDef uart_silabs_eusart_cfg2ll_stopbits(
253 	enum uart_config_stop_bits sb)
254 {
255 	switch (sb) {
256 	case UART_CFG_STOP_BITS_0_5:
257 		return eusartStopbits0p5;
258 	case UART_CFG_STOP_BITS_1:
259 		return eusartStopbits1;
260 	case UART_CFG_STOP_BITS_2:
261 		return eusartStopbits2;
262 	case UART_CFG_STOP_BITS_1_5:
263 		return eusartStopbits1p5;
264 	default:
265 		return eusartStopbits1;
266 	}
267 }
268 
uart_silabs_eusart_ll2cfg_stopbits(EUSART_Stopbits_TypeDef sb)269 static inline enum uart_config_stop_bits uart_silabs_eusart_ll2cfg_stopbits(
270 	EUSART_Stopbits_TypeDef sb)
271 {
272 	switch (sb) {
273 	case eusartStopbits0p5:
274 		return UART_CFG_STOP_BITS_0_5;
275 	case eusartStopbits1:
276 		return UART_CFG_STOP_BITS_1;
277 	case eusartStopbits1p5:
278 		return UART_CFG_STOP_BITS_1_5;
279 	case eusartStopbits2:
280 		return UART_CFG_STOP_BITS_2;
281 	default:
282 		return UART_CFG_STOP_BITS_1;
283 	}
284 }
285 
uart_silabs_eusart_cfg2ll_databits(enum uart_config_data_bits db,enum uart_config_parity p)286 static inline EUSART_Databits_TypeDef uart_silabs_eusart_cfg2ll_databits(
287 	enum uart_config_data_bits db, enum uart_config_parity p)
288 {
289 	switch (db) {
290 	case UART_CFG_DATA_BITS_7:
291 		if (p == UART_CFG_PARITY_NONE) {
292 			return eusartDataBits7;
293 		} else {
294 			return eusartDataBits8;
295 		}
296 	case UART_CFG_DATA_BITS_9:
297 		return eusartDataBits9;
298 	case UART_CFG_DATA_BITS_8:
299 	default:
300 		if (p == UART_CFG_PARITY_NONE) {
301 			return eusartDataBits8;
302 		} else {
303 			return eusartDataBits9;
304 		}
305 		return eusartDataBits8;
306 	}
307 }
308 
uart_silabs_eusart_ll2cfg_databits(EUSART_Databits_TypeDef db,EUSART_Parity_TypeDef p)309 static inline enum uart_config_data_bits uart_silabs_eusart_ll2cfg_databits(
310 	EUSART_Databits_TypeDef db, EUSART_Parity_TypeDef p)
311 {
312 	switch (db) {
313 	case eusartDataBits7:
314 		if (p == eusartNoParity) {
315 			return UART_CFG_DATA_BITS_7;
316 		} else {
317 			return UART_CFG_DATA_BITS_6;
318 		}
319 	case eusartDataBits9:
320 		if (p == eusartNoParity) {
321 			return UART_CFG_DATA_BITS_9;
322 		} else {
323 			return UART_CFG_DATA_BITS_8;
324 		}
325 	case eusartDataBits8:
326 	default:
327 		if (p == eusartNoParity) {
328 			return UART_CFG_DATA_BITS_8;
329 		} else {
330 			return UART_CFG_DATA_BITS_7;
331 		}
332 	}
333 }
334 
335 /**
336  * @brief  Get LL hardware flow control define from
337  *         Zephyr hardware flow control option.
338  * @note   Supports only UART_CFG_FLOW_CTRL_RTS_CTS and UART_CFG_FLOW_CTRL_RS485.
339  * @param  fc: Zephyr hardware flow control option.
340  * @retval eusartHwFlowControlCtsAndRts, or eusartHwFlowControlNone.
341  */
uart_silabs_eusart_cfg2ll_hwctrl(enum uart_config_flow_control fc)342 static inline EUSART_HwFlowControl_TypeDef uart_silabs_eusart_cfg2ll_hwctrl(
343 	enum uart_config_flow_control fc)
344 {
345 	if (fc == UART_CFG_FLOW_CTRL_RTS_CTS) {
346 		return eusartHwFlowControlCtsAndRts;
347 	}
348 
349 	return eusartHwFlowControlNone;
350 }
351 
352 /**
353  * @brief  Get Zephyr hardware flow control option from
354  *         LL hardware flow control define.
355  * @note   Supports only eusartHwFlowControlCtsAndRts.
356  * @param  fc: LL hardware flow control definition.
357  * @retval UART_CFG_FLOW_CTRL_RTS_CTS, or UART_CFG_FLOW_CTRL_NONE.
358  */
uart_silabs_eusart_ll2cfg_hwctrl(EUSART_HwFlowControl_TypeDef fc)359 static inline enum uart_config_flow_control uart_silabs_eusart_ll2cfg_hwctrl(
360 	EUSART_HwFlowControl_TypeDef fc)
361 {
362 	if (fc == eusartHwFlowControlCtsAndRts) {
363 		return UART_CFG_FLOW_CTRL_RTS_CTS;
364 	}
365 
366 	return UART_CFG_FLOW_CTRL_NONE;
367 }
368 
369 /**
370  * @brief Main initializer for UART
371  *
372  * @param dev UART device to be initialized
373  * @return int 0
374  */
uart_silabs_eusart_init(const struct device * dev)375 static int uart_silabs_eusart_init(const struct device *dev)
376 {
377 	int err;
378 	const struct uart_silabs_eusart_config *config = dev->config;
379 	struct uart_silabs_eusart_data *data = dev->data;
380 	struct uart_config *uart_cfg = &data->uart_cfg;
381 
382 	EUSART_UartInit_TypeDef eusartInit = EUSART_UART_INIT_DEFAULT_HF;
383 	EUSART_AdvancedInit_TypeDef advancedSettings = EUSART_ADVANCED_INIT_DEFAULT;
384 
385 	/* The peripheral and gpio clock are already enabled from soc and gpio
386 	 * driver
387 	 */
388 	/* Enable EUSART clock */
389 	err = clock_control_on(config->clock_dev, (clock_control_subsys_t)&config->clock_cfg);
390 	if (err < 0) {
391 		return err;
392 	}
393 
394 	err = pinctrl_apply_state(config->pcfg, PINCTRL_STATE_DEFAULT);
395 	if (err < 0) {
396 		return err;
397 	}
398 
399 	/* Init EUSART */
400 	eusartInit.baudrate = uart_cfg->baudrate;
401 	eusartInit.parity = uart_silabs_eusart_cfg2ll_parity(uart_cfg->parity);
402 	eusartInit.stopbits = uart_silabs_eusart_cfg2ll_stopbits(uart_cfg->stop_bits);
403 	eusartInit.databits = uart_silabs_eusart_cfg2ll_databits(uart_cfg->data_bits,
404 								 uart_cfg->parity);
405 	advancedSettings.hwFlowControl = uart_silabs_eusart_cfg2ll_hwctrl(uart_cfg->flow_ctrl);
406 	eusartInit.advancedSettings = &advancedSettings;
407 
408 	EUSART_UartInitHf(config->eusart, &eusartInit);
409 
410 #ifdef CONFIG_UART_INTERRUPT_DRIVEN
411 	config->irq_config_func(dev);
412 #endif
413 
414 	return 0;
415 }
416 
417 #ifdef CONFIG_PM_DEVICE
uart_silabs_eusart_pm_action(const struct device * dev,enum pm_device_action action)418 static int uart_silabs_eusart_pm_action(const struct device *dev, enum pm_device_action action)
419 {
420 	__maybe_unused const struct uart_silabs_eusart_config *config = dev->config;
421 
422 	switch (action) {
423 	case PM_DEVICE_ACTION_SUSPEND:
424 #ifdef EUSART_STATUS_TXIDLE
425 		/* Wait for TX FIFO to flush before suspending */
426 		while (!(EUSART_StatusGet(config->eusart) & EUSART_STATUS_TXIDLE)) {
427 		}
428 #endif
429 		break;
430 
431 	case PM_DEVICE_ACTION_RESUME:
432 		break;
433 
434 	default:
435 		return -ENOTSUP;
436 	}
437 
438 	return 0;
439 }
440 #endif
441 
442 static DEVICE_API(uart, uart_silabs_eusart_driver_api) = {
443 	.poll_in = uart_silabs_eusart_poll_in,
444 	.poll_out = uart_silabs_eusart_poll_out,
445 	.err_check = uart_silabs_eusart_err_check,
446 #ifdef CONFIG_UART_INTERRUPT_DRIVEN
447 	.fifo_fill = uart_silabs_eusart_fifo_fill,
448 	.fifo_read = uart_silabs_eusart_fifo_read,
449 	.irq_tx_enable = uart_silabs_eusart_irq_tx_enable,
450 	.irq_tx_disable = uart_silabs_eusart_irq_tx_disable,
451 	.irq_tx_complete = uart_silabs_eusart_irq_tx_complete,
452 	.irq_tx_ready = uart_silabs_eusart_irq_tx_ready,
453 	.irq_rx_enable = uart_silabs_eusart_irq_rx_enable,
454 	.irq_rx_disable = uart_silabs_eusart_irq_rx_disable,
455 	.irq_rx_ready = uart_silabs_eusart_irq_rx_ready,
456 	.irq_err_enable = uart_silabs_eusart_irq_err_enable,
457 	.irq_err_disable = uart_silabs_eusart_irq_err_disable,
458 	.irq_is_pending = uart_silabs_eusart_irq_is_pending,
459 	.irq_update = uart_silabs_eusart_irq_update,
460 	.irq_callback_set = uart_silabs_eusart_irq_callback_set,
461 #endif
462 };
463 
464 #ifdef CONFIG_UART_INTERRUPT_DRIVEN
465 #define UART_IRQ_HANDLER_FUNC(idx)							\
466 	.irq_config_func = uart_silabs_eusart_config_func_##idx,
467 #define UART_IRQ_HANDLER(idx)								\
468 	static void uart_silabs_eusart_config_func_##idx(const struct device *dev)	\
469 	{										\
470 		IRQ_CONNECT(DT_INST_IRQ_BY_NAME(idx, rx, irq),				\
471 			    DT_INST_IRQ_BY_NAME(idx, rx, priority),			\
472 			    uart_silabs_eusart_isr, DEVICE_DT_INST_GET(idx), 0);	\
473 		IRQ_CONNECT(DT_INST_IRQ_BY_NAME(idx, tx, irq),				\
474 			    DT_INST_IRQ_BY_NAME(idx, tx, priority),			\
475 			    uart_silabs_eusart_isr, DEVICE_DT_INST_GET(idx), 0);	\
476 											\
477 		irq_enable(DT_INST_IRQ_BY_NAME(idx, rx, irq));				\
478 		irq_enable(DT_INST_IRQ_BY_NAME(idx, tx, irq));				\
479 	}
480 #else
481 #define UART_IRQ_HANDLER_FUNC(idx)
482 #define UART_IRQ_HANDLER(idx)
483 #endif
484 
485 #define UART_INIT(idx)								\
486 UART_IRQ_HANDLER(idx)								\
487 										\
488 PINCTRL_DT_INST_DEFINE(idx);							\
489 										\
490 static const struct uart_silabs_eusart_config uart_silabs_eusart_cfg_##idx = {	\
491 	.eusart = (EUSART_TypeDef *)DT_INST_REG_ADDR(idx),			\
492 	.pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(idx),				\
493 	.clock_dev = DEVICE_DT_GET(DT_INST_CLOCKS_CTLR(idx)),			\
494 	.clock_cfg = SILABS_DT_INST_CLOCK_CFG(idx),				\
495 	UART_IRQ_HANDLER_FUNC(idx)						\
496 };										\
497 										\
498 static struct uart_silabs_eusart_data uart_silabs_eusart_data_##idx = {		\
499 	.uart_cfg = {								\
500 		.baudrate  = DT_INST_PROP(idx, current_speed),			\
501 		.parity	   = DT_INST_ENUM_IDX(idx, parity),			\
502 		.stop_bits = DT_INST_ENUM_IDX(idx, stop_bits),			\
503 		.data_bits = DT_INST_ENUM_IDX(idx, data_bits),			\
504 		.flow_ctrl = DT_INST_PROP(idx, hw_flow_control)			\
505 					? UART_CFG_FLOW_CTRL_RTS_CTS		\
506 					: UART_CFG_FLOW_CTRL_NONE,		\
507 	},									\
508 };										\
509 										\
510 PM_DEVICE_DT_INST_DEFINE(idx, uart_silabs_eusart_pm_action);			\
511 										\
512 DEVICE_DT_INST_DEFINE(idx, uart_silabs_eusart_init, PM_DEVICE_DT_INST_GET(idx),	\
513 		      &uart_silabs_eusart_data_##idx,				\
514 		      &uart_silabs_eusart_cfg_##idx, PRE_KERNEL_1,		\
515 		      CONFIG_SERIAL_INIT_PRIORITY,				\
516 		      &uart_silabs_eusart_driver_api);
517 
518 DT_INST_FOREACH_STATUS_OKAY(UART_INIT)
519