1 /*
2 * Copyright (c) 2022 Cypress Semiconductor Corporation (an Infineon company) or
3 * an affiliate of Cypress Semiconductor Corporation
4 *
5 * SPDX-License-Identifier: Apache-2.0
6 */
7
8 /**
9 * @brief UART driver for Infineon CAT1 MCU family.
10 *
11 * Note:
12 * - Uart ASYNC functionality is not implemented in current
13 * version of Uart CAT1 driver.
14 */
15
16 #define DT_DRV_COMPAT infineon_cat1_uart
17
18 #include <zephyr/drivers/uart.h>
19 #include <zephyr/drivers/pinctrl.h>
20 #include <cyhal_uart.h>
21 #include <cyhal_utils_impl.h>
22 #include <cyhal_scb_common.h>
23
24 /* Data structure */
25 struct ifx_cat1_uart_data {
26 cyhal_uart_t obj; /* UART CYHAL object */
27 struct uart_config cfg;
28 cyhal_resource_inst_t hw_resource;
29 cyhal_clock_t clock;
30
31 #if CONFIG_UART_INTERRUPT_DRIVEN
32 uart_irq_callback_user_data_t irq_cb; /* Interrupt Callback */
33 void *irq_cb_data; /* Interrupt Callback Arg */
34 #endif /* CONFIG_UART_INTERRUPT_DRIVEN */
35 };
36
37 /* Device config structure */
38 struct ifx_cat1_uart_config {
39 const struct pinctrl_dev_config *pcfg;
40 CySCB_Type *reg_addr;
41 struct uart_config dt_cfg;
42 uint8_t irq_priority;
43 };
44
45 /* Default Counter configuration structure */
46 static const cy_stc_scb_uart_config_t _cyhal_uart_default_config = {
47 .uartMode = CY_SCB_UART_STANDARD,
48 .enableMutliProcessorMode = false,
49 .smartCardRetryOnNack = false,
50 .irdaInvertRx = false,
51 .irdaEnableLowPowerReceiver = false,
52 .oversample = 12,
53 .enableMsbFirst = false,
54 .dataWidth = 8UL,
55 .parity = CY_SCB_UART_PARITY_NONE,
56 .stopBits = CY_SCB_UART_STOP_BITS_1,
57 .enableInputFilter = false,
58 .breakWidth = 11UL,
59 .dropOnFrameError = false,
60 .dropOnParityError = false,
61
62 .receiverAddress = 0x0UL,
63 .receiverAddressMask = 0x0UL,
64 .acceptAddrInFifo = false,
65
66 .enableCts = false,
67 .ctsPolarity = CY_SCB_UART_ACTIVE_LOW,
68 #if defined(COMPONENT_CAT1A) || defined(COMPONENT_CAT1B)
69 .rtsRxFifoLevel = 20UL,
70 #elif defined(COMPONENT_CAT2)
71 .rtsRxFifoLevel = 3UL,
72 #endif
73 .rtsPolarity = CY_SCB_UART_ACTIVE_LOW,
74
75 /* Level triggers when at least one element is in FIFO */
76 .rxFifoTriggerLevel = 0UL,
77 .rxFifoIntEnableMask = 0x0UL,
78
79 /* Level triggers when half-fifo is half empty */
80 .txFifoTriggerLevel = (CY_SCB_FIFO_SIZE / 2 - 1),
81 .txFifoIntEnableMask = 0x0UL
82 };
83
84 /* Helper API */
_convert_uart_parity_z_to_cyhal(enum uart_config_parity parity)85 static cyhal_uart_parity_t _convert_uart_parity_z_to_cyhal(enum uart_config_parity parity)
86 {
87 cyhal_uart_parity_t cyhal_parity;
88
89 switch (parity) {
90 case UART_CFG_PARITY_NONE:
91 cyhal_parity = CYHAL_UART_PARITY_NONE;
92 break;
93 case UART_CFG_PARITY_ODD:
94 cyhal_parity = CYHAL_UART_PARITY_ODD;
95 break;
96 case UART_CFG_PARITY_EVEN:
97 cyhal_parity = CYHAL_UART_PARITY_EVEN;
98 break;
99 default:
100 cyhal_parity = CYHAL_UART_PARITY_NONE;
101 }
102 return cyhal_parity;
103 }
104
_convert_uart_stop_bits_z_to_cyhal(enum uart_config_stop_bits stop_bits)105 static uint32_t _convert_uart_stop_bits_z_to_cyhal(enum uart_config_stop_bits stop_bits)
106 {
107 uint32_t cyhal_stop_bits;
108
109 switch (stop_bits) {
110 case UART_CFG_STOP_BITS_1:
111 cyhal_stop_bits = 1u;
112 break;
113
114 case UART_CFG_STOP_BITS_2:
115 cyhal_stop_bits = 2u;
116 break;
117 default:
118 cyhal_stop_bits = 1u;
119 }
120 return cyhal_stop_bits;
121 }
122
_convert_uart_data_bits_z_to_cyhal(enum uart_config_data_bits data_bits)123 static uint32_t _convert_uart_data_bits_z_to_cyhal(enum uart_config_data_bits data_bits)
124 {
125 uint32_t cyhal_data_bits;
126
127 switch (data_bits) {
128 case UART_CFG_DATA_BITS_5:
129 cyhal_data_bits = 1u;
130 break;
131
132 case UART_CFG_DATA_BITS_6:
133 cyhal_data_bits = 6u;
134 break;
135
136 case UART_CFG_DATA_BITS_7:
137 cyhal_data_bits = 7u;
138 break;
139
140 case UART_CFG_DATA_BITS_8:
141 cyhal_data_bits = 8u;
142 break;
143
144 case UART_CFG_DATA_BITS_9:
145 cyhal_data_bits = 9u;
146 break;
147
148 default:
149 cyhal_data_bits = 1u;
150 }
151 return cyhal_data_bits;
152 }
153
_get_hw_block_num(CySCB_Type * reg_addr)154 static int32_t _get_hw_block_num(CySCB_Type *reg_addr)
155 {
156 uint32_t i;
157
158 for (i = 0u; i < _SCB_ARRAY_SIZE; i++) {
159 if (_CYHAL_SCB_BASE_ADDRESSES[i] == reg_addr) {
160 return i;
161 }
162 }
163
164 return -1;
165 }
166
ifx_cat1_uart_poll_in(const struct device * dev,unsigned char * c)167 static int ifx_cat1_uart_poll_in(const struct device *dev, unsigned char *c)
168 {
169 cy_rslt_t rec;
170 struct ifx_cat1_uart_data *data = dev->data;
171
172 rec = cyhal_uart_getc(&data->obj, c, 0u);
173
174 return ((rec == CY_SCB_UART_RX_NO_DATA) ? -1 : 0);
175 }
176
ifx_cat1_uart_poll_out(const struct device * dev,unsigned char c)177 static void ifx_cat1_uart_poll_out(const struct device *dev, unsigned char c)
178 {
179 struct ifx_cat1_uart_data *data = dev->data;
180
181 (void) cyhal_uart_putc(&data->obj, (uint32_t)c);
182 }
183
ifx_cat1_uart_err_check(const struct device * dev)184 static int ifx_cat1_uart_err_check(const struct device *dev)
185 {
186 struct ifx_cat1_uart_data *data = dev->data;
187 uint32_t status = Cy_SCB_UART_GetRxFifoStatus(data->obj.base);
188 int errors = 0;
189
190 if (status & CY_SCB_UART_RX_OVERFLOW) {
191 errors |= UART_ERROR_OVERRUN;
192 }
193
194 if (status & CY_SCB_UART_RX_ERR_PARITY) {
195 errors |= UART_ERROR_PARITY;
196 }
197
198 if (status & CY_SCB_UART_RX_ERR_FRAME) {
199 errors |= UART_ERROR_FRAMING;
200 }
201
202 return errors;
203 }
204
ifx_cat1_uart_configure(const struct device * dev,const struct uart_config * cfg)205 static int ifx_cat1_uart_configure(const struct device *dev,
206 const struct uart_config *cfg)
207 {
208 __ASSERT_NO_MSG(cfg != NULL);
209
210 cy_rslt_t result;
211 struct ifx_cat1_uart_data *data = dev->data;
212
213 cyhal_uart_cfg_t uart_cfg = {
214 .data_bits = _convert_uart_data_bits_z_to_cyhal(cfg->data_bits),
215 .stop_bits = _convert_uart_stop_bits_z_to_cyhal(cfg->stop_bits),
216 .parity = _convert_uart_parity_z_to_cyhal(cfg->parity)
217 };
218
219 /* Store Uart Zephyr configuration (uart config) into data structure */
220 data->cfg = *cfg;
221
222 /* Configure parity, data and stop bits */
223 result = cyhal_uart_configure(&data->obj, &uart_cfg);
224
225 /* Configure the baud rate */
226 if (result == CY_RSLT_SUCCESS) {
227 result = cyhal_uart_set_baud(&data->obj, cfg->baudrate, NULL);
228 }
229
230 /* Set RTS/CTS flow control pins as NC so cyhal will skip initialization */
231 data->obj.pin_cts = NC;
232 data->obj.pin_rts = NC;
233
234 /* Enable RTS/CTS flow control */
235 if ((result == CY_RSLT_SUCCESS) && cfg->flow_ctrl) {
236 Cy_SCB_UART_EnableCts(data->obj.base);
237 }
238
239 return (result == CY_RSLT_SUCCESS) ? 0 : -ENOTSUP;
240 };
241
ifx_cat1_uart_config_get(const struct device * dev,struct uart_config * cfg)242 static int ifx_cat1_uart_config_get(const struct device *dev,
243 struct uart_config *cfg)
244 {
245 ARG_UNUSED(dev);
246
247 struct ifx_cat1_uart_data *const data = dev->data;
248
249 if (cfg == NULL) {
250 return -EINVAL;
251 }
252
253 *cfg = data->cfg;
254 return 0;
255 }
256
257 #ifdef CONFIG_UART_INTERRUPT_DRIVEN
258
259 /* Uart event callback for Interrupt driven mode */
_uart_event_callback_irq_mode(void * arg,cyhal_uart_event_t event)260 static void _uart_event_callback_irq_mode(void *arg, cyhal_uart_event_t event)
261 {
262 ARG_UNUSED(event);
263
264 const struct device *dev = (const struct device *) arg;
265 struct ifx_cat1_uart_data *const data = dev->data;
266
267 if (data->irq_cb != NULL) {
268 data->irq_cb(dev, data->irq_cb_data);
269 }
270 }
271
272 /* Fill FIFO with data */
ifx_cat1_uart_fifo_fill(const struct device * dev,const uint8_t * tx_data,int size)273 static int ifx_cat1_uart_fifo_fill(const struct device *dev,
274 const uint8_t *tx_data, int size)
275 {
276 struct ifx_cat1_uart_data *const data = dev->data;
277 size_t _size = (size_t) size;
278
279 (void)cyhal_uart_write(&data->obj, (uint8_t *) tx_data, &_size);
280 return (int) _size;
281 }
282
283 /* Read data from FIFO */
ifx_cat1_uart_fifo_read(const struct device * dev,uint8_t * rx_data,const int size)284 static int ifx_cat1_uart_fifo_read(const struct device *dev,
285 uint8_t *rx_data, const int size)
286 {
287 struct ifx_cat1_uart_data *const data = dev->data;
288 size_t _size = (size_t) size;
289
290 (void)cyhal_uart_read(&data->obj, rx_data, &_size);
291 return (int) _size;
292 }
293
294 /* Enable TX interrupt */
ifx_cat1_uart_irq_tx_enable(const struct device * dev)295 static void ifx_cat1_uart_irq_tx_enable(const struct device *dev)
296 {
297 struct ifx_cat1_uart_data *const data = dev->data;
298 const struct ifx_cat1_uart_config *const config = dev->config;
299
300 cyhal_uart_enable_event(&data->obj,
301 (cyhal_uart_event_t) CYHAL_UART_IRQ_TX_EMPTY,
302 config->irq_priority, 1);
303 }
304
305 /* Disable TX interrupt */
ifx_cat1_uart_irq_tx_disable(const struct device * dev)306 static void ifx_cat1_uart_irq_tx_disable(const struct device *dev)
307 {
308 struct ifx_cat1_uart_data *const data = dev->data;
309 const struct ifx_cat1_uart_config *const config = dev->config;
310
311 cyhal_uart_enable_event(&data->obj,
312 (cyhal_uart_event_t) CYHAL_UART_IRQ_TX_EMPTY,
313 config->irq_priority, 0);
314 }
315
316 /* Check if UART TX buffer can accept a new char */
ifx_cat1_uart_irq_tx_ready(const struct device * dev)317 static int ifx_cat1_uart_irq_tx_ready(const struct device *dev)
318 {
319 struct ifx_cat1_uart_data *const data = dev->data;
320 uint32_t mask = Cy_SCB_GetTxInterruptStatusMasked(data->obj.base);
321
322 return (((mask & (CY_SCB_UART_TX_NOT_FULL | SCB_INTR_TX_EMPTY_Msk)) != 0u) ? 1 : 0);
323 }
324
325 /* Check if UART TX block finished transmission */
ifx_cat1_uart_irq_tx_complete(const struct device * dev)326 static int ifx_cat1_uart_irq_tx_complete(const struct device *dev)
327 {
328 struct ifx_cat1_uart_data *const data = dev->data;
329
330 return (int) !(cyhal_uart_is_tx_active(&data->obj));
331 }
332
333 /* Enable RX interrupt */
ifx_cat1_uart_irq_rx_enable(const struct device * dev)334 static void ifx_cat1_uart_irq_rx_enable(const struct device *dev)
335 {
336 struct ifx_cat1_uart_data *const data = dev->data;
337 const struct ifx_cat1_uart_config *const config = dev->config;
338
339 cyhal_uart_enable_event(&data->obj,
340 (cyhal_uart_event_t) CYHAL_UART_IRQ_RX_NOT_EMPTY,
341 config->irq_priority, 1);
342 }
343
344 /* Disable TX interrupt */
ifx_cat1_uart_irq_rx_disable(const struct device * dev)345 static void ifx_cat1_uart_irq_rx_disable(const struct device *dev)
346 {
347 struct ifx_cat1_uart_data *const data = dev->data;
348 const struct ifx_cat1_uart_config *const config = dev->config;
349
350 cyhal_uart_enable_event(&data->obj,
351 (cyhal_uart_event_t) CYHAL_UART_IRQ_RX_NOT_EMPTY,
352 config->irq_priority, 0);
353 }
354
355 /* Check if UART RX buffer has a received char */
ifx_cat1_uart_irq_rx_ready(const struct device * dev)356 static int ifx_cat1_uart_irq_rx_ready(const struct device *dev)
357 {
358 struct ifx_cat1_uart_data *const data = dev->data;
359
360 return cyhal_uart_readable(&data->obj) ? 1 : 0;
361 }
362
363 /* Enable Error interrupts */
ifx_cat1_uart_irq_err_enable(const struct device * dev)364 static void ifx_cat1_uart_irq_err_enable(const struct device *dev)
365 {
366 struct ifx_cat1_uart_data *const data = dev->data;
367 const struct ifx_cat1_uart_config *const config = dev->config;
368
369 cyhal_uart_enable_event(&data->obj, (cyhal_uart_event_t)
370 (CYHAL_UART_IRQ_TX_ERROR | CYHAL_UART_IRQ_RX_ERROR),
371 config->irq_priority, 1);
372 }
373
374 /* Disable Error interrupts */
ifx_cat1_uart_irq_err_disable(const struct device * dev)375 static void ifx_cat1_uart_irq_err_disable(const struct device *dev)
376 {
377 struct ifx_cat1_uart_data *const data = dev->data;
378 const struct ifx_cat1_uart_config *const config = dev->config;
379
380 cyhal_uart_enable_event(&data->obj, (cyhal_uart_event_t)
381 (CYHAL_UART_IRQ_TX_ERROR | CYHAL_UART_IRQ_RX_ERROR),
382 config->irq_priority, 0);
383 }
384
385 /* Check if any IRQs is pending */
ifx_cat1_uart_irq_is_pending(const struct device * dev)386 static int ifx_cat1_uart_irq_is_pending(const struct device *dev)
387 {
388 struct ifx_cat1_uart_data *const data = dev->data;
389 uint32_t intcause = Cy_SCB_GetInterruptCause(data->obj.base);
390
391 return (int) (intcause & (CY_SCB_TX_INTR | CY_SCB_RX_INTR));
392 }
393
394 /* Start processing interrupts in ISR.
395 * This function should be called the first thing in the ISR. Calling
396 * uart_irq_rx_ready(), uart_irq_tx_ready(), uart_irq_tx_complete()
397 * allowed only after this.
398 */
ifx_cat1_uart_irq_update(const struct device * dev)399 static int ifx_cat1_uart_irq_update(const struct device *dev)
400 {
401 struct ifx_cat1_uart_data *const data = dev->data;
402 int status = 1;
403
404 if (((ifx_cat1_uart_irq_is_pending(dev) & CY_SCB_RX_INTR) != 0u) &&
405 (Cy_SCB_UART_GetNumInRxFifo(data->obj.base) == 0u)) {
406 status = 0;
407 }
408
409 return status;
410 }
411
ifx_cat1_uart_irq_callback_set(const struct device * dev,uart_irq_callback_user_data_t cb,void * cb_data)412 static void ifx_cat1_uart_irq_callback_set(const struct device *dev,
413 uart_irq_callback_user_data_t cb,
414 void *cb_data)
415 {
416 struct ifx_cat1_uart_data *data = dev->data;
417 cyhal_uart_t *uart_obj = &data->obj;
418
419 /* Store user callback info */
420 data->irq_cb = cb;
421 data->irq_cb_data = cb_data;
422
423 /* Register a uart general callback handler */
424 cyhal_uart_register_callback(uart_obj, _uart_event_callback_irq_mode, (void *) dev);
425 }
426 #endif /* CONFIG_UART_INTERRUPT_DRIVEN */
427
428
ifx_cat1_uart_init(const struct device * dev)429 static int ifx_cat1_uart_init(const struct device *dev)
430 {
431 struct ifx_cat1_uart_data *const data = dev->data;
432 const struct ifx_cat1_uart_config *const config = dev->config;
433 cy_rslt_t result;
434 int ret;
435
436 cyhal_uart_configurator_t uart_init_cfg = {
437 .resource = &data->hw_resource,
438 .config = &_cyhal_uart_default_config,
439 .clock = &data->clock,
440 .gpios = {
441 .pin_tx = NC,
442 .pin_rts = NC,
443 .pin_cts = NC,
444 },
445 };
446
447 /* Dedicate SCB HW resource */
448 data->hw_resource.type = CYHAL_RSC_SCB;
449 data->hw_resource.block_num = _get_hw_block_num(config->reg_addr);
450
451 /* Configure dt provided device signals when available */
452 ret = pinctrl_apply_state(config->pcfg, PINCTRL_STATE_DEFAULT);
453 if (ret < 0) {
454 return ret;
455 }
456
457 /* Allocates clock for selected IP block */
458 result = _cyhal_utils_allocate_clock(&data->clock, &data->hw_resource,
459 CYHAL_CLOCK_BLOCK_PERIPHERAL_16BIT, true);
460 if (result != CY_RSLT_SUCCESS) {
461 return -ENOTSUP;
462 }
463
464 /* Assigns a programmable divider to a selected IP block */
465 en_clk_dst_t clk_idx = _cyhal_scb_get_clock_index(uart_init_cfg.resource->block_num);
466
467 result = _cyhal_utils_peri_pclk_assign_divider(clk_idx, uart_init_cfg.clock);
468 if (result != CY_RSLT_SUCCESS) {
469 return -ENOTSUP;
470 }
471
472 /* Initialize the UART peripheral */
473 result = cyhal_uart_init_cfg(&data->obj, &uart_init_cfg);
474 if (result != CY_RSLT_SUCCESS) {
475 return -ENOTSUP;
476 }
477
478 /* Perform initial Uart configuration */
479 data->obj.is_clock_owned = true;
480 ret = ifx_cat1_uart_configure(dev, &config->dt_cfg);
481
482 return ret;
483 }
484
485 static DEVICE_API(uart, ifx_cat1_uart_driver_api) = {
486 .poll_in = ifx_cat1_uart_poll_in,
487 .poll_out = ifx_cat1_uart_poll_out,
488 .err_check = ifx_cat1_uart_err_check,
489
490 #ifdef CONFIG_UART_USE_RUNTIME_CONFIGURE
491 .configure = ifx_cat1_uart_configure,
492 .config_get = ifx_cat1_uart_config_get,
493 #endif /* CONFIG_UART_USE_RUNTIME_CONFIGURE */
494
495 #ifdef CONFIG_UART_INTERRUPT_DRIVEN
496 .fifo_fill = ifx_cat1_uart_fifo_fill,
497 .fifo_read = ifx_cat1_uart_fifo_read,
498 .irq_tx_enable = ifx_cat1_uart_irq_tx_enable,
499 .irq_tx_disable = ifx_cat1_uart_irq_tx_disable,
500 .irq_tx_ready = ifx_cat1_uart_irq_tx_ready,
501 .irq_rx_enable = ifx_cat1_uart_irq_rx_enable,
502 .irq_rx_disable = ifx_cat1_uart_irq_rx_disable,
503 .irq_tx_complete = ifx_cat1_uart_irq_tx_complete,
504 .irq_rx_ready = ifx_cat1_uart_irq_rx_ready,
505 .irq_err_enable = ifx_cat1_uart_irq_err_enable,
506 .irq_err_disable = ifx_cat1_uart_irq_err_disable,
507 .irq_is_pending = ifx_cat1_uart_irq_is_pending,
508 .irq_update = ifx_cat1_uart_irq_update,
509 .irq_callback_set = ifx_cat1_uart_irq_callback_set,
510 #endif /* CONFIG_UART_INTERRUPT_DRIVEN */
511 };
512
513 #define INFINEON_CAT1_UART_INIT(n) \
514 PINCTRL_DT_INST_DEFINE(n); \
515 static struct ifx_cat1_uart_data ifx_cat1_uart##n##_data; \
516 \
517 static struct ifx_cat1_uart_config ifx_cat1_uart##n##_cfg = { \
518 .dt_cfg.baudrate = DT_INST_PROP(n, current_speed), \
519 .dt_cfg.parity = DT_INST_ENUM_IDX_OR(n, parity, UART_CFG_PARITY_NONE), \
520 .dt_cfg.stop_bits = DT_INST_ENUM_IDX_OR(n, stop_bits, UART_CFG_STOP_BITS_1), \
521 .dt_cfg.data_bits = DT_INST_ENUM_IDX_OR(n, data_bits, UART_CFG_DATA_BITS_8), \
522 .dt_cfg.flow_ctrl = DT_INST_PROP(n, hw_flow_control), \
523 .pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(n), \
524 .reg_addr = (CySCB_Type *)DT_INST_REG_ADDR(n), \
525 .irq_priority = DT_INST_IRQ(n, priority) \
526 }; \
527 \
528 DEVICE_DT_INST_DEFINE(n, \
529 ifx_cat1_uart_init, NULL, \
530 &ifx_cat1_uart##n##_data, \
531 &ifx_cat1_uart##n##_cfg, PRE_KERNEL_1, \
532 CONFIG_SERIAL_INIT_PRIORITY, \
533 &ifx_cat1_uart_driver_api);
534
535 DT_INST_FOREACH_STATUS_OKAY(INFINEON_CAT1_UART_INIT)
536