/* * Copyright (c) 2022 Renesas Electronics Corporation * * SPDX-License-Identifier: Apache-2.0 */ #define DT_DRV_COMPAT renesas_smartbond_uart #include #include #include #include #include #include #include #include #define IIR_NO_INTR 1 #define IIR_THR_EMPTY 2 #define IIR_RX_DATA 4 #define IIR_LINE_STATUS 5 #define IIR_BUSY 7 #define IIR_TIMEOUT 12 #define STOP_BITS_1 0 #define STOP_BITS_2 1 #define DATA_BITS_5 0 #define DATA_BITS_6 1 #define DATA_BITS_7 2 #define DATA_BITS_8 3 #define RX_FIFO_TRIG_1_CHAR 0 #define RX_FIFO_TRIG_1_4_FULL 1 #define RX_FIFO_TRIG_1_2_FULL 2 #define RX_FIFO_TRIG_MINUS_2_CHARS 3 #define TX_FIFO_TRIG_EMPTY 0 #define TX_FIFO_TRIG_2_CHARS 1 #define TX_FIFO_TRIG_1_4_FULL 2 #define TX_FIFO_TRIG_1_2_FULL 3 #define BAUDRATE_CFG_DLH(cfg) (((cfg) >> 16) & 0xff) #define BAUDRATE_CFG_DLL(cfg) (((cfg) >> 8) & 0xff) #define BAUDRATE_CFG_DLF(cfg) ((cfg) & 0xff) struct uart_smartbond_baudrate_cfg { uint32_t baudrate; /* DLH=cfg[23:16] DLL=cfg[15:8] DLF=cfg[7:0] */ uint32_t cfg; }; static const struct uart_smartbond_baudrate_cfg uart_smartbond_baudrate_table[] = { { 2000000, 0x00000100 }, { 1000000, 0x00000200 }, { 921600, 0x00000203 }, { 500000, 0x00000400 }, { 230400, 0x0000080b }, { 115200, 0x00001106 }, { 57600, 0x0000220c }, { 38400, 0x00003401 }, { 28800, 0x00004507 }, { 19200, 0x00006803 }, { 14400, 0x00008a0e }, { 9600, 0x0000d005 }, { 4800, 0x0001a00b }, }; struct uart_smartbond_cfg { UART2_Type *regs; int periph_clock_config; const struct pinctrl_dev_config *pcfg; bool hw_flow_control_supported; #ifdef CONFIG_UART_INTERRUPT_DRIVEN void (*irq_config_func)(const struct device *dev); #endif }; struct uart_smartbond_data { struct uart_config current_config; struct k_spinlock lock; #ifdef CONFIG_UART_INTERRUPT_DRIVEN uart_irq_callback_user_data_t callback; void *cb_data; uint32_t flags; uint8_t rx_enabled; uint8_t tx_enabled; #endif }; static int uart_smartbond_poll_in(const struct device *dev, unsigned char *p_char) { const struct uart_smartbond_cfg *config = dev->config; struct uart_smartbond_data *data = dev->data; k_spinlock_key_t key = k_spin_lock(&data->lock); if ((config->regs->UART2_USR_REG & UART2_UART2_USR_REG_UART_RFNE_Msk) == 0) { k_spin_unlock(&data->lock, key); return -1; } *p_char = config->regs->UART2_RBR_THR_DLL_REG; k_spin_unlock(&data->lock, key); return 0; } static void uart_smartbond_poll_out(const struct device *dev, unsigned char out_char) { const struct uart_smartbond_cfg *config = dev->config; struct uart_smartbond_data *data = dev->data; k_spinlock_key_t key = k_spin_lock(&data->lock); while (!(config->regs->UART2_USR_REG & UART2_UART2_USR_REG_UART_TFNF_Msk)) { /* Wait until FIFO has free space */ } config->regs->UART2_RBR_THR_DLL_REG = out_char; k_spin_unlock(&data->lock, key); } static int uart_smartbond_configure(const struct device *dev, const struct uart_config *cfg) { const struct uart_smartbond_cfg *config = dev->config; struct uart_smartbond_data *data = dev->data; uint32_t baudrate_cfg = 0; k_spinlock_key_t key; uint32_t reg_val; int err; int i; if ((cfg->parity != UART_CFG_PARITY_NONE && cfg->parity != UART_CFG_PARITY_ODD && cfg->parity != UART_CFG_PARITY_EVEN) || (cfg->stop_bits != UART_CFG_STOP_BITS_1 && cfg->stop_bits != UART_CFG_STOP_BITS_2) || (cfg->data_bits != UART_CFG_DATA_BITS_5 && cfg->data_bits != UART_CFG_DATA_BITS_6 && cfg->data_bits != UART_CFG_DATA_BITS_7 && cfg->data_bits != UART_CFG_DATA_BITS_8) || (cfg->flow_ctrl != UART_CFG_FLOW_CTRL_NONE && cfg->flow_ctrl != UART_CFG_FLOW_CTRL_RTS_CTS)) { return -ENOTSUP; } /* Flow control is not supported on UART */ if (cfg->flow_ctrl == UART_CFG_FLOW_CTRL_RTS_CTS && !config->hw_flow_control_supported) { return -ENOTSUP; } /* Lookup configuration for baudrate */ for (i = 0; i < ARRAY_SIZE(uart_smartbond_baudrate_table); i++) { if (uart_smartbond_baudrate_table[i].baudrate == cfg->baudrate) { baudrate_cfg = uart_smartbond_baudrate_table[i].cfg; break; } } if (baudrate_cfg == 0) { return -ENOTSUP; } key = k_spin_lock(&data->lock); CRG_COM->SET_CLK_COM_REG = config->periph_clock_config; config->regs->UART2_SRR_REG = UART2_UART2_SRR_REG_UART_UR_Msk | UART2_UART2_SRR_REG_UART_RFR_Msk | UART2_UART2_SRR_REG_UART_XFR_Msk; config->regs->UART2_LCR_REG |= UART2_UART2_LCR_REG_UART_DLAB_Msk; config->regs->UART2_IER_DLH_REG = BAUDRATE_CFG_DLH(baudrate_cfg); config->regs->UART2_RBR_THR_DLL_REG = BAUDRATE_CFG_DLL(baudrate_cfg); config->regs->UART2_DLF_REG = BAUDRATE_CFG_DLF(baudrate_cfg); config->regs->UART2_LCR_REG &= ~UART2_UART2_LCR_REG_UART_DLAB_Msk; /* Configure frame */ reg_val = 0; switch (cfg->parity) { case UART_CFG_PARITY_NONE: break; case UART_CFG_PARITY_EVEN: reg_val |= UART2_UART2_LCR_REG_UART_EPS_Msk; /* no break */ case UART_CFG_PARITY_ODD: reg_val |= UART2_UART2_LCR_REG_UART_PEN_Msk; break; } if (cfg->stop_bits == UART_CFG_STOP_BITS_2) { reg_val |= STOP_BITS_2 << UART2_UART2_LCR_REG_UART_STOP_Pos; } switch (cfg->data_bits) { case UART_CFG_DATA_BITS_6: reg_val |= DATA_BITS_6 << UART2_UART2_LCR_REG_UART_DLS_Pos; break; case UART_CFG_DATA_BITS_7: reg_val |= DATA_BITS_7 << UART2_UART2_LCR_REG_UART_DLS_Pos; break; case UART_CFG_DATA_BITS_8: reg_val |= DATA_BITS_8 << UART2_UART2_LCR_REG_UART_DLS_Pos; break; } config->regs->UART2_LCR_REG = reg_val; /* Enable hardware FIFO */ config->regs->UART2_SFE_REG = UART2_UART2_SFE_REG_UART_SHADOW_FIFO_ENABLE_Msk; config->regs->UART2_SRT_REG = RX_FIFO_TRIG_1_CHAR; config->regs->UART2_STET_REG = TX_FIFO_TRIG_1_2_FULL; data->current_config = *cfg; k_spin_unlock(&data->lock, key); err = pinctrl_apply_state(config->pcfg, PINCTRL_STATE_DEFAULT); if (err < 0) { return err; } return 0; } #ifdef CONFIG_UART_USE_RUNTIME_CONFIGURE static int uart_smartbond_config_get(const struct device *dev, struct uart_config *cfg) { struct uart_smartbond_data *data = dev->data; *cfg = data->current_config; return 0; } #endif /* CONFIG_UART_USE_RUNTIME_CONFIGURE */ static int uart_smartbond_init(const struct device *dev) { struct uart_smartbond_data *data = dev->data; return uart_smartbond_configure(dev, &data->current_config); } #ifdef CONFIG_UART_INTERRUPT_DRIVEN static inline void irq_tx_enable(const struct device *dev) { const struct uart_smartbond_cfg *config = dev->config; config->regs->UART2_IER_DLH_REG |= UART2_UART2_IER_DLH_REG_PTIME_DLH7_Msk | UART2_UART2_IER_DLH_REG_ETBEI_DLH1_Msk; } static inline void irq_tx_disable(const struct device *dev) { const struct uart_smartbond_cfg *config = dev->config; config->regs->UART2_IER_DLH_REG &= ~(UART2_UART2_IER_DLH_REG_PTIME_DLH7_Msk | UART2_UART2_IER_DLH_REG_ETBEI_DLH1_Msk); } static inline void irq_rx_enable(const struct device *dev) { const struct uart_smartbond_cfg *config = dev->config; config->regs->UART2_IER_DLH_REG |= UART2_UART2_IER_DLH_REG_ERBFI_DLH0_Msk; } static inline void irq_rx_disable(const struct device *dev) { const struct uart_smartbond_cfg *config = dev->config; config->regs->UART2_IER_DLH_REG &= ~UART2_UART2_IER_DLH_REG_ERBFI_DLH0_Msk; } static int uart_smartbond_fifo_fill(const struct device *dev, const uint8_t *tx_data, int len) { const struct uart_smartbond_cfg *config = dev->config; struct uart_smartbond_data *data = dev->data; int num_tx = 0; k_spinlock_key_t key = k_spin_lock(&data->lock); while ((len - num_tx > 0) && (config->regs->UART2_USR_REG & UART2_UART2_USR_REG_UART_TFNF_Msk)) { config->regs->UART2_RBR_THR_DLL_REG = tx_data[num_tx++]; } if (data->tx_enabled) { irq_tx_enable(dev); } k_spin_unlock(&data->lock, key); return num_tx; } static int uart_smartbond_fifo_read(const struct device *dev, uint8_t *rx_data, const int size) { const struct uart_smartbond_cfg *config = dev->config; struct uart_smartbond_data *data = dev->data; int num_rx = 0; k_spinlock_key_t key = k_spin_lock(&data->lock); while ((size - num_rx > 0) && (config->regs->UART2_USR_REG & UART2_UART2_USR_REG_UART_RFNE_Msk)) { rx_data[num_rx++] = config->regs->UART2_RBR_THR_DLL_REG; } if (data->rx_enabled) { irq_rx_enable(dev); } k_spin_unlock(&data->lock, key); return num_rx; } static void uart_smartbond_irq_tx_enable(const struct device *dev) { struct uart_smartbond_data *data = dev->data; k_spinlock_key_t key = k_spin_lock(&data->lock); data->tx_enabled = 1; irq_tx_enable(dev); k_spin_unlock(&data->lock, key); } static void uart_smartbond_irq_tx_disable(const struct device *dev) { struct uart_smartbond_data *data = dev->data; k_spinlock_key_t key = k_spin_lock(&data->lock); irq_tx_disable(dev); data->tx_enabled = 0; k_spin_unlock(&data->lock, key); } static int uart_smartbond_irq_tx_ready(const struct device *dev) { const struct uart_smartbond_cfg *config = dev->config; return (config->regs->UART2_USR_REG & UART2_UART2_USR_REG_UART_TFNF_Msk) != 0; } static void uart_smartbond_irq_rx_enable(const struct device *dev) { struct uart_smartbond_data *data = dev->data; k_spinlock_key_t key = k_spin_lock(&data->lock); data->rx_enabled = 1; irq_rx_enable(dev); k_spin_unlock(&data->lock, key); } static void uart_smartbond_irq_rx_disable(const struct device *dev) { struct uart_smartbond_data *data = dev->data; k_spinlock_key_t key = k_spin_lock(&data->lock); irq_rx_disable(dev); data->rx_enabled = 0; k_spin_unlock(&data->lock, key); } static int uart_smartbond_irq_tx_complete(const struct device *dev) { const struct uart_smartbond_cfg *config = dev->config; return (config->regs->UART2_USR_REG & UART2_UART2_USR_REG_UART_TFE_Msk) != 0; } static int uart_smartbond_irq_rx_ready(const struct device *dev) { const struct uart_smartbond_cfg *config = dev->config; return (config->regs->UART2_USR_REG & UART2_UART2_USR_REG_UART_RFNE_Msk) != 0; } static void uart_smartbond_irq_err_enable(const struct device *dev) { k_panic(); } static void uart_smartbond_irq_err_disable(const struct device *dev) { k_panic(); } static int uart_smartbond_irq_is_pending(const struct device *dev) { k_panic(); return 0; } static int uart_smartbond_irq_update(const struct device *dev) { const struct uart_smartbond_cfg *config = dev->config; bool no_intr = false; while (!no_intr) { switch (config->regs->UART2_IIR_FCR_REG & 0x0F) { case IIR_NO_INTR: no_intr = true; break; case IIR_THR_EMPTY: irq_tx_disable(dev); break; case IIR_RX_DATA: irq_rx_disable(dev); break; case IIR_LINE_STATUS: case IIR_TIMEOUT: /* ignore */ break; case IIR_BUSY: /* busy detect */ /* fall-through */ default: k_panic(); break; } } return 1; } static void uart_smartbond_irq_callback_set(const struct device *dev, uart_irq_callback_user_data_t cb, void *cb_data) { struct uart_smartbond_data *data = dev->data; data->callback = cb; data->cb_data = cb_data; } static void uart_smartbond_isr(const struct device *dev) { struct uart_smartbond_data *data = dev->data; if (data->callback) { data->callback(dev, data->cb_data); } } #endif /* CONFIG_UART_INTERRUPT_DRIVEN */ static const struct uart_driver_api uart_smartbond_driver_api = { .poll_in = uart_smartbond_poll_in, .poll_out = uart_smartbond_poll_out, #ifdef CONFIG_UART_USE_RUNTIME_CONFIGURE .configure = uart_smartbond_configure, .config_get = uart_smartbond_config_get, #endif #ifdef CONFIG_UART_INTERRUPT_DRIVEN .fifo_fill = uart_smartbond_fifo_fill, .fifo_read = uart_smartbond_fifo_read, .irq_tx_enable = uart_smartbond_irq_tx_enable, .irq_tx_disable = uart_smartbond_irq_tx_disable, .irq_tx_ready = uart_smartbond_irq_tx_ready, .irq_rx_enable = uart_smartbond_irq_rx_enable, .irq_rx_disable = uart_smartbond_irq_rx_disable, .irq_tx_complete = uart_smartbond_irq_tx_complete, .irq_rx_ready = uart_smartbond_irq_rx_ready, .irq_err_enable = uart_smartbond_irq_err_enable, .irq_err_disable = uart_smartbond_irq_err_disable, .irq_is_pending = uart_smartbond_irq_is_pending, .irq_update = uart_smartbond_irq_update, .irq_callback_set = uart_smartbond_irq_callback_set, #endif /* CONFIG_UART_INTERRUPT_DRIVEN */ }; #ifdef CONFIG_UART_INTERRUPT_DRIVEN #define UART_SMARTBOND_CONFIGURE(id) \ do { \ IRQ_CONNECT(DT_INST_IRQN(id), \ DT_INST_IRQ(id, priority), \ uart_smartbond_isr, \ DEVICE_DT_INST_GET(id), 0); \ \ irq_enable(DT_INST_IRQN(id)); \ } while (0) #else #define UART_SMARTBOND_CONFIGURE(id) #endif #define UART_SMARTBOND_DEVICE(id) \ PINCTRL_DT_INST_DEFINE(id); \ static const struct uart_smartbond_cfg uart_smartbond_##id##_cfg = { \ .regs = (UART2_Type *)DT_INST_REG_ADDR(id), \ .periph_clock_config = DT_INST_PROP(id, periph_clock_config), \ .pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(id), \ .hw_flow_control_supported = DT_INST_PROP(id, hw_flow_control_supported), \ }; \ static struct uart_smartbond_data uart_smartbond_##id##_data = { \ .current_config = { \ .baudrate = DT_INST_PROP(id, current_speed), \ .parity = UART_CFG_PARITY_NONE, \ .stop_bits = UART_CFG_STOP_BITS_1, \ .data_bits = UART_CFG_DATA_BITS_8, \ .flow_ctrl = UART_CFG_FLOW_CTRL_NONE, \ }, \ }; \ static int uart_smartbond_##id##_init(const struct device *dev) \ { \ UART_SMARTBOND_CONFIGURE(id); \ return uart_smartbond_init(dev); \ } \ DEVICE_DT_INST_DEFINE(id, \ uart_smartbond_##id##_init, \ NULL, \ &uart_smartbond_##id##_data, \ &uart_smartbond_##id##_cfg, \ PRE_KERNEL_1, CONFIG_SERIAL_INIT_PRIORITY, \ &uart_smartbond_driver_api); \ DT_INST_FOREACH_STATUS_OKAY(UART_SMARTBOND_DEVICE)