1 /***************************************************************************//**
2  * @file
3  * @brief Low Energy Universal Asynchronous Receiver/Transmitter (LEUART)
4  *   Peripheral API
5  *******************************************************************************
6  * # License
7  * <b>Copyright 2018 Silicon Laboratories Inc. www.silabs.com</b>
8  *******************************************************************************
9  *
10  * SPDX-License-Identifier: Zlib
11  *
12  * The licensor of this software is Silicon Laboratories Inc.
13  *
14  * This software is provided 'as-is', without any express or implied
15  * warranty. In no event will the authors be held liable for any damages
16  * arising from the use of this software.
17  *
18  * Permission is granted to anyone to use this software for any purpose,
19  * including commercial applications, and to alter it and redistribute it
20  * freely, subject to the following restrictions:
21  *
22  * 1. The origin of this software must not be misrepresented; you must not
23  *    claim that you wrote the original software. If you use this software
24  *    in a product, an acknowledgment in the product documentation would be
25  *    appreciated but is not required.
26  * 2. Altered source versions must be plainly marked as such, and must not be
27  *    misrepresented as being the original software.
28  * 3. This notice may not be removed or altered from any source distribution.
29  *
30  ******************************************************************************/
31 
32 #include "em_leuart.h"
33 #if defined(LEUART_COUNT) && (LEUART_COUNT > 0)
34 
35 #include "em_cmu.h"
36 #include "sl_assert.h"
37 
38 /***************************************************************************//**
39  * @addtogroup leuart LEUART - Low Energy UART
40  * @brief Low Energy Universal Asynchronous Receiver/Transmitter (LEUART)
41  *        Peripheral API
42  * @details
43  *  This module contains functions to control the LEUART peripheral of Silicon
44  *  Labs 32-bit MCUs and SoCs. The LEUART module provides the full UART communication using
45  *  a low frequency 32.768 kHz clock and has special features for communication
46  *  without the CPU intervention.
47  * @{
48  ******************************************************************************/
49 
50 /*******************************************************************************
51  *******************************   DEFINES   ***********************************
52  ******************************************************************************/
53 
54 /** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */
55 
56 /** A validation of the LEUART register block pointer reference
57  *  for assert statements. */
58 #if (LEUART_COUNT == 1)
59 #define LEUART_REF_VALID(ref)    ((ref) == LEUART0)
60 #elif (LEUART_COUNT == 2)
61 #define LEUART_REF_VALID(ref)    (((ref) == LEUART0) || ((ref) == LEUART1))
62 #else
63 #error "Undefined number of low energy UARTs (LEUART)."
64 #endif
65 
66 /** @endcond */
67 
68 /*******************************************************************************
69  **************************   LOCAL FUNCTIONS   ********************************
70  ******************************************************************************/
71 
72 /** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */
73 
74 /***************************************************************************//**
75  * @brief
76  *   Wait for ongoing sync of register(s) to the low-frequency domain to complete.
77  *
78  * @param[in] leuart
79  *   A pointer to the LEUART peripheral register block.
80  *
81  * @param[in] mask
82  *   A bitmask corresponding to SYNCBUSY register defined bits, indicating
83  *   registers that must complete any ongoing synchronization.
84  ******************************************************************************/
LEUART_Sync(LEUART_TypeDef * leuart,uint32_t mask)85 __STATIC_INLINE void LEUART_Sync(LEUART_TypeDef *leuart, uint32_t mask)
86 {
87   /* Avoid deadlock if modifying the same register twice when freeze mode is */
88   /* activated. */
89   if (leuart->FREEZE & LEUART_FREEZE_REGFREEZE) {
90     return;
91   }
92 
93   /* Wait for any pending previous write operation to have been completed */
94   /* in the low-frequency domai. */
95   while ((leuart->SYNCBUSY & mask) != 0U) {
96   }
97 }
98 
99 /** @endcond */
100 
101 /*******************************************************************************
102  **************************   GLOBAL FUNCTIONS   *******************************
103  ******************************************************************************/
104 
105 /***************************************************************************//**
106  * @brief
107  *   Calculate the baudrate for the LEUART given reference frequency and clock division.
108  *
109  * @details
110  *   This function returns the baudrate that a LEUART module will use if
111  *   configured with the given frequency and clock divisor. Notice that
112  *   this function will not use the hardware configuration. It can be used
113  *   to determine if a given configuration is sufficiently accurate for the
114  *   application.
115  *
116  * @param[in] refFreq
117  *   The LEUART peripheral frequency used.
118  *
119  * @param[in] clkdiv
120  *   The clock division factor to be used.
121  *
122  * @return
123  *   A baudrate with given settings.
124  ******************************************************************************/
LEUART_BaudrateCalc(uint32_t refFreq,uint32_t clkdiv)125 uint32_t LEUART_BaudrateCalc(uint32_t refFreq, uint32_t clkdiv)
126 {
127   uint32_t divisor;
128   uint32_t remainder;
129   uint32_t quotient;
130   uint32_t br;
131 
132   /* Mask out unused bits. */
133   clkdiv &= _LEUART_CLKDIV_MASK;
134 
135   /* Use integer division to avoid forcing in float division */
136   /* utils, and yet keep rounding effect errors to a minimum. */
137 
138   /*
139    * Baudrate is given by:
140    *
141    * br = fLEUARTn/(1 + (CLKDIV / 256))
142    *
143    * which can be rewritten to
144    *
145    * br = (256 * fLEUARTn)/(256 + CLKDIV)
146    *
147    * Normally, with fLEUARTn appr 32768 Hz, there is no problem with overflow
148    * if using 32 bit arithmetic. However, since fLEUARTn may be derived from
149    * HFCORECLK, consider the overflow when using integer arithmetic.
150    */
151 
152   /*
153    * The basic problem with integer division in the above formula is that
154    * the dividend (256 * fLEUARTn) may become higher than the maximum 32 bit
155    * integer. Yet we want to evaluate the dividend first before dividing
156    * to get as small rounding effects as possible.
157    * Also, harsh restrictions should be avoided on the maximum fLEUARTn value.
158    *
159    * For division a/b:
160    *
161    * a = qb + r
162    *
163    * where q is the quotient and r is the remainder, both integers.
164    *
165    * The orignal baudrate formula can be rewritten as:
166    *
167    * br = 256a / b = 256(qb + r)/b = 256q + 256r/b
168    *
169    * where a is 'refFreq' and b is 'divisor', referring to variable names.
170    */
171 
172   divisor   = 256 + clkdiv;
173   quotient  = refFreq / divisor;
174   remainder = refFreq % divisor;
175 
176   /* Since the divisor >= 256, the below cannot exceed the maximum 32 bit value. */
177   br = 256 * quotient;
178 
179   /*
180    * A remainder < (256 + clkdiv), which means the dividend (256 * remainder) worst case is
181    * 256*(256 + 0x7ff8) = 0x80F800.
182    */
183   br += (256 * remainder) / divisor;
184 
185   return br;
186 }
187 
188 /***************************************************************************//**
189  * @brief
190  *   Get the current baudrate for LEUART.
191  *
192  * @details
193  *   This function returns the actual baudrate (not considering the oscillator
194  *   inaccuracies) used by the LEUART peripheral.
195  *
196  * @param[in] leuart
197  *   A pointer to the LEUART peripheral register block.
198  *
199  * @return
200  *   The current baudrate.
201  ******************************************************************************/
LEUART_BaudrateGet(LEUART_TypeDef * leuart)202 uint32_t LEUART_BaudrateGet(LEUART_TypeDef *leuart)
203 {
204   uint32_t          freq;
205   CMU_Clock_TypeDef clock;
206 
207   /* Get the current frequency. */
208   if (leuart == LEUART0) {
209     clock = cmuClock_LEUART0;
210   }
211 #if (LEUART_COUNT > 1)
212   else if (leuart == LEUART1) {
213     clock = cmuClock_LEUART1;
214   }
215 #endif
216   else {
217     EFM_ASSERT(0);
218     return 0;
219   }
220 
221   freq = CMU_ClockFreqGet(clock);
222 
223   return LEUART_BaudrateCalc(freq, leuart->CLKDIV);
224 }
225 
226 /***************************************************************************//**
227  * @brief
228  *   Configure the baudrate (or as close as possible to a specified baudrate).
229  *
230  * @note
231  *   The baudrate setting requires synchronization into the
232  *   low-frequency domain. If the same register is modified before a previous
233  *   update has completed, this function will stall until the previous
234  *   synchronization has completed.
235  *
236  * @param[in] leuart
237  *   A pointer to the LEUART peripheral register block.
238  *
239  * @param[in] refFreq
240  *   The LEUART reference clock frequency in Hz that will be used. If set to 0,
241  *   the currently configured reference clock is assumed.
242  *
243  * @param[in] baudrate
244  *   A baudrate to try to achieve for LEUART.
245  ******************************************************************************/
LEUART_BaudrateSet(LEUART_TypeDef * leuart,uint32_t refFreq,uint32_t baudrate)246 void LEUART_BaudrateSet(LEUART_TypeDef *leuart,
247                         uint32_t refFreq,
248                         uint32_t baudrate)
249 {
250   uint32_t          clkdiv;
251   CMU_Clock_TypeDef clock;
252 
253   /* Prevent dividing by 0. */
254   EFM_ASSERT(baudrate);
255 
256   /*
257    * Use integer division to avoid forcing in float division
258    * utils, and yet keep rounding effect errors to a minimum.
259    *
260    * CLKDIV in asynchronous mode is given by:
261    *
262    * CLKDIV = 256*(fLEUARTn/br - 1) = ((256*fLEUARTn)/br) - 256
263    *
264    * Normally, with fLEUARTn appr 32768 Hz, there is no problem with overflow
265    * if using 32 bit arithmetic. However, since fLEUARTn may be derived from
266    * HFCORECLK, consider the overflow when using integer arithmetic.
267    *
268    * The basic problem with integer division in the above formula is that
269    * the dividend (256 * fLEUARTn) may become higher than the maximum 32 bit
270    * integer. Yet, the dividend should be evaluated first before dividing
271    * to get as small rounding effects as possible.
272    * Also, harsh restrictions on the maximum fLEUARTn value should not be made.
273    *
274    * Since the last 3 bits of CLKDIV are don't care, base the
275    * integer arithmetic on the below formula:
276    *
277    * CLKDIV/8 = ((32*fLEUARTn)/br) - 32
278    *
279    * and calculate 1/8 of CLKDIV first. This allows for fLEUARTn
280    * up to 128 MHz without overflowing a 32 bit value.
281    */
282 
283   /* Get the current frequency. */
284   if (!refFreq) {
285     if (leuart == LEUART0) {
286       clock = cmuClock_LEUART0;
287     }
288 #if (LEUART_COUNT > 1)
289     else if (leuart == LEUART1) {
290       clock = cmuClock_LEUART1;
291     }
292 #endif
293     else {
294       EFM_ASSERT(0);
295       return;
296     }
297 
298     refFreq = CMU_ClockFreqGet(clock);
299   }
300 
301   /* Calculate and set the CLKDIV with fractional bits. */
302   clkdiv  = (32 * refFreq) / baudrate;
303   clkdiv -= 32;
304   clkdiv *= 8;
305 
306   /* Verify that the resulting clock divider is within limits. */
307   EFM_ASSERT(clkdiv <= _LEUART_CLKDIV_MASK);
308 
309   /* If the EFM_ASSERT is not enabled, make sure not to write to reserved bits. */
310   clkdiv &= _LEUART_CLKDIV_MASK;
311 
312   /* LF register about to be modified requires sync;  busy check. */
313   LEUART_Sync(leuart, LEUART_SYNCBUSY_CLKDIV);
314 
315   leuart->CLKDIV = clkdiv;
316 }
317 
318 /***************************************************************************//**
319  * @brief
320  *   Enable/disable the LEUART receiver and/or transmitter.
321  *
322  * @details
323  *   Notice that this function does not do any configuration. Enabling should
324  *   normally be done after the initialization is done (if not enabled as part
325  *   of initialization).
326  *
327  * @note
328  *   Enabling/disabling requires synchronization into the low-frequency domain.
329  *   If the same register is modified before a previous update has completed,
330  *   this function will stall until the previous synchronization has completed.
331  *
332  * @param[in] leuart
333  *   A pointer to the LEUART peripheral register block.
334  *
335  * @param[in] enable
336  *   Select status for receiver/transmitter.
337  ******************************************************************************/
LEUART_Enable(LEUART_TypeDef * leuart,LEUART_Enable_TypeDef enable)338 void LEUART_Enable(LEUART_TypeDef *leuart, LEUART_Enable_TypeDef enable)
339 {
340   uint32_t tmp;
341 
342   /* Make sure that the module exists on the selected chip. */
343   EFM_ASSERT(LEUART_REF_VALID(leuart));
344 
345   /* Disable as specified. */
346   tmp   = ~((uint32_t)(enable));
347   tmp  &= (_LEUART_CMD_RXEN_MASK | _LEUART_CMD_TXEN_MASK);
348   tmp <<= 1;
349   /* Enable as specified. */
350   tmp |= (uint32_t)(enable);
351 
352   /* LF register about to be modified requires sync; busy check. */
353   LEUART_Sync(leuart, LEUART_SYNCBUSY_CMD);
354 
355   leuart->CMD = tmp;
356 }
357 
358 /***************************************************************************//**
359  * @brief
360  *   LEUART register synchronization freeze control.
361  *
362  * @details
363  *   Some LEUART registers require synchronization into the low-frequency (LF)
364  *   domain. The freeze feature allows for several such registers to be
365  *   modified before passing them to the LF domain simultaneously (which
366  *   takes place when the freeze mode is disabled).
367  *
368  * @note
369  *   When enabling freeze mode, this function will wait for all current
370  *   ongoing LEUART synchronization to the LF domain to complete (Normally
371  *   synchronization will not be in progress.) However, for this reason, when
372  *   using freeze mode, modifications of registers requiring LF synchronization
373  *   should be done within one freeze enable/disable block to avoid unnecessary
374  *   stalling.
375  *
376  * @param[in] leuart
377  *   A pointer to the LEUART peripheral register block.
378  *
379  * @param[in] enable
380  *   @li True - enable freeze, modified registers are not propagated to the
381  *       LF domain
382  *   @li False - disables freeze, modified registers are propagated to the LF
383  *       domain
384  ******************************************************************************/
LEUART_FreezeEnable(LEUART_TypeDef * leuart,bool enable)385 void LEUART_FreezeEnable(LEUART_TypeDef *leuart, bool enable)
386 {
387   if (enable) {
388     /*
389      * Wait for any ongoing LF synchronization to complete to
390      * protect against the rare case when a user
391      * - modifies a register requiring LF sync
392      * - then enables freeze before LF sync completed
393      * - then modifies the same register again
394      * since modifying a register while it is in sync progress should be
395      * avoided.
396      */
397     while (leuart->SYNCBUSY != 0U) {
398     }
399 
400     leuart->FREEZE = LEUART_FREEZE_REGFREEZE;
401   } else {
402     leuart->FREEZE = 0;
403   }
404 }
405 
406 /***************************************************************************//**
407  * @brief
408  *   Initialize LEUART.
409  *
410  * @details
411  *   This function will configure basic settings to operate in normal
412  *   asynchronous mode. Consider using LEUART_Reset() prior to this function if
413  *   the state of configuration is not known, since only configuration settings
414  *   specified by @p init are set.
415  *
416  *   Special control setup not covered by this function may be done either
417  *   before or after using this function (but normally before enabling)
418  *   by direct modification of the CTRL register.
419  *
420  *   Notice that pins used by the LEUART module must be properly configured
421  *   by the user explicitly for the LEUART to work as intended.
422  *   (When configuring pins consider the sequence of
423  *   configuration to avoid unintended pulses/glitches on output
424  *   pins.)
425  *
426  * @note
427  *   Initializing requires synchronization into the low-frequency domain.
428  *   If the same register is modified before a previous update has completed,
429  *   this function will stall until the previous synchronization has completed.
430  *
431  * @param[in] leuart
432  *   A pointer to the LEUART peripheral register block.
433  *
434  * @param[in] init
435  *   A pointer to the initialization structure used to configure basic async setup.
436  ******************************************************************************/
LEUART_Init(LEUART_TypeDef * leuart,LEUART_Init_TypeDef const * init)437 void LEUART_Init(LEUART_TypeDef *leuart, LEUART_Init_TypeDef const *init)
438 {
439   /* Make sure the module exists on the selected chip. */
440   EFM_ASSERT(LEUART_REF_VALID(leuart));
441 
442   /* LF register about to be modified requires sync; busy check. */
443   LEUART_Sync(leuart, LEUART_SYNCBUSY_CMD);
444 
445   /* Ensure disabled while configuring. */
446   leuart->CMD = LEUART_CMD_RXDIS | LEUART_CMD_TXDIS;
447 
448   /* Freeze registers to avoid stalling for the LF synchronization. */
449   LEUART_FreezeEnable(leuart, true);
450 
451   /* Configure databits and stopbits. */
452   leuart->CTRL = (leuart->CTRL & ~(_LEUART_CTRL_PARITY_MASK
453                                    | _LEUART_CTRL_STOPBITS_MASK))
454                  | (uint32_t)(init->databits)
455                  | (uint32_t)(init->parity)
456                  | (uint32_t)(init->stopbits);
457 
458   /* Configure the baudrate. */
459   LEUART_BaudrateSet(leuart, init->refFreq, init->baudrate);
460 
461   /* Finally enable (as specified). */
462   leuart->CMD = (uint32_t)init->enable;
463 
464   /* Unfreeze registers and pass new settings on to LEUART. */
465   LEUART_FreezeEnable(leuart, false);
466 }
467 
468 /***************************************************************************//**
469  * @brief
470  *   Reset LEUART to the same state that it was in after a hardware reset.
471  *
472  * @param[in] leuart
473  *   A pointer to the LEUART peripheral register block.
474  ******************************************************************************/
LEUART_Reset(LEUART_TypeDef * leuart)475 void LEUART_Reset(LEUART_TypeDef *leuart)
476 {
477   /* Make sure the module exists on the selected chip. */
478   EFM_ASSERT(LEUART_REF_VALID(leuart));
479 
480   /* Freeze registers to avoid stalling for LF synchronization. */
481   LEUART_FreezeEnable(leuart, true);
482 
483   /* Make sure disabled first, before resetting other registers. */
484   leuart->CMD = LEUART_CMD_RXDIS | LEUART_CMD_TXDIS | LEUART_CMD_RXBLOCKDIS
485                 | LEUART_CMD_CLEARTX | LEUART_CMD_CLEARRX;
486   leuart->CTRL       = _LEUART_CTRL_RESETVALUE;
487   leuart->CLKDIV     = _LEUART_CLKDIV_RESETVALUE;
488   leuart->STARTFRAME = _LEUART_STARTFRAME_RESETVALUE;
489   leuart->SIGFRAME   = _LEUART_SIGFRAME_RESETVALUE;
490   leuart->IEN        = _LEUART_IEN_RESETVALUE;
491   leuart->IFC        = _LEUART_IFC_MASK;
492   leuart->PULSECTRL  = _LEUART_PULSECTRL_RESETVALUE;
493 #if defined(_LEUART_ROUTEPEN_MASK)
494   leuart->ROUTEPEN   = _LEUART_ROUTEPEN_RESETVALUE;
495   leuart->ROUTELOC0  = _LEUART_ROUTELOC0_RESETVALUE;
496 #else
497   leuart->ROUTE      = _LEUART_ROUTE_RESETVALUE;
498 #endif
499 
500   /* Unfreeze registers and pass new settings on to LEUART. */
501   LEUART_FreezeEnable(leuart, false);
502 }
503 
504 /***************************************************************************//**
505  * @brief
506  *   Receive one 8 bit frame, (or part of 9 bit frame).
507  *
508  * @details
509  *   This function is normally used to receive one frame when operating with
510  *   frame length 8 bits. See LEUART_RxExt() for reception of
511  *   9 bit frames.
512  *
513  *   Notice that possible parity/stop bits are not considered a part of the specified
514  *   frame bit length.
515  *
516  * @note
517  *   This function will stall if the buffer is empty until data is received.
518  *
519  * @param[in] leuart
520  *   A pointer to the LEUART peripheral register block.
521  *
522  * @return
523  *   Data received.
524  ******************************************************************************/
LEUART_Rx(LEUART_TypeDef * leuart)525 uint8_t LEUART_Rx(LEUART_TypeDef *leuart)
526 {
527   while (!(leuart->STATUS & LEUART_STATUS_RXDATAV)) {
528   }
529 
530   return (uint8_t)leuart->RXDATA;
531 }
532 
533 /***************************************************************************//**
534  * @brief
535  *   Receive one 8-9 bit frame with extended information.
536  *
537  * @details
538  *   This function is normally used to receive one frame and additional RX
539  *   status information is required.
540  *
541  * @note
542  *   This function will stall if buffer is empty until data is received.
543  *
544  * @param[in] leuart
545  *   A pointer to the LEUART peripheral register block.
546  *
547  * @return
548  *   Data received.
549  ******************************************************************************/
LEUART_RxExt(LEUART_TypeDef * leuart)550 uint16_t LEUART_RxExt(LEUART_TypeDef *leuart)
551 {
552   while (!(leuart->STATUS & LEUART_STATUS_RXDATAV)) {
553   }
554 
555   return (uint16_t)leuart->RXDATAX;
556 }
557 
558 /***************************************************************************//**
559  * @brief
560  *   Transmit one frame.
561  *
562  * @details
563  *   Depending on the frame length configuration, 8 (least significant) bits from
564  *   @p data are transmitted. If the frame length is 9, 8 bits are transmitted from
565  *   @p data and one bit as specified by the CTRL register, BIT8DV field.
566  *   See LEUART_TxExt() for transmitting 9 bit frame with full control of
567  *   all 9 bits.
568  *
569  *   Notice that possible parity/stop bits in asynchronous mode are not
570  *   considered a part of the specified frame bit length.
571  *
572  * @note
573  *   This function will stall if buffer is full until the buffer becomes available.
574  *
575  * @param[in] leuart
576  *   A pointer to the LEUART peripheral register block.
577  *
578  * @param[in] data
579  *   Data to transmit. See details above for more info.
580  ******************************************************************************/
LEUART_Tx(LEUART_TypeDef * leuart,uint8_t data)581 void LEUART_Tx(LEUART_TypeDef *leuart, uint8_t data)
582 {
583   /* Check that transmit buffer is empty. */
584   while (!(leuart->STATUS & LEUART_STATUS_TXBL)) {
585   }
586 
587   /* LF register about to be modified requires sync; busy check. */
588   LEUART_Sync(leuart, LEUART_SYNCBUSY_TXDATA);
589 
590   leuart->TXDATA = (uint32_t)data;
591 }
592 
593 /***************************************************************************//**
594  * @brief
595  *   Transmit one 8-9 bit frame with extended control.
596  *
597  * @details
598  *   Notice that possible parity/stop bits in asynchronous mode are not
599  *   considered a part of the specified frame bit length.
600  *
601  * @note
602  *   This function will stall if the buffer is full until the buffer becomes available.
603  *
604  * @param[in] leuart
605  *   A pointer to the LEUART peripheral register block.
606  *
607  * @param[in] data
608  *   Data to transmit with extended control. Least significant bit contains
609  *   frame bits and additional control bits are available as documented in
610  *   the reference manual (set to 0 if not used).
611  ******************************************************************************/
LEUART_TxExt(LEUART_TypeDef * leuart,uint16_t data)612 void LEUART_TxExt(LEUART_TypeDef *leuart, uint16_t data)
613 {
614   /* Check that transmit buffer is empty. */
615   while (!(leuart->STATUS & LEUART_STATUS_TXBL)) {
616   }
617 
618   /* LF register about to be modified requires sync; busy check. */
619   LEUART_Sync(leuart, LEUART_SYNCBUSY_TXDATAX);
620 
621   leuart->TXDATAX = (uint32_t)data;
622 }
623 
624 /***************************************************************************//**
625  * @brief
626  *   Enables handling of LEUART TX by DMA in EM2.
627  *
628  * @param[in] leuart
629  *   A pointer to the LEUART peripheral register block.
630  *
631  * @param[in] enable
632  *   True - enables functionality
633  *   False - disables functionality
634  *
635  ******************************************************************************/
LEUART_TxDmaInEM2Enable(LEUART_TypeDef * leuart,bool enable)636 void LEUART_TxDmaInEM2Enable(LEUART_TypeDef *leuart, bool enable)
637 {
638   /* LF register about to be modified requires sync; busy check. */
639   LEUART_Sync(leuart, LEUART_SYNCBUSY_CTRL | LEUART_SYNCBUSY_CMD);
640 
641 #if defined(_SILICON_LABS_GECKO_INTERNAL_SDID_80)
642   /* LEUART_E201: Changing the value of TXDMAWU while TXEN=1 could potentially
643    * cause unpredictable behavior. */
644   bool txEnabled = (leuart->STATUS & _LEUART_STATUS_TXENS_MASK) != 0U;
645   if (txEnabled) {
646     /* Wait for potential transmit to complete. */
647     while ((leuart->STATUS & LEUART_STATUS_TXIDLE) == 0U) {
648     }
649 
650     leuart->CMD = LEUART_CMD_TXDIS;
651     LEUART_Sync(leuart, LEUART_SYNCBUSY_CMD);
652   }
653 
654   if (enable) {
655     leuart->CTRL |= LEUART_CTRL_TXDMAWU;
656   } else {
657     leuart->CTRL &= ~LEUART_CTRL_TXDMAWU;
658   }
659 
660   if (txEnabled) {
661     leuart->CMD = LEUART_CMD_TXEN;
662   }
663 #else
664   if (enable) {
665     leuart->CTRL |= LEUART_CTRL_TXDMAWU;
666   } else {
667     leuart->CTRL &= ~LEUART_CTRL_TXDMAWU;
668   }
669 #endif
670 }
671 
672 /***************************************************************************//**
673  * @brief
674  *   Enables handling of LEUART RX by DMA in EM2.
675  *
676  * @param[in] leuart
677  *   A pointer to the LEUART peripheral register block.
678  *
679  * @param[in] enable
680  *   True - enables functionality
681  *   False - disables functionality
682  *
683  ******************************************************************************/
LEUART_RxDmaInEM2Enable(LEUART_TypeDef * leuart,bool enable)684 void LEUART_RxDmaInEM2Enable(LEUART_TypeDef *leuart, bool enable)
685 {
686   /* LF register about to be modified requires sync; busy check. */
687   LEUART_Sync(leuart, LEUART_SYNCBUSY_CTRL | LEUART_SYNCBUSY_CMD);
688 
689 #if defined(_SILICON_LABS_GECKO_INTERNAL_SDID_80)
690   /* LEUART_E201: Changing the value of RXDMAWU while RXEN=1 could potentially
691    * cause unpredictable behavior. */
692   bool rxEnabled = (leuart->STATUS & _LEUART_STATUS_RXENS_MASK) != 0U;
693 
694   if (rxEnabled) {
695     leuart->CMD = LEUART_CMD_RXDIS;
696     LEUART_Sync(leuart, LEUART_SYNCBUSY_CMD);
697   }
698 
699   if (enable) {
700     leuart->CTRL |= LEUART_CTRL_RXDMAWU;
701   } else {
702     leuart->CTRL &= ~LEUART_CTRL_RXDMAWU;
703   }
704 
705   if (rxEnabled) {
706     leuart->CMD = LEUART_CMD_RXEN;
707   }
708 #else
709   if (enable) {
710     leuart->CTRL |= LEUART_CTRL_RXDMAWU;
711   } else {
712     leuart->CTRL &= ~LEUART_CTRL_RXDMAWU;
713   }
714 #endif
715 }
716 
717 /** @} (end addtogroup leuart) */
718 #endif /* defined(LEUART_COUNT) && (LEUART_COUNT > 0) */
719