1 /*
2  * Copyright (c) 2025 Microchip Technology Inc.
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 /**
8  * @file uart_mchp_sercom_g1.c
9  * @brief UART driver implementation for Microchip devices.
10  */
11 
12 #include <zephyr/device.h>
13 #include <zephyr/init.h>
14 #include <zephyr/sys/__assert.h>
15 #include <soc.h>
16 #include <zephyr/drivers/uart.h>
17 #include <zephyr/drivers/dma.h>
18 #include <zephyr/drivers/pinctrl.h>
19 #include <zephyr/drivers/clock_control.h>
20 #include <zephyr/irq.h>
21 #include <zephyr/drivers/clock_control/mchp_clock_control.h>
22 #include <string.h>
23 
24 /*******************************************
25  * Const and Macro Defines
26  ******************************************
27  */
28 
29 /* Define compatible string */
30 #define DT_DRV_COMPAT microchip_sercom_g1_uart
31 
32 #define UART_SUCCESS           0
33 #define BITSHIFT_FOR_BAUD_CALC 20
34 
35 /* Do the peripheral clock related configuration */
36 
37 /**
38  * @brief Clock configuration structure for the UART.
39  *
40  * This structure contains the clock configuration parameters for the UART
41  * peripheral.
42  */
43 typedef struct mchp_uart_clock {
44 	/* Clock driver */
45 	const struct device *clock_dev;
46 
47 	/* Main clock subsystem. */
48 	clock_control_subsys_t mclk_sys;
49 
50 	/* Generic clock subsystem. */
51 	clock_control_subsys_t gclk_sys;
52 } mchp_uart_clock_t;
53 
54 #define UART_MCHP_CLOCK_DEFN(n)                                                                    \
55 	.uart_clock.clock_dev = DEVICE_DT_GET(DT_NODELABEL(clock)),                                \
56 	.uart_clock.mclk_sys = (void *)(DT_INST_CLOCKS_CELL_BY_NAME(n, mclk, subsystem)),          \
57 	.uart_clock.gclk_sys = (void *)(DT_INST_CLOCKS_CELL_BY_NAME(n, gclk, subsystem)),
58 
59 /**
60  * @brief UART device constant configuration structure.
61  */
62 typedef struct uart_mchp_dev_cfg {
63 	/* Baud rate for UART communication */
64 	uint32_t baudrate;
65 	uint8_t data_bits;
66 	uint8_t parity;
67 	uint8_t stop_bits;
68 
69 	/* Pointer to the SERCOM registers. */
70 	sercom_registers_t *regs;
71 
72 	/* Flag indicating if the clock is external. */
73 	bool clock_external;
74 
75 	/* RX pinout configuration. */
76 	uint32_t rxpo;
77 
78 	/* TX pinout configuration. */
79 	uint32_t txpo;
80 
81 	/* Clock configuration */
82 	mchp_uart_clock_t uart_clock;
83 
84 	/* Pin control configuration */
85 	const struct pinctrl_dev_config *pcfg;
86 
87 } uart_mchp_dev_cfg_t;
88 
89 /**
90  * @brief UART device runtime data structure.
91  */
92 typedef struct uart_mchp_dev_data {
93 	/* Cached UART configuration */
94 	struct uart_config config_cache;
95 } uart_mchp_dev_data_t;
96 
97 /**
98  * @brief Wait for synchronization of the UART.
99  *
100  * @param regs Pointer to the sercom_registers_t structure.
101  * @param clock_external Boolean to check external or internal clock
102  */
uart_wait_sync(sercom_registers_t * regs,bool clock_external)103 static void uart_wait_sync(sercom_registers_t *regs, bool clock_external)
104 {
105 	if (clock_external == false) {
106 		while ((regs->USART_INT.SERCOM_SYNCBUSY & SERCOM_USART_INT_SYNCBUSY_Msk) != 0) {
107 		}
108 	} else {
109 		while ((regs->USART_EXT.SERCOM_SYNCBUSY & SERCOM_USART_EXT_SYNCBUSY_Msk) != 0) {
110 		}
111 	}
112 }
113 
114 /**
115  * @brief Disable UART interrupts.
116  *
117  * @param regs Pointer to the sercom_registers_t structure.
118  * @param clock_external Boolean to check external or internal clock
119  */
uart_disable_interrupts(sercom_registers_t * regs,bool clock_external)120 static inline void uart_disable_interrupts(sercom_registers_t *regs, bool clock_external)
121 {
122 	if (clock_external == false) {
123 		regs->USART_INT.SERCOM_INTENCLR = SERCOM_USART_INT_INTENCLR_Msk;
124 	} else {
125 		regs->USART_EXT.SERCOM_INTENCLR = SERCOM_USART_EXT_INTENCLR_Msk;
126 	}
127 }
128 
129 /**
130  * @brief Configure the number of data bits for the UART.
131  *
132  * @param regs Pointer to the sercom_registers_t structure.
133  * @param clock_external Boolean to check external or internal clock
134  * @param count Number of data bits (5 to 9).
135  * @return 0 on success, -1 on invalid count.
136  */
uart_config_data_bits(sercom_registers_t * regs,bool clock_external,unsigned int count)137 static int uart_config_data_bits(sercom_registers_t *regs, bool clock_external, unsigned int count)
138 {
139 	uint32_t value;
140 	int retval = UART_SUCCESS;
141 
142 	do {
143 		if (clock_external == false) {
144 			switch (count) {
145 			case UART_CFG_DATA_BITS_5:
146 				value = SERCOM_USART_INT_CTRLB_CHSIZE_5_BIT;
147 				break;
148 			case UART_CFG_DATA_BITS_6:
149 				value = SERCOM_USART_INT_CTRLB_CHSIZE_6_BIT;
150 				break;
151 			case UART_CFG_DATA_BITS_7:
152 				value = SERCOM_USART_INT_CTRLB_CHSIZE_7_BIT;
153 				break;
154 			case UART_CFG_DATA_BITS_8:
155 				value = SERCOM_USART_INT_CTRLB_CHSIZE_8_BIT;
156 				break;
157 			case UART_CFG_DATA_BITS_9:
158 				value = SERCOM_USART_INT_CTRLB_CHSIZE_9_BIT;
159 				break;
160 			default:
161 				retval = -ENOTSUP;
162 			}
163 		} else {
164 			switch (count) {
165 			case UART_CFG_DATA_BITS_5:
166 				value = SERCOM_USART_EXT_CTRLB_CHSIZE_5_BIT;
167 				break;
168 			case UART_CFG_DATA_BITS_6:
169 				value = SERCOM_USART_EXT_CTRLB_CHSIZE_6_BIT;
170 				break;
171 			case UART_CFG_DATA_BITS_7:
172 				value = SERCOM_USART_EXT_CTRLB_CHSIZE_7_BIT;
173 				break;
174 			case UART_CFG_DATA_BITS_8:
175 				value = SERCOM_USART_EXT_CTRLB_CHSIZE_8_BIT;
176 				break;
177 			case UART_CFG_DATA_BITS_9:
178 				value = SERCOM_USART_EXT_CTRLB_CHSIZE_9_BIT;
179 				break;
180 			default:
181 				retval = -ENOTSUP;
182 			}
183 		}
184 		if (retval != UART_SUCCESS) {
185 			break;
186 		}
187 
188 		/* Writing to the CTRLB register requires synchronization */
189 		if (clock_external == false) {
190 			regs->USART_INT.SERCOM_CTRLB &= ~SERCOM_USART_INT_CTRLB_CHSIZE_Msk;
191 			regs->USART_INT.SERCOM_CTRLB |= value;
192 		} else {
193 			regs->USART_EXT.SERCOM_CTRLB &= ~SERCOM_USART_EXT_CTRLB_CHSIZE_Msk;
194 			regs->USART_EXT.SERCOM_CTRLB |= value;
195 		}
196 		uart_wait_sync(regs, clock_external);
197 	} while (0);
198 
199 	return retval;
200 }
201 
202 /**
203  * @brief Configure the parity mode for the UART.
204  *
205  * @param regs Pointer to the sercom_registers_t structure.
206  * @param clock_external Boolean to check external or internal clock
207  * @param parity Parity mode to configure.
208  */
uart_config_parity(sercom_registers_t * regs,bool clock_external,enum uart_config_parity parity)209 static void uart_config_parity(sercom_registers_t *regs, bool clock_external,
210 			       enum uart_config_parity parity)
211 {
212 	if (clock_external == false) {
213 		regs->USART_INT.SERCOM_CTRLA &= ~SERCOM_USART_INT_CTRLA_FORM_Msk;
214 		switch (parity) {
215 		case UART_CFG_PARITY_ODD: {
216 			regs->USART_INT.SERCOM_CTRLA |=
217 				SERCOM_USART_INT_CTRLA_FORM_USART_FRAME_WITH_PARITY;
218 
219 			/* Writing to the CTRLB register requires synchronization */
220 			regs->USART_INT.SERCOM_CTRLB |= SERCOM_USART_INT_CTRLB_PMODE_Msk;
221 			uart_wait_sync(regs, clock_external);
222 			break;
223 		}
224 		case UART_CFG_PARITY_EVEN: {
225 			regs->USART_INT.SERCOM_CTRLA |=
226 				SERCOM_USART_INT_CTRLA_FORM_USART_FRAME_WITH_PARITY;
227 
228 			/* Writing to the CTRLB register requires synchronization */
229 			regs->USART_INT.SERCOM_CTRLB &= ~SERCOM_USART_INT_CTRLB_PMODE_Msk;
230 			uart_wait_sync(regs, clock_external);
231 			break;
232 		}
233 		default: {
234 			regs->USART_INT.SERCOM_CTRLA |=
235 				SERCOM_USART_INT_CTRLA_FORM_USART_FRAME_NO_PARITY;
236 			break;
237 		}
238 		}
239 	} else {
240 		regs->USART_EXT.SERCOM_CTRLA &= ~SERCOM_USART_EXT_CTRLA_FORM_Msk;
241 		switch (parity) {
242 		case UART_CFG_PARITY_ODD: {
243 			regs->USART_EXT.SERCOM_CTRLA |=
244 				SERCOM_USART_EXT_CTRLA_FORM_USART_FRAME_WITH_PARITY;
245 
246 			/* Writing to the CTRLB register requires synchronization */
247 			regs->USART_EXT.SERCOM_CTRLB |= SERCOM_USART_EXT_CTRLB_PMODE_Msk;
248 			uart_wait_sync(regs, clock_external);
249 			break;
250 		}
251 		case UART_CFG_PARITY_EVEN: {
252 			regs->USART_EXT.SERCOM_CTRLA |=
253 				SERCOM_USART_EXT_CTRLA_FORM_USART_FRAME_WITH_PARITY;
254 
255 			/* Writing to the CTRLB register requires synchronization */
256 			regs->USART_EXT.SERCOM_CTRLB &= ~SERCOM_USART_EXT_CTRLB_PMODE_Msk;
257 			uart_wait_sync(regs, clock_external);
258 			break;
259 		}
260 		default: {
261 			regs->USART_EXT.SERCOM_CTRLA |=
262 				SERCOM_USART_EXT_CTRLA_FORM_USART_FRAME_NO_PARITY;
263 			break;
264 		}
265 		}
266 	}
267 }
268 
269 /**
270  * @brief Configure the number of stop bits for the UART.
271  *
272  * @param regs Pointer to the sercom_registers_t structure.
273  * @param clock_external Boolean to check external or internal clock
274  * @param count Number of stop bits (1 or 2).
275  * @return 0 on success, -1 on invalid count.
276  */
uart_config_stop_bits(sercom_registers_t * regs,bool clock_external,unsigned int count)277 static int uart_config_stop_bits(sercom_registers_t *regs, bool clock_external, unsigned int count)
278 {
279 	int retval = UART_SUCCESS;
280 
281 	do {
282 		if (clock_external == false) {
283 			if (count == UART_CFG_STOP_BITS_1) {
284 				regs->USART_INT.SERCOM_CTRLB &= ~SERCOM_USART_INT_CTRLB_SBMODE_Msk;
285 			} else if (count == UART_CFG_STOP_BITS_2) {
286 				regs->USART_INT.SERCOM_CTRLB |= SERCOM_USART_INT_CTRLB_SBMODE_Msk;
287 			} else {
288 				retval = -ENOTSUP;
289 				break;
290 			}
291 		} else {
292 			if (count == UART_CFG_STOP_BITS_1) {
293 				regs->USART_EXT.SERCOM_CTRLB &= ~SERCOM_USART_EXT_CTRLB_SBMODE_Msk;
294 			} else if (count == UART_CFG_STOP_BITS_2) {
295 				regs->USART_EXT.SERCOM_CTRLB |= SERCOM_USART_EXT_CTRLB_SBMODE_Msk;
296 			} else {
297 				retval = -ENOTSUP;
298 				break;
299 			}
300 		}
301 		uart_wait_sync(regs, clock_external);
302 	} while (0);
303 
304 	return retval;
305 }
306 
307 /**
308  * @brief Configure the UART pinout.
309  *
310  * @param regs Pointer to the sercom_registers_t structure.
311  * @param clock_external Boolean to check external or internal clock
312  */
uart_config_pinout(const uart_mchp_dev_cfg_t * const cfg)313 static void uart_config_pinout(const uart_mchp_dev_cfg_t *const cfg)
314 {
315 	uint32_t reg_value;
316 
317 	sercom_registers_t *regs = cfg->regs;
318 	uint32_t rxpo = cfg->rxpo;
319 	uint32_t txpo = cfg->txpo;
320 
321 	if (cfg->clock_external == false) {
322 		reg_value = regs->USART_INT.SERCOM_CTRLA;
323 		reg_value &= ~(SERCOM_USART_INT_CTRLA_RXPO_Msk | SERCOM_USART_INT_CTRLA_TXPO_Msk);
324 		reg_value |=
325 			(SERCOM_USART_INT_CTRLA_RXPO(rxpo) | SERCOM_USART_INT_CTRLA_TXPO(txpo));
326 		cfg->regs->USART_INT.SERCOM_CTRLA = reg_value;
327 	} else {
328 		reg_value = regs->USART_EXT.SERCOM_CTRLA;
329 		reg_value &= ~(SERCOM_USART_EXT_CTRLA_RXPO_Msk | SERCOM_USART_EXT_CTRLA_TXPO_Msk);
330 		reg_value |=
331 			(SERCOM_USART_EXT_CTRLA_RXPO(rxpo) | SERCOM_USART_EXT_CTRLA_TXPO(txpo));
332 		regs->USART_EXT.SERCOM_CTRLA = reg_value;
333 	}
334 }
335 
336 /**
337  * @brief Set clock polarity for UART.
338  *
339  * @param regs Pointer to the sercom_registers_t structure.
340  * @param clock_external Boolean to check external or internal clock
341  * @param tx_rising transmit on rising edge
342  */
uart_set_clock_polarity(sercom_registers_t * regs,bool clock_external,bool tx_rising)343 static void uart_set_clock_polarity(sercom_registers_t *regs, bool clock_external, bool tx_rising)
344 {
345 	if (clock_external == false) {
346 		if (tx_rising == true) {
347 			regs->USART_INT.SERCOM_CTRLA &= ~SERCOM_USART_INT_CTRLA_CPOL_Msk;
348 		} else {
349 			regs->USART_INT.SERCOM_CTRLA |= SERCOM_USART_INT_CTRLA_CPOL_Msk;
350 		}
351 	} else {
352 		if (tx_rising == true) {
353 			regs->USART_EXT.SERCOM_CTRLA &= ~SERCOM_USART_EXT_CTRLA_CPOL_Msk;
354 		} else {
355 			regs->USART_EXT.SERCOM_CTRLA |= SERCOM_USART_EXT_CTRLA_CPOL_Msk;
356 		}
357 	}
358 }
359 
360 /**
361  * @brief Set the clock source for the UART.
362  *
363  * @param regs Pointer to the sercom_registers_t structure.
364  * @param clock_external Boolean to check external or internal clock
365  */
uart_set_clock_source(sercom_registers_t * regs,bool clock_external)366 static void uart_set_clock_source(sercom_registers_t *regs, bool clock_external)
367 {
368 	uint32_t reg_value;
369 
370 	reg_value = regs->USART_INT.SERCOM_CTRLA;
371 	reg_value &= ~SERCOM_USART_INT_CTRLA_MODE_Msk;
372 
373 	if (clock_external == true) {
374 		regs->USART_INT.SERCOM_CTRLA =
375 			reg_value | SERCOM_USART_INT_CTRLA_MODE_USART_EXT_CLK;
376 	} else {
377 		regs->USART_INT.SERCOM_CTRLA =
378 			reg_value | SERCOM_USART_INT_CTRLA_MODE_USART_INT_CLK;
379 	}
380 }
381 
382 /**
383  * @brief Set the data order for the UART.
384  *
385  * @param regs Pointer to the sercom_registers_t structure.
386  * @param clock_external Boolean to check external or internal clock
387  * @param lsb_first Boolean to set the data order.
388  */
uart_set_lsb_first(sercom_registers_t * regs,bool clock_external,bool lsb_first)389 static void uart_set_lsb_first(sercom_registers_t *regs, bool clock_external, bool lsb_first)
390 {
391 	if (clock_external == false) {
392 		if (lsb_first == true) {
393 			regs->USART_INT.SERCOM_CTRLA |= SERCOM_USART_INT_CTRLA_DORD_Msk;
394 		} else {
395 			regs->USART_INT.SERCOM_CTRLA &= ~SERCOM_USART_INT_CTRLA_DORD_Msk;
396 		}
397 	} else {
398 		if (lsb_first == true) {
399 			regs->USART_EXT.SERCOM_CTRLA |= SERCOM_USART_EXT_CTRLA_DORD_Msk;
400 		} else {
401 			regs->USART_EXT.SERCOM_CTRLA &= ~SERCOM_USART_EXT_CTRLA_DORD_Msk;
402 		}
403 	}
404 }
405 
406 /**
407  * @brief Enable or disable the UART receiver.
408  *
409  * @param regs Pointer to the sercom_registers_t structure.
410  * @param clock_external Boolean to check external or internal clock
411  * @param enable Boolean to enable or disable the receiver.
412  */
uart_rx_on_off(sercom_registers_t * regs,bool clock_external,bool enable)413 static void uart_rx_on_off(sercom_registers_t *regs, bool clock_external, bool enable)
414 {
415 	if (clock_external == false) {
416 		if (enable == true) {
417 			regs->USART_INT.SERCOM_CTRLB |= SERCOM_USART_INT_CTRLB_RXEN_Msk;
418 		} else {
419 			regs->USART_INT.SERCOM_CTRLB &= ~SERCOM_USART_INT_CTRLB_RXEN_Msk;
420 		}
421 	} else {
422 		if (enable == true) {
423 			regs->USART_EXT.SERCOM_CTRLB |= SERCOM_USART_EXT_CTRLB_RXEN_Msk;
424 		} else {
425 			regs->USART_EXT.SERCOM_CTRLB &= ~SERCOM_USART_EXT_CTRLB_RXEN_Msk;
426 		}
427 	}
428 
429 	/* Writing to the CTRLB register requires synchronization */
430 	uart_wait_sync(regs, clock_external);
431 }
432 
433 /**
434  * @brief Enable or disable the UART transmitter.
435  *
436  * @param regs Pointer to the sercom_registers_t structure.
437  * @param clock_external Boolean to check external or internal clock
438  * @param enable Boolean to enable or disable the transmitter.
439  */
uart_tx_on_off(sercom_registers_t * regs,bool clock_external,bool enable)440 static void uart_tx_on_off(sercom_registers_t *regs, bool clock_external, bool enable)
441 {
442 	if (clock_external == false) {
443 		if (enable == true) {
444 			regs->USART_INT.SERCOM_CTRLB |= SERCOM_USART_INT_CTRLB_TXEN_Msk;
445 		} else {
446 			regs->USART_INT.SERCOM_CTRLB &= ~SERCOM_USART_INT_CTRLB_TXEN_Msk;
447 		}
448 	} else {
449 		if (enable == true) {
450 			regs->USART_EXT.SERCOM_CTRLB |= SERCOM_USART_EXT_CTRLB_TXEN_Msk;
451 		} else {
452 			regs->USART_EXT.SERCOM_CTRLB &= ~SERCOM_USART_EXT_CTRLB_TXEN_Msk;
453 		}
454 	}
455 
456 	/* Writing to the CTRLB register requires synchronization */
457 	uart_wait_sync(regs, clock_external);
458 }
459 
460 /**
461  * @brief Set the UART baud rate.
462  *
463  * This function sets the baud rate for the specified UART instance.
464  *
465  * @param regs Pointer to the sercom_registers_t structure.
466  * @param clock_external Boolean to check external or internal clock
467  * @param baudrate Desired baud rate.
468  * @param clk_freq_hz Clock frequency in Hz.
469  * @return 0 on success, -ERANGE if the calculated baud rate is out of range.
470  */
uart_set_baudrate(sercom_registers_t * regs,bool clock_external,uint32_t baudrate,uint32_t clk_freq_hz)471 static int uart_set_baudrate(sercom_registers_t *regs, bool clock_external, uint32_t baudrate,
472 			     uint32_t clk_freq_hz)
473 {
474 	uint64_t tmp;
475 	uint16_t baud;
476 
477 	int retval = UART_SUCCESS;
478 
479 	do {
480 		if (clk_freq_hz == 0) {
481 			retval = -EINVAL;
482 			break;
483 		}
484 		tmp = (uint64_t)baudrate << BITSHIFT_FOR_BAUD_CALC;
485 		tmp = (tmp + (clk_freq_hz >> 1)) / clk_freq_hz;
486 
487 		/* Verify that the calculated result is within range */
488 		if ((tmp < 1) || (tmp > UINT16_MAX)) {
489 			retval = -ERANGE;
490 			break;
491 		}
492 
493 		baud = (UINT16_MAX + 1) - (uint16_t)tmp;
494 
495 		if (clock_external == false) {
496 			regs->USART_INT.SERCOM_CTRLA &= ~SERCOM_USART_INT_CTRLA_SAMPR_Msk;
497 			regs->USART_INT.SERCOM_BAUD = baud;
498 		} else {
499 			regs->USART_EXT.SERCOM_CTRLA &= ~SERCOM_USART_EXT_CTRLA_SAMPR_Msk;
500 			regs->USART_EXT.SERCOM_BAUD = baud;
501 		}
502 	} while (0);
503 
504 	return retval;
505 }
506 
507 /**
508  * @brief Enable or disable the UART.
509  *
510  * @param regs Pointer to the sercom_registers_t structure.
511  * @param clock_external Boolean to check external or internal clock
512  * @param enable Boolean to enable or disable the UART.
513  */
uart_enable(sercom_registers_t * regs,bool clock_external,bool enable)514 static void uart_enable(sercom_registers_t *regs, bool clock_external, bool enable)
515 {
516 	if (clock_external == false) {
517 		if (enable == true) {
518 			regs->USART_INT.SERCOM_CTRLA |= SERCOM_USART_INT_CTRLA_RUNSTDBY_Msk;
519 			regs->USART_INT.SERCOM_CTRLA |= SERCOM_USART_INT_CTRLA_ENABLE_Msk;
520 		} else {
521 			regs->USART_INT.SERCOM_CTRLA &= ~SERCOM_USART_INT_CTRLA_ENABLE_Msk;
522 		}
523 	} else {
524 		if (enable == true) {
525 			regs->USART_EXT.SERCOM_CTRLA |= SERCOM_USART_EXT_CTRLA_RUNSTDBY_Msk;
526 			regs->USART_EXT.SERCOM_CTRLA |= SERCOM_USART_EXT_CTRLA_ENABLE_Msk;
527 		} else {
528 			regs->USART_EXT.SERCOM_CTRLA &= ~SERCOM_USART_EXT_CTRLA_ENABLE_Msk;
529 		}
530 	}
531 
532 	/* Enabling and disabling the SERCOM (CTRLA.ENABLE) requires synchronization */
533 	uart_wait_sync(regs, clock_external);
534 }
535 
536 /**
537  * @brief Check if the UART receive is complete.
538  *
539  * This function checks if the receive operation is complete for the specified UART instance.
540  *
541  * @param regs Pointer to the sercom_registers_t structure.
542  * @param clock_external Boolean to check external or internal clock
543  * @return True if receive is complete, false otherwise.
544  */
uart_is_rx_complete(sercom_registers_t * regs,bool clock_external)545 static inline bool uart_is_rx_complete(sercom_registers_t *regs, bool clock_external)
546 {
547 	bool retval;
548 
549 	if (clock_external == false) {
550 		retval = ((regs->USART_INT.SERCOM_INTFLAG & SERCOM_USART_INT_INTFLAG_RXC_Msk) != 0);
551 	} else {
552 		retval = ((regs->USART_EXT.SERCOM_INTFLAG & SERCOM_USART_EXT_INTFLAG_RXC_Msk) != 0);
553 	}
554 
555 	return retval;
556 }
557 
558 /**
559  * @brief Get the received character from the UART.
560  *
561  * This function retrieves the received character from the specified UART instance.
562  *
563  * @param regs Pointer to the sercom_registers_t structure.
564  * @param clock_external Boolean to check external or internal clock
565  * @return The received character.
566  */
uart_get_received_char(sercom_registers_t * regs,bool clock_external)567 static inline unsigned char uart_get_received_char(sercom_registers_t *regs, bool clock_external)
568 {
569 	unsigned char retval;
570 
571 	if (clock_external == false) {
572 		retval = (unsigned char)regs->USART_INT.SERCOM_DATA;
573 	} else {
574 		retval = (unsigned char)regs->USART_EXT.SERCOM_DATA;
575 	}
576 
577 	return retval;
578 }
579 
580 /**
581  * @brief Check if the UART TX is ready.
582  *
583  * This function checks if the TX operation is ready for the specified UART instance.
584  *
585  * @param regs Pointer to the sercom_registers_t structure.
586  * @param clock_external Boolean to check external or internal clock
587  * @return True if TX is ready, false otherwise.
588  */
uart_is_tx_ready(sercom_registers_t * regs,bool clock_external)589 static inline bool uart_is_tx_ready(sercom_registers_t *regs, bool clock_external)
590 {
591 	bool retval;
592 
593 	if (clock_external == false) {
594 		retval = ((regs->USART_INT.SERCOM_INTFLAG & SERCOM_USART_INT_INTFLAG_DRE_Msk) != 0);
595 	} else {
596 		retval = ((regs->USART_EXT.SERCOM_INTFLAG & SERCOM_USART_EXT_INTFLAG_DRE_Msk) != 0);
597 	}
598 
599 	return retval;
600 }
601 
602 /**
603  * @brief Transmit a character via UART.
604  *
605  * This function transmits a character via the specified UART instance.
606  *
607  * @param regs Pointer to the sercom_registers_t structure.
608  * @param clock_external Boolean to check external or internal clock
609  * @param data The character to transmit.
610  */
uart_tx_char(sercom_registers_t * regs,bool clock_external,unsigned char data)611 static inline void uart_tx_char(sercom_registers_t *regs, bool clock_external, unsigned char data)
612 {
613 	if (clock_external == false) {
614 		regs->USART_INT.SERCOM_DATA = data;
615 	} else {
616 		regs->USART_EXT.SERCOM_DATA = data;
617 	}
618 }
619 
620 /**
621  * @brief Initialize the UART device.
622  *
623  * @param dev Device structure.
624  * @return 0 on success, negative error code on failure.
625  */
uart_mchp_init(const struct device * dev)626 static int uart_mchp_init(const struct device *dev)
627 {
628 	const uart_mchp_dev_cfg_t *const cfg = dev->config;
629 	uart_mchp_dev_data_t *const dev_data = dev->data;
630 	sercom_registers_t *regs = cfg->regs;
631 	bool clock_external = cfg->clock_external;
632 	int retval = UART_SUCCESS;
633 
634 	do {
635 		/* Enable the GCLK and MCLK*/
636 		retval = clock_control_on(cfg->uart_clock.clock_dev, cfg->uart_clock.gclk_sys);
637 		if ((retval != UART_SUCCESS) && (retval != -EALREADY)) {
638 			break;
639 		}
640 		retval = clock_control_on(cfg->uart_clock.clock_dev, cfg->uart_clock.mclk_sys);
641 		if ((retval != UART_SUCCESS) && (retval != -EALREADY)) {
642 			break;
643 		}
644 
645 		uart_disable_interrupts(regs, clock_external);
646 
647 		dev_data->config_cache.flow_ctrl = UART_CFG_FLOW_CTRL_NONE;
648 
649 		retval = uart_config_data_bits(regs, clock_external, cfg->data_bits);
650 		if (retval != UART_SUCCESS) {
651 			break;
652 		}
653 		dev_data->config_cache.data_bits = cfg->data_bits;
654 
655 		uart_config_parity(regs, clock_external, cfg->parity);
656 		dev_data->config_cache.parity = cfg->parity;
657 
658 		retval = uart_config_stop_bits(regs, clock_external, cfg->stop_bits);
659 		if (retval != UART_SUCCESS) {
660 			break;
661 		}
662 		dev_data->config_cache.stop_bits = cfg->stop_bits;
663 
664 		uart_config_pinout(cfg);
665 		uart_set_clock_polarity(regs, clock_external, false);
666 		uart_set_clock_source(regs, clock_external);
667 		uart_set_lsb_first(regs, clock_external, true);
668 
669 		/* Enable PINMUX based on PINCTRL */
670 		retval = pinctrl_apply_state(cfg->pcfg, PINCTRL_STATE_DEFAULT);
671 		if (retval != UART_SUCCESS) {
672 			break;
673 		}
674 
675 		/* Enable receiver and transmitter */
676 		uart_rx_on_off(regs, clock_external, true);
677 		uart_tx_on_off(regs, clock_external, true);
678 
679 		uint32_t clock_rate;
680 
681 		clock_control_get_rate(cfg->uart_clock.clock_dev, cfg->uart_clock.gclk_sys,
682 				       &clock_rate);
683 
684 		retval = uart_set_baudrate(regs, clock_external, cfg->baudrate, clock_rate);
685 		if (retval != UART_SUCCESS) {
686 			break;
687 		}
688 		dev_data->config_cache.baudrate = cfg->baudrate;
689 
690 		uart_enable(regs, clock_external, true);
691 	} while (0);
692 
693 	return retval;
694 }
695 
696 /**
697  * @brief Poll the UART device for input.
698  *
699  * @param dev Device structure.
700  * @param data Pointer to store the received data.
701  * @return 0 on success, negative error code on failure.
702  */
uart_mchp_poll_in(const struct device * dev,unsigned char * data)703 static int uart_mchp_poll_in(const struct device *dev, unsigned char *data)
704 {
705 	const uart_mchp_dev_cfg_t *const cfg = dev->config;
706 	sercom_registers_t *regs = cfg->regs;
707 	bool clock_external = cfg->clock_external;
708 	int retval = UART_SUCCESS;
709 
710 	if (uart_is_rx_complete(regs, clock_external) == false) {
711 		retval = -EBUSY;
712 	} else {
713 		*data = uart_get_received_char(regs, clock_external);
714 	}
715 
716 	return retval;
717 }
718 
719 /**
720  * @brief Output a character via UART.
721  *
722  * @param dev Device structure.
723  * @param data Character to send.
724  */
uart_mchp_poll_out(const struct device * dev,unsigned char data)725 static void uart_mchp_poll_out(const struct device *dev, unsigned char data)
726 {
727 	const uart_mchp_dev_cfg_t *const cfg = dev->config;
728 	sercom_registers_t *regs = cfg->regs;
729 	bool clock_external = cfg->clock_external;
730 
731 	while (uart_is_tx_ready(regs, clock_external) == false) {
732 	}
733 
734 	/* send a character */
735 	uart_tx_char(regs, clock_external, data);
736 }
737 
738 static DEVICE_API(uart, uart_mchp_driver_api) = {
739 	.poll_in = uart_mchp_poll_in,
740 	.poll_out = uart_mchp_poll_out,
741 };
742 
743 #define UART_MCHP_CONFIG_DEFN(n)                                                                   \
744 	static const uart_mchp_dev_cfg_t uart_mchp_config_##n = {                                  \
745 		.baudrate = DT_INST_PROP(n, current_speed),                                        \
746 		.data_bits = DT_INST_ENUM_IDX_OR(n, data_bits, UART_CFG_DATA_BITS_8),              \
747 		.parity = DT_INST_ENUM_IDX_OR(n, parity, UART_CFG_PARITY_NONE),                    \
748 		.stop_bits = DT_INST_ENUM_IDX_OR(n, stop_bits, UART_CFG_STOP_BITS_1),              \
749 		.pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(n),                                         \
750 		.regs = (sercom_registers_t *)DT_INST_REG_ADDR(n),                                 \
751 		.rxpo = (DT_INST_PROP(n, rxpo)),                                                   \
752 		.txpo = (DT_INST_PROP(n, txpo)),                                                   \
753 		.clock_external = DT_INST_PROP(n, clock_external),                                 \
754 		UART_MCHP_CLOCK_DEFN(n)}
755 
756 #define UART_MCHP_DEVICE_INIT(n)                                                                   \
757 	PINCTRL_DT_INST_DEFINE(n);                                                                 \
758 	UART_MCHP_CONFIG_DEFN(n);                                                                  \
759 	static uart_mchp_dev_data_t uart_mchp_data_##n;                                            \
760 	DEVICE_DT_INST_DEFINE(n, uart_mchp_init, NULL, &uart_mchp_data_##n, &uart_mchp_config_##n, \
761 			      PRE_KERNEL_1, CONFIG_SERIAL_INIT_PRIORITY, &uart_mchp_driver_api)
762 
763 DT_INST_FOREACH_STATUS_OKAY(UART_MCHP_DEVICE_INIT)
764