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