1 /*******************************************************************************
2 * File Name: cyhal_uart.c
3 *
4 * Description:
5 * Provides a high level interface for interacting with the Infineon UART. This is
6 * a wrapper around the lower level PDL API.
7 *
8 ********************************************************************************
9 * \copyright
10 * Copyright 2018-2022 Cypress Semiconductor Corporation (an Infineon company) or
11 * an affiliate of Cypress Semiconductor Corporation
12 *
13 * SPDX-License-Identifier: Apache-2.0
14 *
15 * Licensed under the Apache License, Version 2.0 (the "License");
16 * you may not use this file except in compliance with the License.
17 * You may obtain a copy of the License at
18 *
19 *     http://www.apache.org/licenses/LICENSE-2.0
20 *
21 * Unless required by applicable law or agreed to in writing, software
22 * distributed under the License is distributed on an "AS IS" BASIS,
23 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
24 * See the License for the specific language governing permissions and
25 * limitations under the License.
26 *******************************************************************************/
27 
28 #include <stdlib.h>
29 #include <string.h>
30 #include "cyhal_uart.h"
31 #include "cyhal_scb_common.h"
32 #include "cyhal_gpio.h"
33 #include "cyhal_system_impl.h"
34 #include "cyhal_hwmgr.h"
35 #include "cyhal_syspm.h"
36 #include "cyhal_clock.h"
37 #if (CYHAL_DRIVER_AVAILABLE_DMA)
38 #include "cyhal_dma.h"
39 #endif
40 #include "cyhal_interconnect.h"
41 #include "cyhal_irq_impl.h"
42 
43 #if (CYHAL_DRIVER_AVAILABLE_UART)
44 
45 /** \addtogroup group_hal_uart UART
46  * \ingroup group_hal
47  * \{
48  * \section group_hal_uart_dma_transfers Asynchronous DMA Transfers
49  * Asynchronous transfers can be performed using DMA to load data into
50  * and from the FIFOs. The purpose of this is to minimize CPU load on
51  * large transfers. Unlike SW async mode, DMA async mode does not support an
52  * overflow buffer as it would cause CPU load and go against the fundamental
53  * principle of using DMA transfers. Trying to set DMA async mode when an
54  * overflow buffer is enabled will return an error. If an application wants
55  * to transition from SW to DMA async modes it is recommended to perform the following
56  * sequence:
57  * 1. Read until the ring buffer is empty. This can be determined by checking
58  * that cyhal_uart_readable returns a number that is smaller than the hardware fifo size.
59  * 2. Enter a critical section, to prevent any interrupts from refilling the ring buffer.
60  * 3. Repeat step 1 if necessary
61  * 4. Use cyhal_uart_configure to disable the ring buffer, or free and reinit the UART
62  *    instance with no SW buffer
63  * 5. Exit the critical section
64  * 6. Call cyhal_uart_set_async_mode to change the async mode to DMA.
65  *
66  * If an application wants to transition from DMA to SW async modes, and enable
67  * the ring buffer, it is recommended to perform the following sequence:
68  * 1. Call cyhal_uart_set_async_mode to change the async mode to SW.
69  * 2. Use cyhal_uart_config_software_buffer to enable the ring buffer
70  *
71  * \section group_hal_uart_pm_strategy UART at different power modes
72  * In order to allow UART to maintain the specified baud rate in all
73  * power modes it was necessary to introduce a power management callback
74  * that deals with the state switching for supported power modes
75  * (e.g. normal, low power).
76  * As the clock inputs can change when switching power modes it is necessary
77  * to reset the baud rate. Since it is not possible to pause a transmission
78  * and then restart it after the power mode has been updated whenever a system
79  * state change is detected any ongoing transmission is aborted.
80  * It is then up to the user to make sure that all transmissions are finished
81  * Therefore, power state transition is prevented if the FIFO is not empty.
82  * It's also important to note that if a custom cyhal_clock_t value was provided
83  * during cyhal_uart_init, the UART HAL cannot automatically update the baud rate
84  * because it does not own the source clock.
85  * In that case, the application must adjust the UART source clock itself to retain
86  * the desired frequency.
87  * \} group_hal_uart
88  */
89 
90 
91 #if defined(__cplusplus)
92 extern "C"
93 {
94 #endif
95 
96 #if defined(COMPONENT_CAT5)
97 /* CAT5 SCB System Clock Maximum frequency is 192MHz, maximum divider
98  * value is 128 and hence the minimum valid oversample value possible is 13
99  */
100 #define _CYHAL_UART_OVERSAMPLE                 13UL
101 #else
102 #define _CYHAL_UART_OVERSAMPLE                 12UL
103 #endif
104 #define _CYHAL_UART_OVERSAMPLE_MIN             8UL
105 #define _CYHAL_UART_OVERSAMPLE_MAX             16UL
106 
107 /* Default UART configuration */
108 static const cy_stc_scb_uart_config_t _cyhal_uart_default_config = {
109     .uartMode                   = CY_SCB_UART_STANDARD,
110     .enableMutliProcessorMode   = false,
111     .smartCardRetryOnNack       = false,
112     .irdaInvertRx               = false,
113     .irdaEnableLowPowerReceiver = false,
114 
115     .oversample                 = _CYHAL_UART_OVERSAMPLE,
116 
117     .enableMsbFirst             = false,
118     .dataWidth                  = 8UL,
119     .parity                     = CY_SCB_UART_PARITY_NONE,
120     .stopBits                   = CY_SCB_UART_STOP_BITS_1,
121     .enableInputFilter          = false,
122     .breakWidth                 = 11UL,
123     .dropOnFrameError           = false,
124     .dropOnParityError          = false,
125 
126     .receiverAddress            = 0x0UL,
127     .receiverAddressMask        = 0x0UL,
128     .acceptAddrInFifo           = false,
129 
130     .enableCts                  = false,
131     .ctsPolarity                = CY_SCB_UART_ACTIVE_LOW,
132 #if defined(COMPONENT_CAT1A) || defined(COMPONENT_CAT1B) || defined(COMPONENT_CAT1C) || defined(COMPONENT_CAT1D) || defined(COMPONENT_CAT5)
133     .rtsRxFifoLevel             = 20UL,
134 #elif defined(COMPONENT_CAT2)
135     .rtsRxFifoLevel             = 3UL,
136 #endif
137     .rtsPolarity                = CY_SCB_UART_ACTIVE_LOW,
138 
139     .rxFifoTriggerLevel         = 0UL,  /* Level triggers when at least one element is in FIFO */
140     .rxFifoIntEnableMask        = 0x0UL,
141 
142     .txFifoTriggerLevel         = (CY_SCB_FIFO_SIZE/2 - 1), /* Level triggers when half-fifo is half empty */
143     .txFifoIntEnableMask        = 0x0UL
144 };
145 
146 /* The PDL clears the IRQ status during Cy_SCB_UART_Interrupt which prevents _cyhal_scb_get_irq_obj()
147  * from working properly in _cyhal_uart_cb_wrapper on devices with muxed IRQs, because they can't tell
148  * at that point which system IRQ caused the CPU IRQ. So we need to save this value at the beginning of the
149  * IRQ handler when we are able to determine what it is */
150 static volatile cyhal_uart_t* _cyhal_uart_irq_obj = NULL;
151 
152 #if defined (COMPONENT_CAT5)
_cyhal_uart_irq_handler(_cyhal_system_irq_t irqn)153 static void _cyhal_uart_irq_handler(_cyhal_system_irq_t irqn)
154 #else
155 static void _cyhal_uart_irq_handler(void)
156 #endif
157 {
158     /* Save the old value and store it aftewards in case we get into a nested IRQ situation */
159     /* Safe to cast away volatile because we don't expect this pointer to be changed while we're in here, they
160      * just might change where the original pointer points */
161     cyhal_uart_t* old_irq_obj = (cyhal_uart_t*)_cyhal_uart_irq_obj;
162 #if defined (COMPONENT_CAT5)
163     _cyhal_uart_irq_obj = (cyhal_uart_t*) _cyhal_scb_get_irq_obj(irqn);
164 #else
165     _cyhal_uart_irq_obj = (cyhal_uart_t*) _cyhal_scb_get_irq_obj();
166 #endif
167 
168     if (NULL == _cyhal_uart_irq_obj)
169     {
170         return;  /* The interrupt object is not valid */
171     }
172 
173     cyhal_uart_t* obj = (cyhal_uart_t*)_cyhal_uart_irq_obj;
174 
175     /* Cy_SCB_UART_Interrupt() manipulates the interrupt masks. Save a copy to work around it. */
176     uint32_t txMasked = Cy_SCB_GetTxInterruptStatusMasked(obj->base);
177     uint32_t rxMasked = Cy_SCB_GetRxInterruptStatusMasked(obj->base);
178 
179    /* SCB high-level API interrupt handler. Must be called as high-level API is used in the HAL */
180     Cy_SCB_UART_Interrupt(obj->base, &(obj->context));
181 
182     /* Custom handling for TX overflow (cannot occur using HAL API but can occur if user makes custom modifications)
183         Note: This is partially handled in Cy_SCB_UART_Interrupt()
184         but it only takes care of NACK and ARB_LOST errors. */
185     if (0UL != (CY_SCB_UART_TX_OVERFLOW & txMasked))
186     {
187         Cy_SCB_ClearTxInterrupt(obj->base, CY_SCB_UART_TX_OVERFLOW);
188 
189         if (NULL != obj->context.cbEvents)
190         {
191             obj->context.cbEvents(CY_SCB_UART_TRANSMIT_ERR_EVENT);
192         }
193     }
194 
195     /* Custom handling for TX underflow (cannot occur using HAL API but can occur if user makes custom modifications)
196         Note: This is partially handled in Cy_SCB_UART_Interrupt()
197         but it only takes care of NACK and ARB_LOST errors. */
198     if (0UL != (CY_SCB_UART_TX_UNDERFLOW & txMasked))
199     {
200         Cy_SCB_ClearTxInterrupt(obj->base, CY_SCB_UART_TX_UNDERFLOW);
201 
202         if (NULL != obj->context.cbEvents)
203         {
204             obj->context.cbEvents(CY_SCB_UART_TRANSMIT_ERR_EVENT);
205         }
206     }
207 
208     /* Custom handling for TX FIFO trigger.
209         Note: This is partially handled in Cy_SCB_UART_Interrupt()
210         when processing CY_SCB_TX_INTR_LEVEL. Do not clear the interrupt. */
211     if (0UL != (CY_SCB_UART_TX_TRIGGER & txMasked))
212     {
213         if (NULL != obj->context.cbEvents)
214         {
215             // Need to shift by 1 due to to existing logic in _cyhal_utils_convert_flags()
216             obj->context.cbEvents(CYHAL_UART_IRQ_TX_FIFO >> 1u);
217         }
218     }
219 
220     /* Manually clear the tx done interrupt and re-enable the interrupt mask */
221     if (0UL != (CY_SCB_UART_TX_DONE & txMasked))
222     {
223         Cy_SCB_ClearTxInterrupt(obj->base, CY_SCB_UART_TX_DONE);
224         Cy_SCB_SetTxInterruptMask(obj->base, Cy_SCB_GetTxInterruptMask(obj->base) | CY_SCB_UART_TX_DONE);
225     }
226 
227     /* Custom handling for RX underflow (cannot occur using HAL API but can occur if user makes custom modifications)
228         Note: This is partially handled in Cy_SCB_UART_Interrupt()
229         which takes care of overflow, frame and parity errors. */
230     if (0UL != (CY_SCB_RX_INTR_UNDERFLOW & rxMasked))
231     {
232         Cy_SCB_ClearRxInterrupt(obj->base, CY_SCB_RX_INTR_UNDERFLOW);
233 
234         if (NULL != obj->context.cbEvents)
235         {
236             obj->context.cbEvents(CY_SCB_UART_RECEIVE_ERR_EVENT);
237         }
238     }
239 
240     /* Custom handling for RX FIFO trigger
241         Note: This is partially handled in Cy_SCB_UART_Interrupt()
242         when processing CY_SCB_RX_INTR_LEVEL. Do not clear the interrupt. */
243     if (0UL != (CY_SCB_UART_RX_TRIGGER & rxMasked))
244     {
245         if (NULL != obj->context.cbEvents)
246         {
247             // Need to shift by 1 due to to existing logic in _cyhal_utils_convert_flags()
248             obj->context.cbEvents(CYHAL_UART_IRQ_RX_FIFO >> 1u);
249         }
250     }
251 
252     _cyhal_uart_irq_obj = old_irq_obj;
253 }
254 
255 #if defined (COMPONENT_CAT5)
256 // CAT5 interrupts are implemented oddly in PDL: they auto-disable themselves after firing.  So re-enable
_cyhal_uart0_irq_handler(void)257 static void _cyhal_uart0_irq_handler(void)
258 {
259     _cyhal_uart_irq_handler(scb_0_interrupt_IRQn);
260     Cy_SCB_EnableInterrupt(SCB0);
261 }
262 
_cyhal_uart1_irq_handler(void)263 static void _cyhal_uart1_irq_handler(void)
264 {
265     _cyhal_uart_irq_handler(scb_1_interrupt_IRQn);
266     Cy_SCB_EnableInterrupt(SCB1);
267 }
268 
_cyhal_uart2_irq_handler(void)269 static void _cyhal_uart2_irq_handler(void)
270 {
271     _cyhal_uart_irq_handler(scb_2_interrupt_IRQn);
272     Cy_SCB_EnableInterrupt(SCB2);
273 }
274 
275 static CY_SCB_IRQ_THREAD_CB_t _cyhal_irq_cb[3] = {_cyhal_uart0_irq_handler, _cyhal_uart1_irq_handler, _cyhal_uart2_irq_handler};
276 #endif
277 
278 #if (CYHAL_DRIVER_AVAILABLE_DMA)
279 cy_rslt_t _cyhal_uart_dma_write_async(cyhal_uart_t *obj);
280 
_cyhal_uart_dma_handler_tx(void * arg,cyhal_dma_event_t event)281 static void _cyhal_uart_dma_handler_tx(void* arg, cyhal_dma_event_t event)
282 {
283     CY_ASSERT(CYHAL_DMA_TRANSFER_COMPLETE == event);
284     CY_UNUSED_PARAMETER(event);
285     cyhal_uart_t* obj = (cyhal_uart_t*)arg;
286     CY_ASSERT(CYHAL_ASYNC_DMA == obj->async_mode);
287 
288     if(obj->async_tx_length > 0)
289     {
290         /* Setup another transfer if we're expecting more */
291         _cyhal_uart_dma_write_async(obj);
292     }
293     else
294     {
295         obj->async_tx_buff = NULL;
296         /* We may have lowered the FIFO to complete a small transfer. Restore it */
297         if(obj->user_fifo_level != ((SCB_TX_FIFO_CTRL(obj->base) & SCB_TX_FIFO_CTRL_TRIGGER_LEVEL_Msk)))
298         {
299             _cyhal_scb_set_fifo_level(obj->base, (cyhal_scb_fifo_type_t)CYHAL_UART_FIFO_TX, obj->user_fifo_level);
300         }
301 
302         if(0 == (CYHAL_UART_IRQ_TX_FIFO & ((cyhal_uart_event_t)obj->irq_cause)))
303         {
304              Cy_SCB_SetTxInterruptMask(obj->base, Cy_SCB_GetTxInterruptMask(obj->base) & ~CY_SCB_TX_INTR_LEVEL);
305         }
306 
307         if(0 != ((cyhal_uart_event_t)obj->irq_cause & CYHAL_UART_IRQ_TX_TRANSMIT_IN_FIFO))
308         {
309             cyhal_uart_event_callback_t callback = (cyhal_uart_event_callback_t)obj->callback_data.callback;
310             if(NULL != callback)
311             {
312                 callback(obj->callback_data.callback_arg, CYHAL_UART_IRQ_TX_TRANSMIT_IN_FIFO);
313             }
314         }
315     }
316 }
317 
318 cy_rslt_t _cyhal_uart_dma_read_async(cyhal_uart_t *obj);
319 
_cyhal_uart_dma_handler_rx(void * arg,cyhal_dma_event_t event)320 static void _cyhal_uart_dma_handler_rx(void* arg, cyhal_dma_event_t event)
321 {
322     CY_ASSERT(CYHAL_DMA_TRANSFER_COMPLETE == event);
323     CY_UNUSED_PARAMETER(event);
324     cyhal_uart_t* obj = (cyhal_uart_t*)arg;
325     CY_ASSERT(CYHAL_ASYNC_DMA == obj->async_mode);
326 
327     if(obj->async_rx_length > 0)
328     {
329         /* Setup another transfer if we're expecting more */
330         _cyhal_uart_dma_read_async(obj);
331     }
332     else
333     {
334         obj->async_rx_buff = NULL;
335         /* We may have lowered the FIFO to complete a small transfer. Restore it */
336         if(obj->user_fifo_level != ((SCB_RX_FIFO_CTRL(obj->base) & SCB_RX_FIFO_CTRL_TRIGGER_LEVEL_Msk)))
337         {
338             _cyhal_scb_set_fifo_level(obj->base, (cyhal_scb_fifo_type_t)CYHAL_UART_FIFO_RX, obj->user_fifo_level);
339         }
340 
341         if(0 != ((cyhal_uart_event_t)obj->irq_cause & CYHAL_UART_IRQ_RX_DONE))
342         {
343             cyhal_uart_event_callback_t callback = (cyhal_uart_event_callback_t)obj->callback_data.callback;
344             if(NULL != callback)
345             {
346                 callback(obj->callback_data.callback_arg, CYHAL_UART_IRQ_RX_DONE);
347             }
348         }
349     }
350 }
351 #endif
352 
_cyhal_uart_cb_wrapper(uint32_t event)353 static void _cyhal_uart_cb_wrapper(uint32_t event)
354 {
355     static const uint32_t status_map[] =
356     {
357         /* The ordering here has to match group_scb_uart_macros_callback_events in the PDL since it uses
358            the high-level APIs to perform the callback handling.
359            Note: Remember that the values are shifted by 1. */
360         (uint32_t)CYHAL_UART_IRQ_NONE,                 // 0: Default no IRQ
361         (uint32_t)CYHAL_UART_IRQ_TX_TRANSMIT_IN_FIFO,  // 1: CY_SCB_UART_TRANSMIT_IN_FIFO_EVENT
362         (uint32_t)CYHAL_UART_IRQ_TX_DONE,              // 2: CY_SCB_UART_TRANSMIT_DONE_EVENT
363         (uint32_t)CYHAL_UART_IRQ_RX_DONE,              // 3: CY_SCB_UART_RECEIVE_DONE_EVENT
364         (uint32_t)CYHAL_UART_IRQ_RX_FULL,              // 4: CY_SCB_UART_RB_FULL_EVENT
365         (uint32_t)CYHAL_UART_IRQ_RX_ERROR,             // 5: CY_SCB_UART_RECEIVE_ERR_EVENT
366         (uint32_t)CYHAL_UART_IRQ_TX_ERROR,             // 6: CY_SCB_UART_TRANSMIT_ERR_EVENT
367         (uint32_t)CYHAL_UART_IRQ_RX_NOT_EMPTY,         // 7: CY_SCB_UART_RECEIVE_NOT_EMTPY
368         (uint32_t)CYHAL_UART_IRQ_TX_EMPTY,             // 8: CY_SCB_UART_TRANSMIT_EMTPY
369         // Custom events from HAL
370         (uint32_t)CYHAL_UART_IRQ_TX_FIFO,              // 9: CYHAL_UART_IRQ_TX_FIFO
371         (uint32_t)CYHAL_UART_IRQ_RX_FIFO,              // 10: CYHAL_UART_IRQ_RX_FIFO
372     };
373     uint32_t hal_event = _cyhal_utils_convert_flags(status_map, sizeof(status_map) / sizeof(uint32_t), event);
374 
375     /* Safe to cast away volatile because we don't expect this pointer to be changed while we're in here, they
376      * just might change where the original pointer points */
377     cyhal_uart_t *obj = (cyhal_uart_t*)_cyhal_uart_irq_obj;
378     cyhal_uart_event_t anded_events = (cyhal_uart_event_t)(obj->irq_cause & hal_event);
379     if (anded_events)
380     {
381         cyhal_uart_event_callback_t callback = (cyhal_uart_event_callback_t) obj->callback_data.callback;
382         callback(obj->callback_data.callback_arg, anded_events);
383     }
384 }
385 
_cyhal_uart_pm_callback_instance(void * obj_ptr,cyhal_syspm_callback_state_t state,cy_en_syspm_callback_mode_t pdl_mode)386 static bool _cyhal_uart_pm_callback_instance(void *obj_ptr, cyhal_syspm_callback_state_t state, cy_en_syspm_callback_mode_t pdl_mode)
387 {
388     cyhal_uart_t *obj = (cyhal_uart_t*)obj_ptr;
389     bool allow = false;
390 
391     // The output pins need to be set to high before going to deepsleep.
392     // Otherwise the UART on the other side would see incoming data as '0'.
393     GPIO_PRT_Type *txport = obj->pin_tx != NC ? CYHAL_GET_PORTADDR(obj->pin_tx) : NULL;
394     GPIO_PRT_Type *rtsport = ((obj->pin_rts != NC) && obj->rts_enabled) ? CYHAL_GET_PORTADDR(obj->pin_rts) : NULL;
395     uint8_t txpin = (uint8_t)CYHAL_GET_PIN(obj->pin_tx);
396     uint8_t rtspin = (uint8_t)CYHAL_GET_PIN(obj->pin_rts);
397     #if defined (COMPONENT_CAT5)
398         CY_UNUSED_PARAMETER(txpin);
399         CY_UNUSED_PARAMETER(rtspin);
400     #endif
401 
402    if (state == CYHAL_SYSPM_CB_SYSTEM_NORMAL || state == CYHAL_SYSPM_CB_SYSTEM_LOW)
403     {
404         if(pdl_mode == CY_SYSPM_CHECK_READY)
405         {
406             /* Check whether the High-level API is not busy executing the transmit
407             * or receive operation.
408             */
409             if ((0UL == (CY_SCB_UART_TRANSMIT_ACTIVE & Cy_SCB_UART_GetTransmitStatus(obj->base, &(obj->context)))) &&
410                 (0UL == (CY_SCB_UART_RECEIVE_ACTIVE  & Cy_SCB_UART_GetReceiveStatus (obj->base, &(obj->context))))
411             #if (CYHAL_DRIVER_AVAILABLE_DMA)
412                 & (obj->async_rx_buff == NULL) & (obj->async_tx_buff == NULL))
413             #else
414                 )
415             #endif /* CYHAL_DRIVER_AVAILABLE_DMA */
416             {
417                 /* If all data elements are transmitted from the TX FIFO and
418                 * shifter and the RX FIFO is empty: the UART is ready to enter
419                 * Deep Sleep mode.
420                 */
421                 if (Cy_SCB_UART_IsTxComplete(obj->base))
422                 {
423                     if (0UL == Cy_SCB_UART_GetNumInRxFifo(obj->base))
424                     {
425                         allow = true;
426                     }
427                 }
428             }
429         }
430         if (pdl_mode == CY_SYSPM_AFTER_TRANSITION)
431         {
432             allow = true;
433             //Return of the function is ignored as failing to reset the baud rate
434             //does not classify as reason to interrupt the power mode transition
435             cyhal_uart_set_baud(obj, obj->baud_rate, NULL);
436 
437             if (NULL != rtsport)
438             {
439                 Cy_GPIO_SetHSIOM(rtsport, rtspin, obj->saved_rts_hsiom);
440             }
441         }
442         if (pdl_mode == CY_SYSPM_BEFORE_TRANSITION)
443         {
444             allow = true;
445 
446             if (NULL != rtsport)
447             {
448                 obj->saved_rts_hsiom = Cy_GPIO_GetHSIOM(rtsport, rtspin);
449                 Cy_GPIO_Set(rtsport, rtspin);
450                 Cy_GPIO_SetHSIOM(rtsport, rtspin, HSIOM_SEL_GPIO);
451             }
452         }
453     }
454     else
455     {
456         switch (pdl_mode)
457         {
458             case CY_SYSPM_CHECK_READY:
459                 /* Check whether the High-level API is not busy executing the transmit
460                 * or receive operation.
461                 */
462                 if ((0UL == (CY_SCB_UART_TRANSMIT_ACTIVE & Cy_SCB_UART_GetTransmitStatus(obj->base, &(obj->context)))) &&
463                     (0UL == (CY_SCB_UART_RECEIVE_ACTIVE  & Cy_SCB_UART_GetReceiveStatus (obj->base, &(obj->context))))
464                 #if (CYHAL_DRIVER_AVAILABLE_DMA)
465                     & (obj->async_rx_buff == NULL) & (obj->async_tx_buff == NULL))
466                 #else
467                     )
468                 #endif /* CYHAL_DRIVER_AVAILABLE_DMA */
469                 {
470                     /* Not all interrupts are preserved across DeepSleep entry/exit. For example,
471                      * TX_UART_DONE is not. So block DeepSleep entry if any of our interrupts are
472                      * unmasked. It is safe to check at the SCB level rather than the NVIC because
473                      * the HAL never unmasks an interrupt without registering a handler.
474                      *
475                      * Neither of the FIFOs are preserved, so also block DeepSleep if either of them
476                      * has contents.
477                      */
478                     uint32_t txMasked = Cy_SCB_GetTxInterruptStatusMasked(obj->base);
479                     uint32_t rxMasked = Cy_SCB_GetRxInterruptStatusMasked(obj->base);
480                     /* If all data elements are transmitted from the TX FIFO and
481                     * shifter and the RX FIFO is empty: the UART is ready to enter
482                     * Deep Sleep mode.
483                     */
484 
485                     if (Cy_SCB_UART_IsTxComplete(obj->base)
486                          && (0UL == Cy_SCB_UART_GetNumInRxFifo(obj->base))
487                          && (0UL == txMasked) && (0UL == rxMasked))
488                     {
489                         /* Disable the UART. The transmitter stops driving the
490                         * lines and the receiver stops receiving data until
491                         * the UART is enabled.
492                         * This happens when the device failed to enter Deep
493                         * Sleep or it is awaken from Deep Sleep mode.
494                         */
495 
496                         if (NULL != txport)
497                         {
498                             obj->saved_tx_hsiom = Cy_GPIO_GetHSIOM(txport, txpin);
499                             Cy_GPIO_Set(txport, txpin);
500                             Cy_GPIO_SetHSIOM(txport, txpin, HSIOM_SEL_GPIO);
501                         }
502                         if (NULL != rtsport)
503                         {
504                             obj->saved_rts_hsiom = Cy_GPIO_GetHSIOM(rtsport, rtspin);
505                             Cy_GPIO_Set(rtsport, rtspin);
506                             Cy_GPIO_SetHSIOM(rtsport, rtspin, HSIOM_SEL_GPIO);
507                         }
508 
509                         Cy_SCB_UART_Disable(obj->base, &(obj->context));
510                         allow = true;
511                     }
512                 }
513 
514                 break;
515 
516             case CY_SYSPM_CHECK_FAIL:
517             case CY_SYSPM_AFTER_TRANSITION:
518                 allow = true;
519                 Cy_SCB_UART_Enable(obj->base);
520                 if (NULL != txport)
521                 {
522                     Cy_GPIO_SetHSIOM(txport, txpin, obj->saved_tx_hsiom);
523                 }
524                 if (NULL != rtsport)
525                 {
526                     Cy_GPIO_SetHSIOM(rtsport, rtspin, obj->saved_rts_hsiom);
527                 }
528                 break;
529 
530             case CY_SYSPM_BEFORE_TRANSITION:
531                 allow = true;
532                 break;
533     #if defined(COMPONENT_CAT1B)
534             case CY_SYSPM_AFTER_DS_WFI_TRANSITION:
535                 allow = true;
536                 break;
537     #endif
538             default:
539                 CY_ASSERT(false);
540                 break;
541         }
542     }
543     return allow;
544 }
_cyhal_uart_convert_parity(cyhal_uart_parity_t parity)545 static cy_en_scb_uart_parity_t _cyhal_uart_convert_parity(cyhal_uart_parity_t parity)
546 {
547     switch (parity)
548     {
549         case CYHAL_UART_PARITY_NONE:
550             return CY_SCB_UART_PARITY_NONE;
551         case CYHAL_UART_PARITY_EVEN:
552             return CY_SCB_UART_PARITY_EVEN;
553         case CYHAL_UART_PARITY_ODD:
554             return CY_SCB_UART_PARITY_ODD;
555         default:
556             return CY_SCB_UART_PARITY_NONE;
557     }
558 }
559 
_cyhal_uart_convert_stopbits(uint8_t stopbits)560 static cy_en_scb_uart_stop_bits_t _cyhal_uart_convert_stopbits(uint8_t stopbits)
561 {
562     switch (stopbits)
563     {
564         case 1:
565             return CY_SCB_UART_STOP_BITS_1;
566         case 2:
567             return CY_SCB_UART_STOP_BITS_2;
568         case 3:
569             return CY_SCB_UART_STOP_BITS_3;
570         case 4:
571             return CY_SCB_UART_STOP_BITS_4;
572         default:
573             CY_ASSERT(false);
574             return CY_SCB_UART_STOP_BITS_1;
575     }
576 }
577 
_cyhal_uart_actual_baud(const cyhal_resource_inst_t * resource,uint32_t divider,uint32_t oversample)578 static uint32_t _cyhal_uart_actual_baud(const cyhal_resource_inst_t *resource, uint32_t divider, uint32_t oversample)
579 {
580     return _cyhal_utils_get_peripheral_clock_frequency(resource) / (divider * oversample);
581 }
582 
_cyhal_uart_baud_perdif(uint32_t desired_baud,uint32_t actual_baud)583 static uint32_t _cyhal_uart_baud_perdif(uint32_t desired_baud, uint32_t actual_baud)
584 {
585     return (actual_baud > desired_baud)
586         ? ((actual_baud * 100) - (desired_baud * 100)) / desired_baud
587         : ((desired_baud * 100) - (actual_baud * 100)) / desired_baud;
588 }
589 
_cyhal_uart_best_oversample(const cyhal_resource_inst_t * resource,uint32_t baudrate)590 static uint8_t _cyhal_uart_best_oversample(const cyhal_resource_inst_t *resource, uint32_t baudrate)
591 {
592     uint8_t best_oversample = _CYHAL_UART_OVERSAMPLE_MIN;
593     uint8_t best_difference = 0xFF;
594 
595     for (uint8_t i = _CYHAL_UART_OVERSAMPLE_MIN; i < _CYHAL_UART_OVERSAMPLE_MAX + 1; i++)
596     {
597         uint32_t divider = _cyhal_utils_divider_value(resource, baudrate * i, 0);
598 
599         #if defined(COMPONENT_CAT5)
600         if(!_cyhal_clock_is_divider_valid(resource, divider))
601         {
602             continue;
603         }
604         #endif
605         uint8_t difference = (uint8_t)_cyhal_uart_baud_perdif(baudrate, _cyhal_uart_actual_baud(resource, divider, i));
606 
607         if (difference < best_difference)
608         {
609             best_difference = difference;
610             best_oversample = i;
611         }
612     }
613 
614     return best_oversample;
615 }
616 
_cyhal_uart_setup_resources(cyhal_uart_t * obj,cyhal_gpio_t tx,cyhal_gpio_t rx,cyhal_gpio_t cts,cyhal_gpio_t rts,const cyhal_clock_t * clk)617 static cy_rslt_t _cyhal_uart_setup_resources(cyhal_uart_t *obj, cyhal_gpio_t tx, cyhal_gpio_t rx, cyhal_gpio_t cts,
618                                              cyhal_gpio_t rts, const cyhal_clock_t *clk)
619 {
620     cy_rslt_t result;
621 
622     // Explicitly marked not allocated resources as invalid to prevent freeing them.
623     obj->resource.type = CYHAL_RSC_INVALID;
624     obj->is_clock_owned = false;
625     obj->pin_rx = CYHAL_NC_PIN_VALUE;
626     obj->pin_tx = CYHAL_NC_PIN_VALUE;
627     obj->pin_cts = CYHAL_NC_PIN_VALUE;
628     obj->pin_rts = CYHAL_NC_PIN_VALUE;
629 
630     // checking for invalid flow control pin combinations (corresponding pin required) and checking that at least TX or RX is specified (both cannot be NC)
631     if ((NC == tx && NC != rts) || (NC == rx && NC != cts) || (NC == tx && NC == rx))
632     {
633         return CYHAL_UART_RSLT_ERR_INVALID_PIN;
634     }
635 
636     uint32_t saved_intr_status = cyhal_system_critical_section_enter();
637 
638     // pins_blocks will contain bit representation of blocks, that are connected to specified pin
639     // 1 block - 1 bit, so, for example, pin_blocks = 0x00000006 means that certain pin
640     // can belong to next non-reserved blocks SCB2 and SCB1
641     uint32_t pins_blocks = _CYHAL_SCB_AVAILABLE_BLOCKS_MASK;
642     if (NC != tx)
643     {
644         pins_blocks &= _CYHAL_SCB_CHECK_AFFILIATION(tx, cyhal_pin_map_scb_uart_tx);
645     }
646     if (NC != rx)
647     {
648         pins_blocks &= _CYHAL_SCB_CHECK_AFFILIATION(rx, cyhal_pin_map_scb_uart_rx);
649     }
650     if (NC != cts)
651     {
652         pins_blocks &= _CYHAL_SCB_CHECK_AFFILIATION(cts, cyhal_pin_map_scb_uart_cts);
653     }
654     if (NC != rts)
655     {
656         pins_blocks &= _CYHAL_SCB_CHECK_AFFILIATION(rts, cyhal_pin_map_scb_uart_rts);
657     }
658     // One (or more) pin does not belong to any SCB instance or all corresponding SCB instances
659     // are reserved
660     if (0 == pins_blocks)
661     {
662         cyhal_system_critical_section_exit(saved_intr_status);
663         return CYHAL_UART_RSLT_ERR_INVALID_PIN;
664     }
665 
666     uint8_t found_block_idx = 0;
667     while(((pins_blocks >> found_block_idx) & 0x1) == 0)
668     {
669         found_block_idx++;
670     }
671 
672     cyhal_resource_inst_t uart_rsc = { CYHAL_RSC_SCB, found_block_idx, 0 };
673 
674     // Reserve the UART
675     const cyhal_resource_pin_mapping_t *tx_map = _CYHAL_SCB_FIND_MAP_BLOCK(tx, cyhal_pin_map_scb_uart_tx, &uart_rsc);
676     const cyhal_resource_pin_mapping_t *rx_map = _CYHAL_SCB_FIND_MAP_BLOCK(rx, cyhal_pin_map_scb_uart_rx, &uart_rsc);
677     const cyhal_resource_pin_mapping_t *cts_map = _CYHAL_SCB_FIND_MAP_BLOCK(cts, cyhal_pin_map_scb_uart_cts, &uart_rsc);
678     const cyhal_resource_pin_mapping_t *rts_map = _CYHAL_SCB_FIND_MAP_BLOCK(rts, cyhal_pin_map_scb_uart_rts, &uart_rsc);
679 
680     if ((NC != tx && NULL == tx_map) || (NC != rx && NULL == rx_map) || (NC != cts && NULL == cts_map) || (NC != rts && NULL == rts_map))
681     {
682         // Should never enter here, as _CYHAL_SCB_CHECK_AFFILIATION above garantee that all of these pin maps exist.
683         CY_ASSERT(false);
684     }
685 
686     cyhal_resource_inst_t rsc_to_reserve = { CYHAL_RSC_SCB, _cyhal_scb_get_block_index(found_block_idx), 0 };
687     result = cyhal_hwmgr_reserve(&rsc_to_reserve);
688     cyhal_system_critical_section_exit(saved_intr_status);
689     if (CY_RSLT_SUCCESS != result)
690     {
691         return result;
692     }
693 
694     obj->resource = uart_rsc;
695 
696     // reserve the TX pin
697     if ((result == CY_RSLT_SUCCESS) && NC != tx)
698     {
699         result = _cyhal_utils_reserve_and_connect(tx_map, (uint8_t)CYHAL_PIN_MAP_DRIVE_MODE_SCB_UART_TX);
700         if (result == CY_RSLT_SUCCESS)
701         {
702             obj->pin_tx = tx;
703         }
704     }
705 
706     // reserve the RX pin
707     if ((result == CY_RSLT_SUCCESS) && NC != rx)
708     {
709         result = _cyhal_utils_reserve_and_connect(rx_map, (uint8_t)CYHAL_PIN_MAP_DRIVE_MODE_SCB_UART_RX);
710         if (result == CY_RSLT_SUCCESS)
711         {
712             obj->pin_rx = rx;
713         }
714     }
715 
716     if ((result == CY_RSLT_SUCCESS) && (NULL != cts_map))
717     {
718         // reserve the CTS pin
719         result = _cyhal_utils_reserve_and_connect(cts_map, (uint8_t)CYHAL_PIN_MAP_DRIVE_MODE_SCB_UART_CTS);
720         if (result == CY_RSLT_SUCCESS)
721         {
722             obj->cts_enabled = true;
723             obj->pin_cts = cts;
724         }
725     }
726 
727     if ((result == CY_RSLT_SUCCESS) && (NULL != rts_map))
728     {
729         // reserve the RTS pin
730         result = _cyhal_utils_reserve_and_connect(rts_map, (uint8_t)CYHAL_PIN_MAP_DRIVE_MODE_SCB_UART_RTS);
731         if (result == CY_RSLT_SUCCESS)
732         {
733             obj->rts_enabled = true;
734             obj->pin_rts = rts;
735         }
736     }
737 
738     if (result == CY_RSLT_SUCCESS)
739     {
740         if (clk == NULL)
741         {
742             result = _cyhal_utils_allocate_clock(&(obj->clock), &obj->resource, CYHAL_CLOCK_BLOCK_PERIPHERAL_16BIT, true);
743             obj->is_clock_owned = (CY_RSLT_SUCCESS == result);
744         }
745         else
746         {
747             obj->is_clock_owned = false;
748             obj->clock = *clk;
749         }
750     }
751 
752     if (result == CY_RSLT_SUCCESS)
753     {
754         result = _cyhal_utils_peri_pclk_assign_divider(
755             _cyhal_scb_get_clock_index(obj->resource.block_num), &(obj->clock));
756     }
757 
758     return result;
759 }
760 
_cyhal_uart_init_hw(cyhal_uart_t * obj)761 static cy_rslt_t _cyhal_uart_init_hw(cyhal_uart_t *obj)
762 {
763     uint8_t scb_arr_index = _cyhal_scb_get_block_index(obj->resource.block_num);
764     obj->base = _CYHAL_SCB_BASE_ADDRESSES[scb_arr_index];
765 
766     #if defined(COMPONENT_CAT5)
767     // CAT5 SCBs must be enabled prior to UART init
768     if(obj->is_clock_owned == true)
769     {
770         cyhal_uart_set_baud(obj, CYHAL_UART_DEFAULT_BAUD, NULL);
771     }
772     else
773     {
774         // Ensure user-provided clock is enabled
775         Cy_SCB_EnableClock(obj->base, cyhal_clock_get_frequency(&(obj->clock)), false);
776     }
777     #endif
778 
779     cy_rslt_t result = (cy_rslt_t) Cy_SCB_UART_Init(obj->base, &(obj->config), &(obj->context));
780 
781     if (CY_RSLT_SUCCESS == result)
782     {
783         obj->callback_data.callback = NULL;
784         obj->callback_data.callback_arg = NULL;
785         obj->irq_cause = CYHAL_UART_IRQ_NONE;
786 
787         #if defined (COMPONENT_CAT5)
788             Cy_SCB_RegisterInterruptCallback(obj->base, _cyhal_irq_cb[_CYHAL_SCB_IRQ_N[scb_arr_index]]);
789             Cy_SCB_EnableInterrupt(obj->base);
790         #endif
791 
792         _cyhal_irq_register(_CYHAL_SCB_IRQ_N[scb_arr_index], CYHAL_ISR_PRIORITY_DEFAULT, (cy_israddress)_cyhal_uart_irq_handler);
793         _cyhal_irq_enable(_CYHAL_SCB_IRQ_N[scb_arr_index]);
794 
795         _cyhal_scb_update_instance_data(obj->resource.block_num, (void*)obj, &_cyhal_uart_pm_callback_instance);
796 
797         Cy_SCB_UART_Enable(obj->base);
798     }
799 
800     return result;
801 }
802 
cyhal_uart_init(cyhal_uart_t * obj,cyhal_gpio_t tx,cyhal_gpio_t rx,cyhal_gpio_t cts,cyhal_gpio_t rts,const cyhal_clock_t * clk,const cyhal_uart_cfg_t * cfg)803 cy_rslt_t cyhal_uart_init(cyhal_uart_t *obj, cyhal_gpio_t tx, cyhal_gpio_t rx, cyhal_gpio_t cts, cyhal_gpio_t rts,
804                           const cyhal_clock_t *clk, const cyhal_uart_cfg_t *cfg)
805 {
806     CY_ASSERT(NULL != obj);
807     memset(obj, 0, sizeof(cyhal_uart_t));
808 
809     obj->dc_configured = false;
810     #if (CYHAL_DRIVER_AVAILABLE_DMA)
811     obj->async_mode = CYHAL_ASYNC_SW;
812     /* resource type used to check if DMAs have been initialized before freeing */
813     obj->dma_tx.resource.type = CYHAL_RSC_INVALID;
814     obj->dma_rx.resource.type = CYHAL_RSC_INVALID;
815     #endif
816     obj->baud_rate = CYHAL_UART_DEFAULT_BAUD;
817     cy_rslt_t result = _cyhal_uart_setup_resources(obj, tx, rx, cts, rts, clk);
818 
819     if (CY_RSLT_SUCCESS == result)
820     {
821         obj->config = _cyhal_uart_default_config;
822         obj->config.enableCts = obj->cts_enabled;
823 
824         if (cfg != NULL)
825         {
826             obj->config.dataWidth = cfg->data_bits;
827             obj->config.stopBits = _cyhal_uart_convert_stopbits((uint8_t)cfg->stop_bits);
828             obj->config.parity = _cyhal_uart_convert_parity(cfg->parity);
829         }
830 
831         result = _cyhal_uart_init_hw(obj);
832     }
833 
834     if (CY_RSLT_SUCCESS == result)
835     {
836         if ((cfg != NULL) && (cfg->rx_buffer != NULL))
837         {
838             cyhal_uart_config_software_buffer(obj, cfg->rx_buffer, cfg->rx_buffer_size);
839         }
840 
841         #if !defined(COMPONENT_CAT5)
842         // This was done earlier for CAT5
843         if (obj->is_clock_owned)
844         {
845             result = cyhal_uart_set_baud(obj, obj->baud_rate, NULL);
846         }
847         #endif
848 
849         #if (CYHAL_DRIVER_AVAILABLE_DMA)
850         /* Use default FIFO level until user changes it */
851         obj->user_fifo_level = (Cy_SCB_GetFifoSize(obj->base) / 2);
852         #endif
853     }
854 
855     if (CY_RSLT_SUCCESS != result)
856     {
857         cyhal_uart_free(obj);
858     }
859     return result;
860 }
861 
cyhal_uart_init_cfg(cyhal_uart_t * obj,const cyhal_uart_configurator_t * cfg)862 cy_rslt_t cyhal_uart_init_cfg(cyhal_uart_t *obj, const cyhal_uart_configurator_t *cfg)
863 {
864     CY_ASSERT(NULL != obj);
865     CY_ASSERT(NULL != cfg);
866     CY_ASSERT(NULL != cfg->config);
867     memset(obj, 0, sizeof(cyhal_uart_t));
868 
869     obj->resource = *cfg->resource;
870     obj->clock = *cfg->clock;
871     obj->is_clock_owned = false;
872     obj->pin_tx = cfg->gpios.pin_tx;
873     obj->pin_rts = cfg->gpios.pin_rts;
874     obj->pin_cts = cfg->gpios.pin_cts;
875     obj->dc_configured = true;
876     obj->cts_enabled = cfg->config->enableCts;
877     obj->rts_enabled = (NC != cfg->gpios.pin_rts);
878     #if (CYHAL_DRIVER_AVAILABLE_DMA)
879     obj->async_mode = CYHAL_ASYNC_SW;
880     obj->dma_tx.resource.type = CYHAL_RSC_INVALID;
881     obj->dma_rx.resource.type = CYHAL_RSC_INVALID;
882     #endif
883 
884     obj->config = *cfg->config;
885     cy_rslt_t result = _cyhal_uart_init_hw(obj);
886 
887     #if (CYHAL_DRIVER_AVAILABLE_DMA)
888     if(result == CY_RSLT_SUCCESS)
889     {
890         /* Use default FIFO level until user changes it */
891         obj->user_fifo_level = (Cy_SCB_GetFifoSize(obj->base) / 2);
892     }
893     #endif
894 
895     return result;
896 }
897 
cyhal_uart_free(cyhal_uart_t * obj)898 void cyhal_uart_free(cyhal_uart_t *obj)
899 {
900     CY_ASSERT(NULL != obj);
901 
902     if (NULL != obj->base)
903     {
904         Cy_SCB_UART_Disable(obj->base, &obj->context);
905         Cy_SCB_UART_DeInit(obj->base);
906         obj->base = NULL;
907     }
908 
909     if (obj->resource.type != CYHAL_RSC_INVALID)
910     {
911         uint8_t scb_arr_index = _cyhal_scb_get_block_index(obj->resource.block_num);
912         _cyhal_system_irq_t irqn = _CYHAL_SCB_IRQ_N[scb_arr_index];
913         _cyhal_irq_free(irqn);
914 
915         _cyhal_scb_update_instance_data(obj->resource.block_num, NULL, NULL);
916 
917         if (false == obj->dc_configured)
918         {
919             cyhal_resource_inst_t rsc_to_free = { CYHAL_RSC_SCB, _cyhal_scb_get_block_index(obj->resource.block_num), obj->resource.channel_num };
920             cyhal_hwmgr_free(&(rsc_to_free));
921         }
922 
923         obj->resource.type = CYHAL_RSC_INVALID;
924     }
925 
926     if (false == obj->dc_configured)
927     {
928         _cyhal_utils_release_if_used(&(obj->pin_rx));
929         _cyhal_utils_release_if_used(&(obj->pin_tx));
930         _cyhal_utils_release_if_used(&(obj->pin_rts));
931         _cyhal_utils_release_if_used(&(obj->pin_cts));
932 
933         if (obj->is_clock_owned)
934         {
935             cyhal_clock_free(&(obj->clock));
936         }
937     }
938 
939     #if (CYHAL_DRIVER_AVAILABLE_DMA)
940     if(obj->async_mode == CYHAL_ASYNC_DMA)
941     {
942         if(CYHAL_RSC_INVALID != obj->dma_tx.resource.type)
943         {
944             cyhal_dma_free(&obj->dma_tx);
945             obj->dma_tx.resource.type = CYHAL_RSC_INVALID;
946         }
947         if(CYHAL_RSC_INVALID != obj->dma_rx.resource.type)
948         {
949             cyhal_dma_free(&obj->dma_rx);
950             obj->dma_rx.resource.type = CYHAL_RSC_INVALID;
951         }
952     }
953     #endif
954 }
955 
cyhal_uart_set_baud(cyhal_uart_t * obj,uint32_t baudrate,uint32_t * actualbaud)956 cy_rslt_t cyhal_uart_set_baud(cyhal_uart_t *obj, uint32_t baudrate, uint32_t *actualbaud)
957 {
958     cy_rslt_t status;
959     if( obj->baud_rate != baudrate)
960     {
961         obj->baud_rate = baudrate;
962     }
963 
964     if (obj->is_clock_owned)
965     {
966         uint8_t oversample_value;
967         uint32_t calculated_baud;
968         uint32_t divider;
969 
970         Cy_SCB_UART_Disable(obj->base, NULL);
971         status = cyhal_clock_set_enabled(&(obj->clock), false, false);
972         if(status != CY_RSLT_SUCCESS)
973         {
974             Cy_SCB_UART_Enable(obj->base);
975             return status;
976         }
977 
978         oversample_value = _cyhal_uart_best_oversample(&(obj->resource), baudrate);
979         obj->config.oversample = oversample_value;
980 
981         divider = _cyhal_utils_divider_value(&(obj->resource), baudrate * oversample_value, 0);
982         #if defined(COMPONENT_CAT5)
983         if( !_cyhal_clock_is_divider_valid(&(obj->resource),divider))
984         {
985             Cy_SCB_UART_Enable(obj->base);
986             status = CYHAL_UART_RSLT_ERR_UNSUPPORTED_CONFIG;
987             return status;
988         }
989         #endif
990         /* Set baud rate */
991         #if defined (COMPONENT_CAT5)
992         status = _cyhal_utils_peri_pclk_set_freq(0, &(obj->clock), baudrate, oversample_value);
993         #else
994         status = cyhal_clock_set_divider(&(obj->clock), divider);
995         #endif
996         if(status != CY_RSLT_SUCCESS)
997         {
998             cyhal_clock_set_enabled(&(obj->clock), true, false);
999             Cy_SCB_UART_Enable(obj->base);
1000             return status;
1001         }
1002 
1003         calculated_baud = _cyhal_uart_actual_baud(&(obj->resource), divider, oversample_value);
1004 
1005         if (actualbaud != NULL)
1006             *actualbaud = calculated_baud;
1007         uint32_t baud_difference = _cyhal_uart_baud_perdif(baudrate, calculated_baud);
1008         if (baud_difference > CYHAL_UART_MAX_BAUD_PERCENT_DIFFERENCE)
1009             status = CY_RSLT_WRN_CSP_UART_BAUD_TOLERANCE;
1010 
1011         status = cyhal_clock_set_enabled(&(obj->clock), true, false);
1012 
1013         /* Configure the UART interface */
1014         #if (CY_IP_MXSCB_VERSION >= 2) || (CY_IP_MXS22SCB_VERSION >= 1)
1015         uint32_t mem_width = (obj->config.dataWidth <= CY_SCB_BYTE_WIDTH)
1016             #if defined(COMPONENT_CAT1) || defined (COMPONENT_CAT5)
1017             ? CY_SCB_MEM_WIDTH_BYTE : CY_SCB_MEM_WIDTH_HALFWORD;
1018             #elif defined(COMPONENT_CAT2)
1019             ? CY_SCB_CTRL_MEM_WIDTH_BYTE : CY_SCB_CTRL_MEM_WIDTH_HALFWORD;
1020             #endif
1021 
1022         SCB_CTRL(obj->base) = _BOOL2FLD(SCB_CTRL_ADDR_ACCEPT, obj->config.acceptAddrInFifo)     |
1023                     _BOOL2FLD(SCB_CTRL_MEM_WIDTH, mem_width)                                    |
1024                     _VAL2FLD(SCB_CTRL_OVS, oversample_value - 1)                                |
1025                     _VAL2FLD(SCB_CTRL_MODE, CY_SCB_CTRL_MODE_UART);
1026         #else /* Older versions of the block */
1027         SCB_CTRL(obj->base) = _BOOL2FLD(SCB_CTRL_ADDR_ACCEPT, obj->config.acceptAddrInFifo)     |
1028                     _BOOL2FLD(SCB_CTRL_BYTE_MODE, (obj->config.dataWidth <= CY_SCB_BYTE_WIDTH)) |
1029                     _VAL2FLD(SCB_CTRL_OVS, oversample_value - 1)                                |
1030                     _VAL2FLD(SCB_CTRL_MODE, CY_SCB_CTRL_MODE_UART);
1031         #endif
1032 
1033         Cy_SCB_UART_Enable(obj->base);
1034     }
1035     else
1036     {
1037         /* Not able to make changes in user-provided clock */
1038         status = CYHAL_UART_RSLT_CLOCK_ERROR;
1039     }
1040 
1041     return status;
1042 }
1043 
cyhal_uart_configure(cyhal_uart_t * obj,const cyhal_uart_cfg_t * cfg)1044 cy_rslt_t cyhal_uart_configure(cyhal_uart_t *obj, const cyhal_uart_cfg_t *cfg)
1045 {
1046     CY_ASSERT(NULL != obj);
1047     CY_ASSERT(NULL != cfg);
1048     Cy_SCB_UART_Disable(obj->base, NULL);
1049     obj->config.dataWidth = cfg->data_bits;
1050     obj->config.stopBits = _cyhal_uart_convert_stopbits((uint8_t)cfg->stop_bits);
1051     obj->config.parity = _cyhal_uart_convert_parity(cfg->parity);
1052     obj->config.enableCts = obj->cts_enabled;
1053     // Do not pass obj->context here because Cy_SCB_UART_Init will destroy it
1054     Cy_SCB_UART_Init(obj->base, &(obj->config), NULL);
1055     Cy_SCB_UART_Enable(obj->base);
1056     return CY_RSLT_SUCCESS;
1057 }
1058 
cyhal_uart_getc(cyhal_uart_t * obj,uint8_t * value,uint32_t timeout)1059 cy_rslt_t cyhal_uart_getc(cyhal_uart_t *obj, uint8_t *value, uint32_t timeout)
1060 {
1061     if (_cyhal_scb_pm_transition_pending())
1062         return CYHAL_SYSPM_RSLT_ERR_PM_PENDING;
1063 
1064     uint32_t read_value = Cy_SCB_UART_Get(obj->base);
1065     uint32_t timeoutTicks = timeout;
1066     while (read_value == CY_SCB_UART_RX_NO_DATA)
1067     {
1068         if(timeout != 0UL)
1069         {
1070             if(timeoutTicks > 0UL)
1071             {
1072                 cyhal_system_delay_ms(1);
1073                 timeoutTicks--;
1074             }
1075             else
1076             {
1077                 return CY_RSLT_ERR_CSP_UART_GETC_TIMEOUT;
1078             }
1079         }
1080         read_value = Cy_SCB_UART_Get(obj->base);
1081     }
1082     *value = (uint8_t)read_value;
1083     return CY_RSLT_SUCCESS;
1084 }
1085 
cyhal_uart_putc(cyhal_uart_t * obj,uint32_t value)1086 cy_rslt_t cyhal_uart_putc(cyhal_uart_t *obj, uint32_t value)
1087 {
1088     if (_cyhal_scb_pm_transition_pending())
1089         return CYHAL_SYSPM_RSLT_ERR_PM_PENDING;
1090 
1091     uint32_t count = 0;
1092     while (count == 0)
1093     {
1094         count = Cy_SCB_UART_Put(obj->base, value);
1095     }
1096 
1097     return CY_RSLT_SUCCESS;
1098 }
1099 
cyhal_uart_readable(cyhal_uart_t * obj)1100 uint32_t cyhal_uart_readable(cyhal_uart_t *obj)
1101 {
1102     uint32_t number_available = Cy_SCB_UART_GetNumInRxFifo(obj->base);
1103     if(obj->context.rxRingBuf != NULL)
1104     {
1105         number_available += Cy_SCB_UART_GetNumInRingBuffer(obj->base, &(obj->context));
1106     }
1107     return number_available;
1108 }
1109 
cyhal_uart_writable(cyhal_uart_t * obj)1110 uint32_t cyhal_uart_writable(cyhal_uart_t *obj)
1111 {
1112     return Cy_SCB_GetFifoSize(obj->base) - Cy_SCB_GetNumInTxFifo(obj->base);
1113 }
1114 
cyhal_uart_clear(cyhal_uart_t * obj)1115 cy_rslt_t cyhal_uart_clear(cyhal_uart_t *obj)
1116 {
1117     Cy_SCB_UART_ClearRxFifo(obj->base);
1118     Cy_SCB_UART_ClearTxFifo(obj->base);
1119 
1120     if(obj->context.rxRingBuf != NULL)
1121     {
1122         Cy_SCB_UART_ClearRingBuffer(obj->base, &(obj->context));
1123     }
1124 
1125     return CY_RSLT_SUCCESS;
1126 }
1127 
cyhal_uart_enable_flow_control(cyhal_uart_t * obj,bool enable_cts,bool enable_rts)1128 cy_rslt_t cyhal_uart_enable_flow_control(cyhal_uart_t *obj, bool enable_cts, bool enable_rts)
1129 {
1130     cy_rslt_t result = CY_RSLT_SUCCESS;
1131 
1132     if (obj->pin_cts != NC)
1133     {
1134         if (enable_cts && (false == obj->cts_enabled))
1135         {
1136             const cyhal_resource_pin_mapping_t *cts_map = _CYHAL_SCB_FIND_MAP_BLOCK(obj->pin_cts, cyhal_pin_map_scb_uart_cts, &obj->resource);
1137             if (false == obj->dc_configured)
1138             {
1139                 result = _cyhal_utils_reserve_and_connect(cts_map, (uint8_t)CYHAL_PIN_MAP_DRIVE_MODE_SCB_UART_CTS);
1140             }
1141             else
1142             {
1143                 result = cyhal_connect_pin(cts_map, (uint8_t)CYHAL_PIN_MAP_DRIVE_MODE_SCB_UART_CTS);
1144             }
1145             if (CY_RSLT_SUCCESS == result)
1146             {
1147                 Cy_SCB_UART_EnableCts(obj->base);
1148                 obj->cts_enabled = true;
1149             }
1150         }
1151         else if (!enable_cts)
1152         {
1153             if (false == obj->dc_configured)
1154             {
1155                 _cyhal_utils_disconnect_and_free(obj->pin_cts);
1156             }
1157             else
1158             {
1159                 result = cyhal_disconnect_pin(obj->pin_cts);
1160             }
1161             if (CY_RSLT_SUCCESS == result)
1162             {
1163                 Cy_SCB_UART_DisableCts(obj->base);
1164                 obj->cts_enabled = false;
1165             }
1166         }
1167     }
1168 
1169     if ((CY_RSLT_SUCCESS == result) && (obj->pin_rts != NC))
1170     {
1171         if (enable_rts && (false == obj->rts_enabled))
1172         {
1173             const cyhal_resource_pin_mapping_t *rts_map = _CYHAL_SCB_FIND_MAP_BLOCK(obj->pin_rts, cyhal_pin_map_scb_uart_rts, &obj->resource);
1174             if (false == obj->dc_configured)
1175             {
1176                 result = _cyhal_utils_reserve_and_connect(rts_map, (uint8_t)CYHAL_PIN_MAP_DRIVE_MODE_SCB_UART_RTS);
1177             }
1178             else
1179             {
1180                 result = cyhal_connect_pin(rts_map, (uint8_t)CYHAL_PIN_MAP_DRIVE_MODE_SCB_UART_RTS);
1181             }
1182             obj->rts_enabled = (CY_RSLT_SUCCESS == result);
1183         }
1184         else if (!enable_rts)
1185         {
1186             if (false == obj->dc_configured)
1187             {
1188                 _cyhal_utils_disconnect_and_free(obj->pin_rts);
1189             }
1190             else
1191             {
1192                 result = cyhal_disconnect_pin(obj->pin_rts);
1193             }
1194             if (CY_RSLT_SUCCESS == result)
1195             {
1196                 obj->rts_enabled = false;
1197             }
1198         }
1199     }
1200 
1201     return result;
1202 }
1203 
cyhal_uart_write(cyhal_uart_t * obj,void * tx,size_t * tx_length)1204 cy_rslt_t cyhal_uart_write(cyhal_uart_t *obj, void *tx, size_t *tx_length)
1205 {
1206     if (_cyhal_scb_pm_transition_pending())
1207         return CYHAL_SYSPM_RSLT_ERR_PM_PENDING;
1208 
1209     *tx_length = Cy_SCB_UART_PutArray(obj->base, tx, *tx_length);
1210 
1211     return CY_RSLT_SUCCESS;
1212 }
1213 
cyhal_uart_read(cyhal_uart_t * obj,void * rx,size_t * rx_length)1214 cy_rslt_t cyhal_uart_read(cyhal_uart_t *obj, void *rx, size_t *rx_length)
1215 {
1216     if (_cyhal_scb_pm_transition_pending())
1217         return CYHAL_SYSPM_RSLT_ERR_PM_PENDING;
1218 
1219     *rx_length = Cy_SCB_UART_GetArray(obj->base, rx, *rx_length);
1220     return CY_RSLT_SUCCESS;
1221 }
1222 
cyhal_uart_set_async_mode(cyhal_uart_t * obj,cyhal_async_mode_t mode,uint8_t dma_priority)1223 cy_rslt_t cyhal_uart_set_async_mode(cyhal_uart_t *obj, cyhal_async_mode_t mode, uint8_t dma_priority)
1224 {
1225     CY_ASSERT(NULL != obj);
1226 
1227     if (_cyhal_scb_pm_transition_pending())
1228         return CYHAL_SYSPM_RSLT_ERR_PM_PENDING;
1229 
1230     #if (CYHAL_DRIVER_AVAILABLE_DMA)
1231     if((obj->async_tx_buff != NULL) || (obj->async_rx_buff != NULL))
1232     {
1233         return CYHAL_DMA_RSLT_ERR_CHANNEL_BUSY;
1234     }
1235     #endif
1236 
1237     cy_rslt_t result = CY_RSLT_SUCCESS;
1238 
1239     if(mode == CYHAL_ASYNC_DMA)
1240     {
1241     #if (CYHAL_DRIVER_AVAILABLE_DMA)
1242         if (obj->context.rxRingBuf != NULL)
1243         {
1244             return CYHAL_UART_RSLT_ERR_UNSUPPORTED_CONFIG;
1245         }
1246         else
1247         {
1248             /* Setup DMA for TX */
1249             if(CYHAL_RSC_INVALID == obj->dma_tx.resource.type && obj->pin_tx != CYHAL_NC_PIN_VALUE)
1250             {
1251                 cyhal_source_t source_tx;
1252                 cyhal_uart_disable_output(obj, CYHAL_UART_OUTPUT_TRIGGER_TX_FIFO_LEVEL_REACHED);
1253                 cyhal_uart_enable_output(obj, CYHAL_UART_OUTPUT_TRIGGER_TX_FIFO_LEVEL_REACHED, &source_tx);
1254                 cyhal_dma_src_t dma_src_tx =
1255                 {
1256                     .source = source_tx,
1257                     .input = CYHAL_DMA_INPUT_TRIGGER_ALL_ELEMENTS,
1258                 };
1259                 /* Set the DMA to correct direction and connect appropriate triggers */
1260                 result = cyhal_dma_init_adv(&(obj->dma_tx), &dma_src_tx, NULL, NULL, dma_priority, CYHAL_DMA_DIRECTION_MEM2PERIPH);
1261             }
1262             cyhal_dma_register_callback(&(obj->dma_tx), &_cyhal_uart_dma_handler_tx, obj);
1263 
1264             /* Setup DMA for RX */
1265             if(CYHAL_RSC_INVALID == obj->dma_rx.resource.type && obj->pin_rx != CYHAL_NC_PIN_VALUE)
1266             {
1267                 cyhal_source_t source_rx;
1268                 cyhal_uart_disable_output(obj, CYHAL_UART_OUTPUT_TRIGGER_RX_FIFO_LEVEL_REACHED);
1269                 cyhal_uart_enable_output(obj, CYHAL_UART_OUTPUT_TRIGGER_RX_FIFO_LEVEL_REACHED, &source_rx);
1270                 cyhal_dma_src_t dma_src_rx =
1271                 {
1272                     .source = source_rx,
1273                     .input = CYHAL_DMA_INPUT_TRIGGER_ALL_ELEMENTS,
1274                 };
1275                 /* Set the DMA to correct direction and connect appropriate triggers */
1276                 cyhal_dma_init_adv(&(obj->dma_rx), &dma_src_rx, NULL, NULL, dma_priority, CYHAL_DMA_DIRECTION_PERIPH2MEM);
1277             }
1278             cyhal_dma_register_callback(&(obj->dma_rx), &_cyhal_uart_dma_handler_rx, obj);
1279 
1280             // Default RTS level (20 or 3) may lead to DMA waiting for more data before reading and RTS waiting for data read
1281             // before allowing more.  Raise RTS to near-max to ensure DMA runs smoothest
1282             Cy_SCB_UART_SetRtsFifoLevel(obj->base, Cy_SCB_GetFifoSize(obj->base) - 1);
1283         }
1284     #else
1285         CY_UNUSED_PARAMETER(obj);
1286         CY_UNUSED_PARAMETER(dma_priority);
1287         result = CYHAL_UART_RSLT_ERR_UNSUPPORTED_OPERATION; // DMA not supported
1288     #endif
1289     }
1290     else
1291     {
1292     #if (CYHAL_DRIVER_AVAILABLE_DMA)
1293         /* Free the DMA instances if we reserved them but don't need them anymore */
1294         if(CYHAL_RSC_INVALID != obj->dma_tx.resource.type)
1295         {
1296             cyhal_dma_free(&obj->dma_tx);
1297             obj->dma_tx.resource.type = CYHAL_RSC_INVALID;
1298         }
1299         if(CYHAL_RSC_INVALID != obj->dma_rx.resource.type)
1300         {
1301             cyhal_dma_free(&obj->dma_rx);
1302             obj->dma_rx.resource.type = CYHAL_RSC_INVALID;
1303         }
1304 
1305         // Restore RTS values for SW mode.  Values taken from _cyhal_uart_default_config
1306         Cy_SCB_UART_SetRtsFifoLevel(obj->base, _cyhal_uart_default_config.rtsRxFifoLevel);
1307     #endif
1308     }
1309 
1310     #if (CYHAL_DRIVER_AVAILABLE_DMA)
1311     if(CY_RSLT_SUCCESS == result)
1312     {
1313         obj->async_mode = mode;
1314     }
1315     #endif
1316     return result;
1317 }
1318 
1319 #if (CYHAL_DRIVER_AVAILABLE_DMA)
_cyhal_uart_dma_write_async(cyhal_uart_t * obj)1320 cy_rslt_t _cyhal_uart_dma_write_async(cyhal_uart_t *obj)
1321 {
1322     if(cyhal_dma_is_busy(&(obj->dma_tx)))
1323     {
1324         return CYHAL_DMA_RSLT_ERR_CHANNEL_BUSY;
1325     }
1326 
1327     CY_ASSERT(NULL != obj->async_tx_buff);
1328 
1329     /* If tranfer exceeds FIFO level, break into many small transfers */
1330     uint32_t length = obj->async_tx_length;
1331     if(length >= Cy_SCB_GetFifoSize(obj->base))
1332     {
1333         length = (Cy_SCB_GetFifoSize(obj->base) / 2);
1334     }
1335 
1336     uint32_t mem_width = (obj->config.dataWidth <= CY_SCB_BYTE_WIDTH) ? 8 : 16;
1337     cyhal_dma_cfg_t dma_config =
1338     {
1339         .src_addr = (uint32_t)obj->async_tx_buff,
1340         .src_increment = 1,
1341         .dst_addr = (uint32_t)&obj->base->TX_FIFO_WR,
1342         .dst_increment = 0,
1343         .length = length,
1344         .transfer_width = mem_width,
1345         .burst_size = 0,
1346         .action = CYHAL_DMA_TRANSFER_FULL_DISABLE,
1347     };
1348 
1349     /* Move next src up and keep track of how much data has already been sent */
1350     uint32_t savedIntrStatus = cyhal_system_critical_section_enter();
1351     obj->async_tx_buff = ((length * (mem_width / 8)) + (char*)obj->async_tx_buff);
1352     obj->async_tx_length -= length;
1353     cyhal_system_critical_section_exit(savedIntrStatus);
1354 
1355     /* Transmit once UART is full */
1356     cy_rslt_t result = cyhal_uart_set_fifo_level(obj, CYHAL_UART_FIFO_TX, Cy_SCB_GetFifoSize(obj->base) - length);
1357     if(result == CY_RSLT_SUCCESS)
1358     {
1359         cyhal_dma_enable_event(&(obj->dma_tx), CYHAL_DMA_TRANSFER_COMPLETE, CYHAL_DMA_PRIORITY_DEFAULT, true);
1360         #if defined(CY_IP_M4CPUSS_DMA) || defined(CY_IP_M7CPUSS_DMA) || defined(CY_IP_MXDW)
1361         obj->dma_tx.descriptor_config.dw.retrigger = CY_DMA_WAIT_FOR_REACT;
1362         #endif
1363         result = cyhal_dma_configure(&(obj->dma_tx), &dma_config);
1364     }
1365 
1366     #if defined (__DCACHE_PRESENT) && (__DCACHE_PRESENT == 1U)
1367     SCB_CleanDCache_by_Addr((void *)obj->async_tx_buff, (length * (mem_width / 8)));
1368     #endif /* defined (__DCACHE_PRESENT) && (__DCACHE_PRESENT == 1U) */
1369 
1370     if(result == CY_RSLT_SUCCESS)
1371     {
1372         result = cyhal_dma_enable(&(obj->dma_tx));
1373     }
1374     return result;
1375 }
1376 #endif
1377 
cyhal_uart_write_async(cyhal_uart_t * obj,void * tx,size_t length)1378 cy_rslt_t cyhal_uart_write_async(cyhal_uart_t *obj, void *tx, size_t length)
1379 {
1380     if (_cyhal_scb_pm_transition_pending())
1381         return CYHAL_SYSPM_RSLT_ERR_PM_PENDING;
1382 
1383     cy_rslt_t result;
1384     #if (CYHAL_DRIVER_AVAILABLE_DMA)
1385     if(obj->async_mode == CYHAL_ASYNC_DMA)
1386     {
1387         uint32_t savedIntrStatus = cyhal_system_critical_section_enter();
1388         obj->async_tx_buff = tx;
1389         obj->async_tx_length = length;
1390         result = _cyhal_uart_dma_write_async(obj);
1391         cyhal_system_critical_section_exit(savedIntrStatus);
1392     }
1393     else
1394     #endif
1395     {
1396         result = Cy_SCB_UART_Transmit(obj->base, tx, length, &(obj->context));
1397     }
1398     return result;
1399 }
1400 
1401 #if (CYHAL_DRIVER_AVAILABLE_DMA)
_cyhal_uart_dma_read_async(cyhal_uart_t * obj)1402 cy_rslt_t _cyhal_uart_dma_read_async(cyhal_uart_t *obj)
1403 {
1404     if(cyhal_dma_is_busy(&(obj->dma_rx)))
1405     {
1406         return CYHAL_DMA_RSLT_ERR_CHANNEL_BUSY;
1407     }
1408 
1409     CY_ASSERT(NULL != obj->async_rx_buff);
1410 
1411     /* If tranfer exceeds FIFO size, break into many small transfers */
1412     uint32_t length = obj->async_rx_length;
1413     if(length >= Cy_SCB_GetFifoSize(obj->base))
1414     {
1415         length = (Cy_SCB_GetFifoSize(obj->base) / 2);
1416     }
1417 
1418     uint32_t mem_width = (obj->config.dataWidth <= CY_SCB_BYTE_WIDTH) ? 8 : 16;
1419     cyhal_dma_cfg_t dma_config =
1420     {
1421         .src_addr = (uint32_t)(&(obj->base->RX_FIFO_RD)),
1422         .src_increment = 0,
1423         .dst_addr = (uint32_t)obj->async_rx_buff,
1424         .dst_increment = 1,
1425         .length = length,
1426         .transfer_width = mem_width,
1427         .burst_size = 0,
1428         .action = CYHAL_DMA_TRANSFER_FULL_DISABLE,
1429     };
1430 
1431     /* Move next src up and keep track of how much data has already been sent */
1432     uint32_t savedIntrStatus = cyhal_system_critical_section_enter();
1433     obj->async_rx_buff = ((length * (mem_width / 8)) + (char*)obj->async_rx_buff);
1434     obj->async_rx_length -= length;
1435     cyhal_system_critical_section_exit(savedIntrStatus);
1436 
1437     /* Read once UART is full */
1438     cy_rslt_t result = CY_RSLT_SUCCESS;
1439     result = _cyhal_scb_set_fifo_level(obj->base, (cyhal_scb_fifo_type_t)CYHAL_UART_FIFO_RX, length - 1);
1440     if(result == CY_RSLT_SUCCESS)
1441     {
1442         cyhal_dma_enable_event(&(obj->dma_rx), CYHAL_DMA_TRANSFER_COMPLETE, CYHAL_DMA_PRIORITY_DEFAULT, true);
1443         #if defined(CY_IP_M4CPUSS_DMA) || defined(CY_IP_M7CPUSS_DMA) || defined(CY_IP_MXDW)
1444         obj->dma_tx.descriptor_config.dw.retrigger = CY_DMA_WAIT_FOR_REACT;
1445         #endif
1446         result = cyhal_dma_configure(&(obj->dma_rx), &dma_config);
1447     }
1448 
1449     #if defined (__DCACHE_PRESENT) && (__DCACHE_PRESENT == 1U)
1450     SCB_InvalidateDCache_by_Addr((void *)obj->async_rx_buff, (length * (mem_width / 8)));
1451     #endif /* defined (__DCACHE_PRESENT) && (__DCACHE_PRESENT == 1U) */
1452 
1453     if(result == CY_RSLT_SUCCESS)
1454     {
1455         result = cyhal_dma_enable(&(obj->dma_rx));
1456     }
1457 
1458     return result;
1459 }
1460 #endif
1461 
cyhal_uart_read_async(cyhal_uart_t * obj,void * rx,size_t length)1462 cy_rslt_t cyhal_uart_read_async(cyhal_uart_t *obj, void *rx, size_t length)
1463 {
1464     if (_cyhal_scb_pm_transition_pending())
1465         return CYHAL_SYSPM_RSLT_ERR_PM_PENDING;
1466 
1467     cy_rslt_t result = CY_RSLT_SUCCESS;
1468     #if (CYHAL_DRIVER_AVAILABLE_DMA)
1469     if(obj->async_mode == CYHAL_ASYNC_DMA)
1470     {
1471         uint32_t savedIntrStatus = cyhal_system_critical_section_enter();
1472         obj->async_rx_buff = rx;
1473         obj->async_rx_length = length;
1474         cyhal_system_critical_section_exit(savedIntrStatus);
1475 
1476         result = _cyhal_uart_dma_read_async(obj);
1477     }
1478     else
1479     #endif
1480     {
1481         result = Cy_SCB_UART_Receive(obj->base, rx, length, &(obj->context));
1482     }
1483     return result;
1484 }
1485 
cyhal_uart_is_tx_active(cyhal_uart_t * obj)1486 bool cyhal_uart_is_tx_active(cyhal_uart_t *obj)
1487 {
1488     return (0UL != (obj->context.txStatus & CY_SCB_UART_TRANSMIT_ACTIVE)) || !Cy_SCB_IsTxComplete(obj->base)
1489     #if (CYHAL_DRIVER_AVAILABLE_DMA)
1490             || (obj->async_tx_buff != NULL)
1491     #endif
1492             ;
1493 }
1494 
cyhal_uart_is_rx_active(cyhal_uart_t * obj)1495 bool cyhal_uart_is_rx_active(cyhal_uart_t *obj)
1496 {
1497     return (0UL != (obj->context.rxStatus & CY_SCB_UART_RECEIVE_ACTIVE))
1498     #if (CYHAL_DRIVER_AVAILABLE_DMA)
1499             || (obj->async_rx_buff != NULL)
1500     #endif
1501             ;
1502 }
1503 
cyhal_uart_write_abort(cyhal_uart_t * obj)1504 cy_rslt_t cyhal_uart_write_abort(cyhal_uart_t *obj)
1505 {
1506     #if (CYHAL_DRIVER_AVAILABLE_DMA)
1507     if(obj->async_mode == CYHAL_ASYNC_DMA && (obj->async_tx_buff != NULL))
1508     {
1509         uint32_t savedIntrStatus = cyhal_system_critical_section_enter();
1510         obj->async_tx_buff = NULL;
1511         cyhal_dma_enable_event(&(obj->dma_tx), CYHAL_DMA_TRANSFER_COMPLETE, CYHAL_DMA_PRIORITY_DEFAULT, false);
1512         cyhal_dma_disable(&(obj->dma_tx));
1513         cyhal_system_critical_section_exit(savedIntrStatus);
1514         Cy_SCB_UART_ClearTxFifo(obj->base);
1515     }
1516     else
1517     #endif
1518     {
1519         Cy_SCB_UART_AbortTransmit(obj->base, &(obj->context));
1520     }
1521     return CY_RSLT_SUCCESS;
1522 }
1523 
cyhal_uart_read_abort(cyhal_uart_t * obj)1524 cy_rslt_t cyhal_uart_read_abort(cyhal_uart_t *obj)
1525 {
1526     #if (CYHAL_DRIVER_AVAILABLE_DMA)
1527     if(obj->async_mode == CYHAL_ASYNC_DMA && (obj->async_rx_buff != NULL))
1528     {
1529         uint32_t savedIntrStatus = cyhal_system_critical_section_enter();
1530         obj->async_rx_buff = NULL;
1531         cyhal_dma_enable_event(&(obj->dma_rx), CYHAL_DMA_TRANSFER_COMPLETE, CYHAL_DMA_PRIORITY_DEFAULT, false);
1532         cyhal_dma_disable(&(obj->dma_rx));
1533         cyhal_system_critical_section_exit(savedIntrStatus);
1534     }
1535     else
1536     #endif
1537     {
1538         Cy_SCB_UART_AbortReceive(obj->base, &(obj->context));
1539     }
1540     return CY_RSLT_SUCCESS;
1541 }
1542 
cyhal_uart_register_callback(cyhal_uart_t * obj,cyhal_uart_event_callback_t callback,void * callback_arg)1543 void cyhal_uart_register_callback(cyhal_uart_t *obj, cyhal_uart_event_callback_t callback, void *callback_arg)
1544 {
1545     uint32_t savedIntrStatus = cyhal_system_critical_section_enter();
1546     obj->callback_data.callback = (cy_israddress) callback;
1547     obj->callback_data.callback_arg = callback_arg;
1548     cyhal_system_critical_section_exit(savedIntrStatus);
1549     Cy_SCB_UART_RegisterCallback(obj->base, _cyhal_uart_cb_wrapper, &(obj->context));
1550 
1551     obj->irq_cause = CYHAL_UART_IRQ_NONE;
1552 }
1553 
cyhal_uart_enable_event(cyhal_uart_t * obj,cyhal_uart_event_t event,uint8_t intr_priority,bool enable)1554 void cyhal_uart_enable_event(cyhal_uart_t *obj, cyhal_uart_event_t event, uint8_t intr_priority, bool enable)
1555 {
1556     uint8_t scb_arr_index = _cyhal_scb_get_block_index(obj->resource.block_num);
1557     #if defined(COMPONENT_CAT5)
1558     Cy_SCB_DisableInterrupt(obj->base);
1559     #else
1560     _cyhal_irq_disable(_CYHAL_SCB_IRQ_N[scb_arr_index]);
1561     _cyhal_irq_clear_pending(_CYHAL_SCB_IRQ_N[scb_arr_index]);
1562     #endif
1563 
1564     if (enable)
1565     {
1566         obj->irq_cause |= event;
1567         if (event & CYHAL_UART_IRQ_TX_EMPTY)
1568         {
1569             Cy_SCB_ClearTxInterrupt(obj->base, CY_SCB_UART_TX_EMPTY);
1570             Cy_SCB_SetTxInterruptMask(obj->base, Cy_SCB_GetTxInterruptMask(obj->base) | CY_SCB_UART_TX_EMPTY);
1571         }
1572         if (event & CYHAL_UART_IRQ_TX_DONE)
1573         {
1574             Cy_SCB_ClearTxInterrupt(obj->base, CY_SCB_UART_TX_DONE);
1575             Cy_SCB_SetTxInterruptMask(obj->base, Cy_SCB_GetTxInterruptMask(obj->base) | CY_SCB_UART_TX_DONE);
1576         }
1577         if (event & CYHAL_UART_IRQ_TX_ERROR)
1578         {
1579             // Omit underflow condition as the interrupt perpetually triggers
1580             //Standard mode only uses OVERFLOW irq
1581             if(obj->config.uartMode == CY_SCB_UART_STANDARD)
1582             {
1583                 Cy_SCB_ClearTxInterrupt(obj->base, (CY_SCB_UART_TX_OVERFLOW | CY_SCB_UART_TRANSMIT_ERR));
1584                 Cy_SCB_SetTxInterruptMask(obj->base, Cy_SCB_GetTxInterruptMask(obj->base) | (CY_SCB_UART_TX_OVERFLOW | CY_SCB_UART_TRANSMIT_ERR));
1585             }
1586             //SMARTCARD mode uses OVERFLOW, NACK, and ARB_LOST irq's
1587             else if(obj->config.uartMode == CY_SCB_UART_SMARTCARD)
1588             {
1589                 Cy_SCB_ClearTxInterrupt(obj->base, (CY_SCB_UART_TX_OVERFLOW | CY_SCB_TX_INTR_UART_NACK | CY_SCB_TX_INTR_UART_ARB_LOST | CY_SCB_UART_TRANSMIT_ERR));
1590                 Cy_SCB_SetTxInterruptMask(obj->base, Cy_SCB_GetTxInterruptMask(obj->base) | (CY_SCB_UART_TX_OVERFLOW | CY_SCB_TX_INTR_UART_NACK | CY_SCB_TX_INTR_UART_ARB_LOST | CY_SCB_UART_TRANSMIT_ERR));
1591             }
1592             //LIN Mode only uses OVERFLOW, ARB_LOST irq's
1593             else
1594             {
1595                 Cy_SCB_ClearTxInterrupt(obj->base, (CY_SCB_UART_TX_OVERFLOW | CY_SCB_TX_INTR_UART_ARB_LOST | CY_SCB_UART_TRANSMIT_ERR));
1596                 Cy_SCB_SetTxInterruptMask(obj->base, Cy_SCB_GetTxInterruptMask(obj->base) | (CY_SCB_UART_TX_OVERFLOW | CY_SCB_TX_INTR_UART_ARB_LOST | CY_SCB_UART_TRANSMIT_ERR));
1597             }
1598         }
1599         if (event & CYHAL_UART_IRQ_TX_FIFO)
1600         {
1601             Cy_SCB_ClearTxInterrupt(obj->base, CY_SCB_UART_TX_TRIGGER);
1602             Cy_SCB_SetTxInterruptMask(obj->base, Cy_SCB_GetTxInterruptMask(obj->base) | CY_SCB_UART_TX_TRIGGER);
1603         }
1604 
1605         if (event & CYHAL_UART_IRQ_RX_NOT_EMPTY)
1606         {
1607             Cy_SCB_ClearRxInterrupt(obj->base, CY_SCB_UART_RX_NOT_EMPTY);
1608             Cy_SCB_SetRxInterruptMask(obj->base, Cy_SCB_GetRxInterruptMask(obj->base) | CY_SCB_UART_RX_NOT_EMPTY);
1609         }
1610         if (event & CYHAL_UART_IRQ_RX_ERROR)
1611         {
1612             // Omit underflow condition as the interrupt perpetually triggers.
1613             Cy_SCB_ClearRxInterrupt(obj->base, CY_SCB_UART_RECEIVE_ERR);
1614             Cy_SCB_SetRxInterruptMask(obj->base, Cy_SCB_GetRxInterruptMask(obj->base) | CY_SCB_UART_RECEIVE_ERR);
1615         }
1616         if (event & CYHAL_UART_IRQ_RX_FIFO)
1617         {
1618             Cy_SCB_ClearRxInterrupt(obj->base, CY_SCB_UART_RX_TRIGGER);
1619             Cy_SCB_SetRxInterruptMask(obj->base, Cy_SCB_GetRxInterruptMask(obj->base) | CY_SCB_UART_RX_TRIGGER);
1620         }
1621     }
1622     else
1623     {
1624         obj->irq_cause &= ~event;
1625         if (event & CYHAL_UART_IRQ_TX_EMPTY)
1626         {
1627             Cy_SCB_SetTxInterruptMask(obj->base, Cy_SCB_GetTxInterruptMask(obj->base) & ~CY_SCB_UART_TX_EMPTY);
1628         }
1629         if (event & CYHAL_UART_IRQ_TX_DONE)
1630         {
1631             Cy_SCB_SetTxInterruptMask(obj->base, Cy_SCB_GetTxInterruptMask(obj->base) & ~CY_SCB_UART_TX_DONE);
1632         }
1633         if (event & CYHAL_UART_IRQ_TX_ERROR)
1634         {
1635             Cy_SCB_SetTxInterruptMask(obj->base, Cy_SCB_GetTxInterruptMask(obj->base) & ~(CY_SCB_UART_TX_OVERFLOW | CY_SCB_UART_TRANSMIT_ERR));
1636         }
1637         if (event & CYHAL_UART_IRQ_TX_FIFO)
1638         {
1639             Cy_SCB_SetTxInterruptMask(obj->base, Cy_SCB_GetTxInterruptMask(obj->base) & ~CY_SCB_UART_TX_TRIGGER);
1640         }
1641 
1642         if (event & CYHAL_UART_IRQ_RX_NOT_EMPTY)
1643         {
1644             Cy_SCB_SetRxInterruptMask(obj->base, Cy_SCB_GetRxInterruptMask(obj->base) & ~CY_SCB_UART_RX_NOT_EMPTY);
1645         }
1646         if (event & CYHAL_UART_IRQ_RX_ERROR)
1647         {
1648             Cy_SCB_SetRxInterruptMask(obj->base, Cy_SCB_GetRxInterruptMask(obj->base) & ~CY_SCB_UART_RECEIVE_ERR);
1649         }
1650         if (event & CYHAL_UART_IRQ_RX_FIFO)
1651         {
1652             Cy_SCB_SetRxInterruptMask(obj->base, Cy_SCB_GetRxInterruptMask(obj->base) & ~CY_SCB_UART_RX_TRIGGER);
1653         }
1654     }
1655 
1656     if (event & CYHAL_UART_IRQ_TX_TRANSMIT_IN_FIFO)
1657     {
1658         /* This is a software event only. It is only applicable for cyhal_uart_write_async() */
1659     }
1660     if (event & CYHAL_UART_IRQ_RX_FULL)
1661     {
1662         /* This is a software event only. It is only applicable when using rx software buffer */
1663     }
1664     if (event & CYHAL_UART_IRQ_RX_DONE)
1665     {
1666         /* This is a software event only. It is only applicable for cyhal_uart_read_async() */
1667     }
1668     if (event == CYHAL_UART_IRQ_NONE)
1669     {
1670         /* "No interrupt" is equivalent for both "enable" and "disable" */
1671         Cy_SCB_ClearRxInterrupt(obj->base, CY_SCB_RX_INTR_MASK);
1672         Cy_SCB_ClearTxInterrupt(obj->base, CY_SCB_TX_INTR_MASK);
1673         Cy_SCB_SetRxInterruptMask(obj->base, Cy_SCB_GetRxInterruptMask(obj->base) & ~CY_SCB_RX_INTR_MASK);
1674         Cy_SCB_SetTxInterruptMask(obj->base, Cy_SCB_GetTxInterruptMask(obj->base) & ~CY_SCB_TX_INTR_MASK);
1675     }
1676 
1677     _cyhal_irq_set_priority(_CYHAL_SCB_IRQ_N[scb_arr_index], intr_priority);
1678     _cyhal_irq_enable(_CYHAL_SCB_IRQ_N[scb_arr_index]);
1679     #if defined(COMPONENT_CAT5)
1680     // The above Cy_SCB_DisableInterrupt also disconnects all the callback functions. They need to be registered again
1681     Cy_SCB_RegisterInterruptCallback(obj->base, _cyhal_irq_cb[_CYHAL_SCB_IRQ_N[scb_arr_index]]);
1682     Cy_SCB_EnableInterrupt(obj->base);
1683     #endif
1684 }
1685 
cyhal_uart_set_fifo_level(cyhal_uart_t * obj,cyhal_uart_fifo_type_t type,uint16_t level)1686 cy_rslt_t cyhal_uart_set_fifo_level(cyhal_uart_t *obj, cyhal_uart_fifo_type_t type, uint16_t level)
1687 {
1688     cy_rslt_t result = _cyhal_scb_set_fifo_level(obj->base, (cyhal_scb_fifo_type_t)type, level);
1689     #if (CYHAL_DRIVER_AVAILABLE_DMA)
1690     // Note: Because of this, it is important that any internal FIFO level setting is done via the above command directly
1691     if(result == CY_RSLT_SUCCESS)
1692     {
1693         obj->user_fifo_level = level;
1694     }
1695     #endif
1696     return result;
1697 }
1698 
cyhal_uart_enable_output(cyhal_uart_t * obj,cyhal_uart_output_t output,cyhal_source_t * source)1699 cy_rslt_t cyhal_uart_enable_output(cyhal_uart_t *obj, cyhal_uart_output_t output, cyhal_source_t *source)
1700 {
1701     return _cyhal_scb_enable_output(obj->resource, (cyhal_scb_output_t)output, source);
1702 }
1703 
cyhal_uart_disable_output(cyhal_uart_t * obj,cyhal_uart_output_t output)1704 cy_rslt_t cyhal_uart_disable_output(cyhal_uart_t *obj, cyhal_uart_output_t output)
1705 {
1706     CY_UNUSED_PARAMETER(obj);
1707     return _cyhal_scb_disable_output((cyhal_scb_output_t)output);
1708 }
1709 
cyhal_uart_config_software_buffer(cyhal_uart_t * obj,uint8_t * rx_buffer,uint32_t rx_buffer_size)1710 cy_rslt_t cyhal_uart_config_software_buffer(cyhal_uart_t *obj, uint8_t *rx_buffer, uint32_t rx_buffer_size)
1711 {
1712     cy_rslt_t result = CY_RSLT_SUCCESS;
1713     CY_ASSERT(NULL != obj);
1714     CY_ASSERT(NULL != rx_buffer);
1715 
1716 #if (CYHAL_DRIVER_AVAILABLE_DMA)
1717     if(obj->async_mode == CYHAL_ASYNC_DMA)
1718     {
1719         result =  CYHAL_UART_RSLT_ERR_UNSUPPORTED_CONFIG;
1720     }
1721     else
1722 #endif
1723     {
1724 
1725         Cy_SCB_UART_StartRingBuffer(obj->base, rx_buffer, rx_buffer_size, &(obj->context));
1726     }
1727 
1728     return result;
1729 }
1730 
1731 #if defined(__cplusplus)
1732 }
1733 #endif
1734 
1735 #endif /* CYHAL_DRIVER_AVAILABLE_UART */
1736