| /* |
| * Copyright (c) 2022 Cypress Semiconductor Corporation (an Infineon company) or |
| * an affiliate of Cypress Semiconductor Corporation |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| /** |
| * @brief UART driver for Infineon CAT1 MCU family. |
| * |
| * Note: |
| * - Uart ASYNC functionality is not implemented in current |
| * version of Uart CAT1 driver. |
| */ |
| |
| #define DT_DRV_COMPAT infineon_cat1_uart |
| |
| #include <zephyr/drivers/uart.h> |
| #include <zephyr/drivers/pinctrl.h> |
| #include <cyhal_uart.h> |
| #include <cyhal_utils_impl.h> |
| #include <cyhal_scb_common.h> |
| |
| /* Data structure */ |
| struct ifx_cat1_uart_data { |
| cyhal_uart_t obj; /* UART CYHAL object */ |
| struct uart_config cfg; |
| cyhal_resource_inst_t hw_resource; |
| cyhal_clock_t clock; |
| |
| #if CONFIG_UART_INTERRUPT_DRIVEN |
| uart_irq_callback_user_data_t irq_cb; /* Interrupt Callback */ |
| void *irq_cb_data; /* Interrupt Callback Arg */ |
| #endif /* CONFIG_UART_INTERRUPT_DRIVEN */ |
| }; |
| |
| /* Device config structure */ |
| struct ifx_cat1_uart_config { |
| const struct pinctrl_dev_config *pcfg; |
| CySCB_Type *reg_addr; |
| struct uart_config dt_cfg; |
| uint8_t irq_priority; |
| }; |
| |
| /* Default Counter configuration structure */ |
| static const cy_stc_scb_uart_config_t _cyhal_uart_default_config = { |
| .uartMode = CY_SCB_UART_STANDARD, |
| .enableMutliProcessorMode = false, |
| .smartCardRetryOnNack = false, |
| .irdaInvertRx = false, |
| .irdaEnableLowPowerReceiver = false, |
| .oversample = 12, |
| .enableMsbFirst = false, |
| .dataWidth = 8UL, |
| .parity = CY_SCB_UART_PARITY_NONE, |
| .stopBits = CY_SCB_UART_STOP_BITS_1, |
| .enableInputFilter = false, |
| .breakWidth = 11UL, |
| .dropOnFrameError = false, |
| .dropOnParityError = false, |
| |
| .receiverAddress = 0x0UL, |
| .receiverAddressMask = 0x0UL, |
| .acceptAddrInFifo = false, |
| |
| .enableCts = false, |
| .ctsPolarity = CY_SCB_UART_ACTIVE_LOW, |
| #if defined(COMPONENT_CAT1A) || defined(COMPONENT_CAT1B) |
| .rtsRxFifoLevel = 20UL, |
| #elif defined(COMPONENT_CAT2) |
| .rtsRxFifoLevel = 3UL, |
| #endif |
| .rtsPolarity = CY_SCB_UART_ACTIVE_LOW, |
| |
| /* Level triggers when at least one element is in FIFO */ |
| .rxFifoTriggerLevel = 0UL, |
| .rxFifoIntEnableMask = 0x0UL, |
| |
| /* Level triggers when half-fifo is half empty */ |
| .txFifoTriggerLevel = (CY_SCB_FIFO_SIZE / 2 - 1), |
| .txFifoIntEnableMask = 0x0UL |
| }; |
| |
| /* Helper API */ |
| static cyhal_uart_parity_t _convert_uart_parity_z_to_cyhal(enum uart_config_parity parity) |
| { |
| cyhal_uart_parity_t cyhal_parity; |
| |
| switch (parity) { |
| case UART_CFG_PARITY_NONE: |
| cyhal_parity = CYHAL_UART_PARITY_NONE; |
| break; |
| case UART_CFG_PARITY_ODD: |
| cyhal_parity = CYHAL_UART_PARITY_ODD; |
| break; |
| case UART_CFG_PARITY_EVEN: |
| cyhal_parity = CYHAL_UART_PARITY_EVEN; |
| break; |
| default: |
| cyhal_parity = CYHAL_UART_PARITY_NONE; |
| } |
| return cyhal_parity; |
| } |
| |
| static uint32_t _convert_uart_stop_bits_z_to_cyhal(enum uart_config_stop_bits stop_bits) |
| { |
| uint32_t cyhal_stop_bits; |
| |
| switch (stop_bits) { |
| case UART_CFG_STOP_BITS_1: |
| cyhal_stop_bits = 1u; |
| break; |
| |
| case UART_CFG_STOP_BITS_2: |
| cyhal_stop_bits = 2u; |
| break; |
| default: |
| cyhal_stop_bits = 1u; |
| } |
| return cyhal_stop_bits; |
| } |
| |
| static uint32_t _convert_uart_data_bits_z_to_cyhal(enum uart_config_data_bits data_bits) |
| { |
| uint32_t cyhal_data_bits; |
| |
| switch (data_bits) { |
| case UART_CFG_DATA_BITS_5: |
| cyhal_data_bits = 1u; |
| break; |
| |
| case UART_CFG_DATA_BITS_6: |
| cyhal_data_bits = 6u; |
| break; |
| |
| case UART_CFG_DATA_BITS_7: |
| cyhal_data_bits = 7u; |
| break; |
| |
| case UART_CFG_DATA_BITS_8: |
| cyhal_data_bits = 8u; |
| break; |
| |
| case UART_CFG_DATA_BITS_9: |
| cyhal_data_bits = 9u; |
| break; |
| |
| default: |
| cyhal_data_bits = 1u; |
| } |
| return cyhal_data_bits; |
| } |
| |
| static int32_t _get_hw_block_num(CySCB_Type *reg_addr) |
| { |
| uint32_t i; |
| |
| for (i = 0u; i < _SCB_ARRAY_SIZE; i++) { |
| if (_CYHAL_SCB_BASE_ADDRESSES[i] == reg_addr) { |
| return i; |
| } |
| } |
| |
| return -1; |
| } |
| |
| static int ifx_cat1_uart_poll_in(const struct device *dev, unsigned char *c) |
| { |
| cy_rslt_t rec; |
| struct ifx_cat1_uart_data *data = dev->data; |
| |
| rec = cyhal_uart_getc(&data->obj, c, 0u); |
| |
| return ((rec == CY_SCB_UART_RX_NO_DATA) ? -1 : 0); |
| } |
| |
| static void ifx_cat1_uart_poll_out(const struct device *dev, unsigned char c) |
| { |
| struct ifx_cat1_uart_data *data = dev->data; |
| |
| (void) cyhal_uart_putc(&data->obj, (uint32_t)c); |
| } |
| |
| static int ifx_cat1_uart_err_check(const struct device *dev) |
| { |
| struct ifx_cat1_uart_data *data = dev->data; |
| uint32_t status = Cy_SCB_UART_GetRxFifoStatus(data->obj.base); |
| int errors = 0; |
| |
| if (status & CY_SCB_UART_RX_OVERFLOW) { |
| errors |= UART_ERROR_OVERRUN; |
| } |
| |
| if (status & CY_SCB_UART_RX_ERR_PARITY) { |
| errors |= UART_ERROR_PARITY; |
| } |
| |
| if (status & CY_SCB_UART_RX_ERR_FRAME) { |
| errors |= UART_ERROR_FRAMING; |
| } |
| |
| return errors; |
| } |
| |
| static int ifx_cat1_uart_configure(const struct device *dev, |
| const struct uart_config *cfg) |
| { |
| __ASSERT_NO_MSG(cfg != NULL); |
| |
| cy_rslt_t result; |
| struct ifx_cat1_uart_data *data = dev->data; |
| |
| cyhal_uart_cfg_t uart_cfg = { |
| .data_bits = _convert_uart_data_bits_z_to_cyhal(cfg->data_bits), |
| .stop_bits = _convert_uart_stop_bits_z_to_cyhal(cfg->stop_bits), |
| .parity = _convert_uart_parity_z_to_cyhal(cfg->parity) |
| }; |
| |
| /* Store Uart Zephyr configuration (uart config) into data structure */ |
| data->cfg = *cfg; |
| |
| /* Configure parity, data and stop bits */ |
| result = cyhal_uart_configure(&data->obj, &uart_cfg); |
| |
| /* Configure the baud rate */ |
| if (result == CY_RSLT_SUCCESS) { |
| result = cyhal_uart_set_baud(&data->obj, cfg->baudrate, NULL); |
| } |
| |
| /* Set RTS/CTS flow control pins as NC so cyhal will skip initialization */ |
| data->obj.pin_cts = NC; |
| data->obj.pin_rts = NC; |
| |
| /* Enable RTS/CTS flow control */ |
| if ((result == CY_RSLT_SUCCESS) && cfg->flow_ctrl) { |
| Cy_SCB_UART_EnableCts(data->obj.base); |
| } |
| |
| return (result == CY_RSLT_SUCCESS) ? 0 : -ENOTSUP; |
| }; |
| |
| static int ifx_cat1_uart_config_get(const struct device *dev, |
| struct uart_config *cfg) |
| { |
| ARG_UNUSED(dev); |
| |
| struct ifx_cat1_uart_data *const data = dev->data; |
| |
| if (cfg == NULL) { |
| return -EINVAL; |
| } |
| |
| *cfg = data->cfg; |
| return 0; |
| } |
| |
| #ifdef CONFIG_UART_INTERRUPT_DRIVEN |
| |
| /* Uart event callback for Interrupt driven mode */ |
| static void _uart_event_callback_irq_mode(void *arg, cyhal_uart_event_t event) |
| { |
| ARG_UNUSED(event); |
| |
| const struct device *dev = (const struct device *) arg; |
| struct ifx_cat1_uart_data *const data = dev->data; |
| |
| if (data->irq_cb != NULL) { |
| data->irq_cb(dev, data->irq_cb_data); |
| } |
| } |
| |
| /* Fill FIFO with data */ |
| static int ifx_cat1_uart_fifo_fill(const struct device *dev, |
| const uint8_t *tx_data, int size) |
| { |
| struct ifx_cat1_uart_data *const data = dev->data; |
| size_t _size = (size_t) size; |
| |
| (void)cyhal_uart_write(&data->obj, (uint8_t *) tx_data, &_size); |
| return (int) _size; |
| } |
| |
| /* Read data from FIFO */ |
| static int ifx_cat1_uart_fifo_read(const struct device *dev, |
| uint8_t *rx_data, const int size) |
| { |
| struct ifx_cat1_uart_data *const data = dev->data; |
| size_t _size = (size_t) size; |
| |
| (void)cyhal_uart_read(&data->obj, rx_data, &_size); |
| return (int) _size; |
| } |
| |
| /* Enable TX interrupt */ |
| static void ifx_cat1_uart_irq_tx_enable(const struct device *dev) |
| { |
| struct ifx_cat1_uart_data *const data = dev->data; |
| const struct ifx_cat1_uart_config *const config = dev->config; |
| |
| cyhal_uart_enable_event(&data->obj, |
| (cyhal_uart_event_t) CYHAL_UART_IRQ_TX_EMPTY, |
| config->irq_priority, 1); |
| } |
| |
| /* Disable TX interrupt */ |
| static void ifx_cat1_uart_irq_tx_disable(const struct device *dev) |
| { |
| struct ifx_cat1_uart_data *const data = dev->data; |
| const struct ifx_cat1_uart_config *const config = dev->config; |
| |
| cyhal_uart_enable_event(&data->obj, |
| (cyhal_uart_event_t) CYHAL_UART_IRQ_TX_EMPTY, |
| config->irq_priority, 0); |
| } |
| |
| /* Check if UART TX buffer can accept a new char */ |
| static int ifx_cat1_uart_irq_tx_ready(const struct device *dev) |
| { |
| struct ifx_cat1_uart_data *const data = dev->data; |
| uint32_t mask = Cy_SCB_GetTxInterruptStatusMasked(data->obj.base); |
| |
| return (((mask & (CY_SCB_UART_TX_NOT_FULL | SCB_INTR_TX_EMPTY_Msk)) != 0u) ? 1 : 0); |
| } |
| |
| /* Check if UART TX block finished transmission */ |
| static int ifx_cat1_uart_irq_tx_complete(const struct device *dev) |
| { |
| struct ifx_cat1_uart_data *const data = dev->data; |
| |
| return (int) !(cyhal_uart_is_tx_active(&data->obj)); |
| } |
| |
| /* Enable RX interrupt */ |
| static void ifx_cat1_uart_irq_rx_enable(const struct device *dev) |
| { |
| struct ifx_cat1_uart_data *const data = dev->data; |
| const struct ifx_cat1_uart_config *const config = dev->config; |
| |
| cyhal_uart_enable_event(&data->obj, |
| (cyhal_uart_event_t) CYHAL_UART_IRQ_RX_NOT_EMPTY, |
| config->irq_priority, 1); |
| } |
| |
| /* Disable TX interrupt */ |
| static void ifx_cat1_uart_irq_rx_disable(const struct device *dev) |
| { |
| struct ifx_cat1_uart_data *const data = dev->data; |
| const struct ifx_cat1_uart_config *const config = dev->config; |
| |
| cyhal_uart_enable_event(&data->obj, |
| (cyhal_uart_event_t) CYHAL_UART_IRQ_RX_NOT_EMPTY, |
| config->irq_priority, 0); |
| } |
| |
| /* Check if UART RX buffer has a received char */ |
| static int ifx_cat1_uart_irq_rx_ready(const struct device *dev) |
| { |
| struct ifx_cat1_uart_data *const data = dev->data; |
| |
| return cyhal_uart_readable(&data->obj) ? 1 : 0; |
| } |
| |
| /* Enable Error interrupts */ |
| static void ifx_cat1_uart_irq_err_enable(const struct device *dev) |
| { |
| struct ifx_cat1_uart_data *const data = dev->data; |
| const struct ifx_cat1_uart_config *const config = dev->config; |
| |
| cyhal_uart_enable_event(&data->obj, (cyhal_uart_event_t) |
| (CYHAL_UART_IRQ_TX_ERROR | CYHAL_UART_IRQ_RX_ERROR), |
| config->irq_priority, 1); |
| } |
| |
| /* Disable Error interrupts */ |
| static void ifx_cat1_uart_irq_err_disable(const struct device *dev) |
| { |
| struct ifx_cat1_uart_data *const data = dev->data; |
| const struct ifx_cat1_uart_config *const config = dev->config; |
| |
| cyhal_uart_enable_event(&data->obj, (cyhal_uart_event_t) |
| (CYHAL_UART_IRQ_TX_ERROR | CYHAL_UART_IRQ_RX_ERROR), |
| config->irq_priority, 0); |
| } |
| |
| /* Check if any IRQs is pending */ |
| static int ifx_cat1_uart_irq_is_pending(const struct device *dev) |
| { |
| struct ifx_cat1_uart_data *const data = dev->data; |
| uint32_t intcause = Cy_SCB_GetInterruptCause(data->obj.base); |
| |
| return (int) (intcause & (CY_SCB_TX_INTR | CY_SCB_RX_INTR)); |
| } |
| |
| /* Start processing interrupts in ISR. |
| * This function should be called the first thing in the ISR. Calling |
| * uart_irq_rx_ready(), uart_irq_tx_ready(), uart_irq_tx_complete() |
| * allowed only after this. |
| */ |
| static int ifx_cat1_uart_irq_update(const struct device *dev) |
| { |
| struct ifx_cat1_uart_data *const data = dev->data; |
| int status = 1; |
| |
| if (((ifx_cat1_uart_irq_is_pending(dev) & CY_SCB_RX_INTR) != 0u) && |
| (Cy_SCB_UART_GetNumInRxFifo(data->obj.base) == 0u)) { |
| status = 0; |
| } |
| |
| return status; |
| } |
| |
| static void ifx_cat1_uart_irq_callback_set(const struct device *dev, |
| uart_irq_callback_user_data_t cb, |
| void *cb_data) |
| { |
| struct ifx_cat1_uart_data *data = dev->data; |
| cyhal_uart_t *uart_obj = &data->obj; |
| |
| /* Store user callback info */ |
| data->irq_cb = cb; |
| data->irq_cb_data = cb_data; |
| |
| /* Register a uart general callback handler */ |
| cyhal_uart_register_callback(uart_obj, _uart_event_callback_irq_mode, (void *) dev); |
| } |
| #endif /* CONFIG_UART_INTERRUPT_DRIVEN */ |
| |
| |
| static int ifx_cat1_uart_init(const struct device *dev) |
| { |
| struct ifx_cat1_uart_data *const data = dev->data; |
| const struct ifx_cat1_uart_config *const config = dev->config; |
| cy_rslt_t result; |
| int ret; |
| |
| cyhal_uart_configurator_t uart_init_cfg = { |
| .resource = &data->hw_resource, |
| .config = &_cyhal_uart_default_config, |
| .clock = &data->clock, |
| .gpios = { |
| .pin_tx = NC, |
| .pin_rts = NC, |
| .pin_cts = NC, |
| }, |
| }; |
| |
| /* Dedicate SCB HW resource */ |
| data->hw_resource.type = CYHAL_RSC_SCB; |
| data->hw_resource.block_num = _get_hw_block_num(config->reg_addr); |
| |
| /* Configure dt provided device signals when available */ |
| ret = pinctrl_apply_state(config->pcfg, PINCTRL_STATE_DEFAULT); |
| if (ret < 0) { |
| return ret; |
| } |
| |
| /* Allocates clock for selected IP block */ |
| result = _cyhal_utils_allocate_clock(&data->clock, &data->hw_resource, |
| CYHAL_CLOCK_BLOCK_PERIPHERAL_16BIT, true); |
| if (result != CY_RSLT_SUCCESS) { |
| return -ENOTSUP; |
| } |
| |
| /* Assigns a programmable divider to a selected IP block */ |
| en_clk_dst_t clk_idx = _cyhal_scb_get_clock_index(uart_init_cfg.resource->block_num); |
| |
| result = _cyhal_utils_peri_pclk_assign_divider(clk_idx, uart_init_cfg.clock); |
| if (result != CY_RSLT_SUCCESS) { |
| return -ENOTSUP; |
| } |
| |
| /* Initialize the UART peripheral */ |
| result = cyhal_uart_init_cfg(&data->obj, &uart_init_cfg); |
| if (result != CY_RSLT_SUCCESS) { |
| return -ENOTSUP; |
| } |
| |
| /* Perform initial Uart configuration */ |
| data->obj.is_clock_owned = true; |
| ret = ifx_cat1_uart_configure(dev, &config->dt_cfg); |
| |
| return ret; |
| } |
| |
| static const struct uart_driver_api ifx_cat1_uart_driver_api = { |
| .poll_in = ifx_cat1_uart_poll_in, |
| .poll_out = ifx_cat1_uart_poll_out, |
| .err_check = ifx_cat1_uart_err_check, |
| |
| #ifdef CONFIG_UART_USE_RUNTIME_CONFIGURE |
| .configure = ifx_cat1_uart_configure, |
| .config_get = ifx_cat1_uart_config_get, |
| #endif /* CONFIG_UART_USE_RUNTIME_CONFIGURE */ |
| |
| #ifdef CONFIG_UART_INTERRUPT_DRIVEN |
| .fifo_fill = ifx_cat1_uart_fifo_fill, |
| .fifo_read = ifx_cat1_uart_fifo_read, |
| .irq_tx_enable = ifx_cat1_uart_irq_tx_enable, |
| .irq_tx_disable = ifx_cat1_uart_irq_tx_disable, |
| .irq_tx_ready = ifx_cat1_uart_irq_tx_ready, |
| .irq_rx_enable = ifx_cat1_uart_irq_rx_enable, |
| .irq_rx_disable = ifx_cat1_uart_irq_rx_disable, |
| .irq_tx_complete = ifx_cat1_uart_irq_tx_complete, |
| .irq_rx_ready = ifx_cat1_uart_irq_rx_ready, |
| .irq_err_enable = ifx_cat1_uart_irq_err_enable, |
| .irq_err_disable = ifx_cat1_uart_irq_err_disable, |
| .irq_is_pending = ifx_cat1_uart_irq_is_pending, |
| .irq_update = ifx_cat1_uart_irq_update, |
| .irq_callback_set = ifx_cat1_uart_irq_callback_set, |
| #endif /* CONFIG_UART_INTERRUPT_DRIVEN */ |
| }; |
| |
| #define INFINEON_CAT1_UART_INIT(n) \ |
| PINCTRL_DT_INST_DEFINE(n); \ |
| static struct ifx_cat1_uart_data ifx_cat1_uart##n##_data; \ |
| \ |
| static struct ifx_cat1_uart_config ifx_cat1_uart##n##_cfg = { \ |
| .dt_cfg.baudrate = DT_INST_PROP(n, current_speed), \ |
| .dt_cfg.parity = DT_INST_ENUM_IDX_OR(n, parity, UART_CFG_PARITY_NONE), \ |
| .dt_cfg.stop_bits = DT_INST_ENUM_IDX_OR(n, stop_bits, UART_CFG_STOP_BITS_1), \ |
| .dt_cfg.data_bits = DT_INST_ENUM_IDX_OR(n, data_bits, UART_CFG_DATA_BITS_8), \ |
| .dt_cfg.flow_ctrl = DT_INST_PROP(n, hw_flow_control), \ |
| .pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(n), \ |
| .reg_addr = (CySCB_Type *)DT_INST_REG_ADDR(n), \ |
| .irq_priority = DT_INST_IRQ(n, priority) \ |
| }; \ |
| \ |
| DEVICE_DT_INST_DEFINE(n, \ |
| ifx_cat1_uart_init, NULL, \ |
| &ifx_cat1_uart##n##_data, \ |
| &ifx_cat1_uart##n##_cfg, PRE_KERNEL_1, \ |
| CONFIG_SERIAL_INIT_PRIORITY, \ |
| &ifx_cat1_uart_driver_api); |
| |
| DT_INST_FOREACH_STATUS_OKAY(INFINEON_CAT1_UART_INIT) |