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