/* * Copyright (c) 2022-2025, ArtInChip Technology Co., Ltd * * SPDX-License-Identifier: Apache-2.0 */ #include #include #include #include #include #include #include #include #include #define AIC_CLK_UART_MAX_FREQ 100000000 /* max 100M*/ #ifndef AIC_CLK_UART0_FREQ #define AIC_CLK_UART0_FREQ 48000000 /* default 48M*/ #endif #ifndef AIC_CLK_UART1_FREQ #define AIC_CLK_UART1_FREQ 48000000 /* default 48M*/ #endif #ifndef AIC_CLK_UART2_FREQ #define AIC_CLK_UART2_FREQ 48000000 /* default 48M*/ #endif #ifndef AIC_CLK_UART3_FREQ #define AIC_CLK_UART3_FREQ 48000000 /* default 48M*/ #endif #ifndef AIC_CLK_UART4_FREQ #define AIC_CLK_UART4_FREQ 48000000 /* default 48M*/ #endif #ifndef AIC_CLK_UART5_FREQ #define AIC_CLK_UART5_FREQ 48000000 /* default 48M*/ #endif #ifndef AIC_CLK_UART6_FREQ #define AIC_CLK_UART6_FREQ 48000000 /* default 48M*/ #endif #ifndef AIC_CLK_UART7_FREQ #define AIC_CLK_UART7_FREQ 48000000 /* default 48M*/ #endif #define DEVICE_FLAG_INT_RX 0x100 /**< INT mode on Rx */ #define DEVICE_FLAG_DMA_RX 0x200 /**< DMA mode on Rx */ #define DEVICE_FLAG_INT_TX 0x400 /**< INT mode on Tx */ #define DEVICE_FLAG_DMA_TX 0x800 /**< DMA mode on Tx */ #define SERIAL_MODE (DEVICE_FLAG_INT_RX) #ifndef SERIAL_RB_BUFSZ #define SERIAL_RB_BUFSZ 64 #endif #define UART_EVENT_RX_IND 0x01 /* Rx indication */ #define UART_EVENT_TX_DONE 0x02 /* Tx complete */ #define UART_EVENT_RX_DMADONE 0x03 /* Rx DMA transfer done */ #define UART_EVENT_TX_DMADONE 0x04 /* Tx DMA transfer done */ #define UART_EVENT_RX_TIMEOUT 0x05 /* Rx timeout */ struct uart_rx_fifo { /* software fifo */ struct ringbuffer rb; u8 buffer[] __attribute__((aligned(CACHE_LINE_SIZE))); }; struct uart_tx_fifo { /* software fifo */ struct ringbuffer rb; u8 buffer[] __attribute__((aligned(CACHE_LINE_SIZE))); }; struct uart_dev_para { u32 id : 4; u32 data_bits : 4; u32 stop_bits : 2; u32 parity : 2; u32 rx_mode : 2; u32 rx_bufsz; u32 tx_bufsz; u32 baud_rate; u32 clk; u32 clk_freq; u32 rst; u32 function; char *name; }; struct aic_uart { usart_handle_t handle; struct uart_dev_para *para; struct uart_rx_fifo *rx_fifo; struct uart_tx_fifo *tx_fifo; #if defined (AIC_SERIAL_USING_DMA) u32 rx_size; u32 tx_size; u32 tx_dma_working; u8 dma_tx_buf[AIC_UART_TX_FIFO_SIZE] __attribute__((aligned(CACHE_LINE_SIZE))); u8 dma_rx_buf[AIC_UART_RX_FIFO_SIZE] __attribute__((aligned(CACHE_LINE_SIZE))); #endif }; static struct uart_dev_para uart_dev_paras[] = { #ifdef AIC_USING_UART0 {0, AIC_DEV_UART0_DATABITS, AIC_DEV_UART0_STOPBITS, AIC_DEV_UART0_PARITY, AIC_DEV_UART0_RX_MODE, AIC_DEV_UART0_RX_BUFSZ, AIC_DEV_UART0_TX_BUFSZ, AIC_DEV_UART0_BAUDRATE, CLK_UART0, AIC_CLK_UART0_FREQ, RESET_UART0, AIC_DEV_UART0_MODE, "uart0"}, #endif #ifdef AIC_USING_UART1 {1, AIC_DEV_UART1_DATABITS, AIC_DEV_UART1_STOPBITS, AIC_DEV_UART1_PARITY, AIC_DEV_UART1_RX_MODE, AIC_DEV_UART1_RX_BUFSZ, AIC_DEV_UART1_TX_BUFSZ, AIC_DEV_UART1_BAUDRATE, CLK_UART1, AIC_CLK_UART1_FREQ, RESET_UART1, AIC_DEV_UART1_MODE, "uart1"}, #endif #ifdef AIC_USING_UART2 {2, AIC_DEV_UART2_DATABITS, AIC_DEV_UART2_STOPBITS, AIC_DEV_UART2_PARITY, AIC_DEV_UART2_RX_MODE, AIC_DEV_UART2_RX_BUFSZ, AIC_DEV_UART2_TX_BUFSZ, AIC_DEV_UART2_BAUDRATE, CLK_UART2, AIC_CLK_UART2_FREQ, RESET_UART2, AIC_DEV_UART2_MODE, "uart2"}, #endif #ifdef AIC_USING_UART3 {3, AIC_DEV_UART3_DATABITS, AIC_DEV_UART3_STOPBITS, AIC_DEV_UART3_PARITY, AIC_DEV_UART3_RX_MODE, AIC_DEV_UART3_RX_BUFSZ, AIC_DEV_UART3_TX_BUFSZ, AIC_DEV_UART3_BAUDRATE, CLK_UART3, AIC_CLK_UART3_FREQ, RESET_UART3, AIC_DEV_UART3_MODE, "uart3"}, #endif #ifdef AIC_USING_UART4 {4, AIC_DEV_UART4_DATABITS, AIC_DEV_UART4_STOPBITS, AIC_DEV_UART4_PARITY, AIC_DEV_UART4_RX_MODE, AIC_DEV_UART4_RX_BUFSZ, AIC_DEV_UART4_TX_BUFSZ, AIC_DEV_UART4_BAUDRATE, CLK_UART4, AIC_CLK_UART4_FREQ, RESET_UART4, AIC_DEV_UART4_MODE, "uart4"}, #endif #ifdef AIC_USING_UART5 {5, AIC_DEV_UART5_DATABITS, AIC_DEV_UART5_STOPBITS, AIC_DEV_UART5_PARITY, AIC_DEV_UART5_RX_MODE, AIC_DEV_UART5_RX_BUFSZ, AIC_DEV_UART5_TX_BUFSZ, AIC_DEV_UART5_BAUDRATE, CLK_UART5, AIC_CLK_UART5_FREQ, RESET_UART5, AIC_DEV_UART5_MODE, "uart5"}, #endif #ifdef AIC_USING_UART6 {6, AIC_DEV_UART6_DATABITS, AIC_DEV_UART6_STOPBITS, AIC_DEV_UART6_PARITY, AIC_DEV_UART6_RX_MODE, AIC_DEV_UART6_RX_BUFSZ, AIC_DEV_UART6_TX_BUFSZ, AIC_DEV_UART6_BAUDRATE, CLK_UART6, AIC_CLK_UART6_FREQ, RESET_UART6, AIC_DEV_UART6_MODE, "uart6"}, #endif #ifdef AIC_USING_UART7 {7, AIC_DEV_UART7_DATABITS, AIC_DEV_UART7_STOPBITS, AIC_DEV_UART7_PARITY, AIC_DEV_UART7_RX_MODE, AIC_DEV_UART7_RX_BUFSZ, AIC_DEV_UART7_TX_BUFSZ, AIC_DEV_UART7_BAUDRATE, CLK_UART7, AIC_CLK_UART7_FREQ, RESET_UART7, AIC_DEV_UART7_MODE, "uart7"}, #endif }; static void *uart_dev[AIC_UART_DEV_NUM]; static void uart_irqhandler(int irq, void * data) { struct aic_uart *uart = NULL; int id = irq - UART0_IRQn; u8 status= 0; if (id >= AIC_UART_DEV_NUM) return; uart = uart_dev[id]; if (!uart) return; status = hal_usart_get_irqstatus(id); #if defined (AIC_SERIAL_USING_DMA) struct uart_tx_fifo *tx_fifo = NULL; tx_fifo = uart->tx_fifo; switch (status) { case AIC_IIR_THR_EMPTY: hal_usart_set_interrupt(uart->handle, USART_INTR_WRITE, 0); if (uart->tx_dma_working) return; uart->tx_size = ringbuf_len(&(tx_fifo->rb)); if (uart->tx_size) { if (uart->tx_size > AIC_UART_TX_FIFO_SIZE) uart->tx_size = AIC_UART_TX_FIFO_SIZE; ringbuf_out(&(uart->tx_fifo->rb), uart->dma_tx_buf, uart->tx_size); aicos_dcache_clean_range(uart->dma_tx_buf, ALIGN_UP(AIC_UART_TX_FIFO_SIZE, CACHE_LINE_SIZE)); hal_uart_send_by_dma(uart->handle, uart->dma_tx_buf, uart->tx_size); uart->tx_dma_working = 1; } break; case AIC_IIR_RECV_DATA: case AIC_IIR_CHAR_TIMEOUT: uart->rx_size = hal_usart_get_rx_fifo_num(uart->handle); if (uart->rx_size) { hal_usart_set_interrupt(uart->handle, USART_INTR_READ, 0); hal_uart_rx_dma_config(uart->handle, uart->dma_rx_buf, uart->rx_size); } break; default: break; } #else struct uart_rx_fifo *rx_fifo = NULL; struct uart_tx_fifo *tx_fifo = NULL; int ch = -1; rx_fifo = uart->rx_fifo; tx_fifo = uart->tx_fifo; switch (status) { case AIC_IIR_THR_EMPTY: hal_usart_set_interrupt(uart->handle, USART_INTR_WRITE, 0); while (1) { if (ringbuf_len(&(tx_fifo->rb)) > 0) { if (hal_usart_putchar(uart->handle, ch) != 0) break; } else { break; } } break; case AIC_IIR_RECV_DATA: case AIC_IIR_CHAR_TIMEOUT: hal_usart_set_interrupt(uart->handle, USART_INTR_READ, 0); while (1) { ch = hal_uart_getchar(uart->handle); if (ch == -1) break; ringbuf_in(&(rx_fifo->rb), &ch, 1); } hal_usart_set_interrupt(uart->handle, USART_INTR_READ, 1); break; case AIC_IIR_RECV_LINE: hal_usart_set_interrupt(uart->handle, USART_INTR_READ, 0); hal_usart_intr_recv_line(id, uart->handle); hal_usart_set_interrupt(uart->handle, USART_INTR_READ, 1); break; default: break; } #endif } #if defined (AIC_SERIAL_USING_DMA) static void uart_dma_callback(aic_usart_priv_t *handle, void *arg) { struct aic_uart *uart = NULL; unsigned long event = (unsigned long)arg; uart = uart_dev[handle->idx]; if (!uart) return; switch(event) { case AIC_UART_TX_INT: uart->tx_dma_working = 0; if (ringbuf_len(&(uart->tx_fifo->rb))) hal_usart_set_interrupt(handle, USART_INTR_WRITE, 1); break; case AIC_UART_RX_INT: aicos_dcache_invalid_range(uart->dma_rx_buf, ALIGN_UP(AIC_UART_RX_FIFO_SIZE, CACHE_LINE_SIZE)); ringbuf_in(&(uart->rx_fifo->rb), uart->dma_rx_buf, uart->rx_size); hal_usart_set_interrupt(handle, USART_INTR_READ, 1); break; default: hal_log_err("not support event\n"); break; } } #endif static int uart_clk_init(struct aic_uart *uart) { s32 ret = 0; hal_clk_disable_assertrst(uart->para->clk); hal_clk_disable(uart->para->clk); hal_clk_set_freq(uart->para->clk, uart->para->clk_freq); ret = hal_clk_enable(uart->para->clk); if (ret < 0) { pr_err("UART%d clk enable failed!\n", uart->para->id); return ret; } ret = hal_reset_assert(uart->para->rst); if (ret < 0) { pr_err("UART%d reset assert failed!\n", uart->para->id); return ret; } aic_udelay(500); ret = hal_reset_deassert(uart->para->rst); if (ret < 0) { pr_err("UART%d reset deassert failed!\n", uart->para->id); return ret; } return ret; } static u32 get_parity(u32 cfg) { switch (cfg) { case 2: return USART_PARITY_EVEN; case 1: return USART_PARITY_ODD; default: return USART_PARITY_NONE; } } static u32 get_stop_bits(u32 cfg) { switch (cfg) { case 0: return USART_STOP_BITS_0_5; case 1: return USART_STOP_BITS_1; case 2: return USART_STOP_BITS_2; case 3: return USART_STOP_BITS_1_5; default: return USART_STOP_BITS_1; } } static u32 get_data_bits(u32 cfg) { switch (cfg) { case 5: return USART_DATA_BITS_5; case 6: return USART_DATA_BITS_6; case 7: return USART_DATA_BITS_7; case 8: return USART_DATA_BITS_8; case 9: return USART_DATA_BITS_9; } return USART_DATA_BITS_8; } int uart_init(int id) { struct aic_uart *uart = NULL; struct uart_dev_para *para = NULL; struct uart_rx_fifo *rx_fifo = NULL; struct uart_tx_fifo *tx_fifo = NULL; usart_handle_t handle = NULL; u32 data_bits, stop_bits, parity; int ret = -1, i; if (id < 0 || id > AIC_UART_DEV_NUM - 1) { pr_err("Invalid UART ID %d\n", id); return -1; } if (uart_dev[id]) { pr_info("UART%d was already inited\n", id); return 0; } uart = malloc(sizeof(struct aic_uart)); if (!uart) { pr_err("Failed to malloc(%d)\n", (u32)sizeof(struct aic_uart)); return -1; } memset(uart, 0, sizeof(struct aic_uart)); for (i = 0; i < ARRAY_SIZE(uart_dev_paras); i++) { if (id == uart_dev_paras[i].id) { para = &uart_dev_paras[i]; break; } } if (para == NULL) { ret = -ENODEV; goto err; } uart->para = para; uart_clk_init(uart); if (para->rx_mode != AIC_UART_RX_MODE_INT) { handle = hal_usart_initialize(id, NULL, NULL); hal_usart_set_interrupt(handle, USART_INTR_READ, 0); hal_usart_set_interrupt(handle, USART_INTR_WRITE, 0); } else { rx_fifo = (struct uart_rx_fifo *)malloc(sizeof(struct uart_rx_fifo) + para->rx_bufsz); if (rx_fifo == NULL) { ret = -ENOMEM; goto err; } memset(rx_fifo, 0, sizeof(struct uart_rx_fifo) + para->rx_bufsz); ringbuf_init(&(rx_fifo->rb), rx_fifo->buffer, para->rx_bufsz); uart->rx_fifo = rx_fifo; tx_fifo = (struct uart_tx_fifo *)malloc(sizeof(struct uart_tx_fifo) + para->tx_bufsz); if (tx_fifo == NULL) { ret = -ENOMEM; goto err; } memset(tx_fifo, 0, sizeof(struct uart_tx_fifo) + para->tx_bufsz); ringbuf_init(&(tx_fifo->rb), tx_fifo->buffer, para->tx_bufsz); uart->tx_fifo = tx_fifo; handle = hal_usart_initialize(id, NULL, uart_irqhandler); hal_usart_set_interrupt(handle, USART_INTR_READ, 1); } if (!handle) { ret = -ENODEV; goto err; } uart->handle = handle; data_bits = get_data_bits(para->data_bits); stop_bits = get_stop_bits(para->stop_bits); parity = get_parity(para->parity); ret = hal_usart_config(handle, para->baud_rate, USART_MODE_ASYNCHRONOUS, parity, stop_bits, data_bits, para->function); if (ret != 0) goto err; #if defined (AIC_SERIAL_USING_DMA) hal_uart_set_fifo(handle); hal_uart_attach_callback(handle, uart_dma_callback, NULL); memset(uart->dma_rx_buf, 0, AIC_UART_RX_FIFO_SIZE); memset(uart->dma_tx_buf, 0, AIC_UART_TX_FIFO_SIZE); #endif uart_dev[id] = uart; return ret; err: if (uart->rx_fifo != NULL) { free(uart->rx_fifo); uart->rx_fifo = NULL; } if (uart->tx_fifo != NULL) { free(uart->tx_fifo); uart->tx_fifo = NULL; } if (uart != NULL) { free(uart); uart = NULL; } return ret; } int uart_config_update(int id, int baudrate) { struct aic_uart *uart = NULL; struct uart_dev_para *para = NULL; u32 data_bits, stop_bits, parity; int freq, parent_clk_id, parent_freq; int cmu_div, uart_div, real_baudrate; u64 err, min_err = baudrate / 40; int ret = 0; uart = uart_dev[id]; if (!uart) return -ENODEV; para = uart->para; parent_clk_id = hal_clk_get_parent(para->clk); parent_freq = hal_clk_get_freq(parent_clk_id); freq = para->clk_freq; #if defined(AIC_CMU_DRV_V12) for (cmu_div = 16; cmu_div > 0; cmu_div--) { #else for (cmu_div = 32; cmu_div > 0; cmu_div--) { #endif if ((parent_freq / cmu_div) > AIC_CLK_UART_MAX_FREQ) continue; for (uart_div = 1; uart_div < 65536; uart_div++) { real_baudrate = parent_freq / (cmu_div * 16 * uart_div); err = abs(baudrate - real_baudrate); if (err < min_err) { freq = parent_freq / cmu_div; min_err = err; } } } if (min_err >= (baudrate / 40)) { printf("Error is too large for this baud rate.\n"); return -1; } hal_clk_set_freq(para->clk, freq); hal_clk_enable(para->clk); hal_reset_assert(para->rst); hal_reset_deassert(para->rst); data_bits = get_data_bits(para->data_bits); stop_bits = get_stop_bits(para->stop_bits); parity = get_parity(para->parity); hal_usart_set_interrupt(uart->handle, USART_INTR_READ, 0); ret = hal_usart_config(uart->handle, baudrate, USART_MODE_ASYNCHRONOUS, parity, stop_bits, data_bits, para->function); if (ret) { pr_err("set baudrate failed.\n"); } hal_usart_set_interrupt(uart->handle, USART_INTR_READ, 1); return ret; } int uart_deinit(int id) { struct aic_uart *uart = NULL; int ret; uart = uart_dev[id]; if (!uart) return -ENODEV; ret = hal_usart_uninitialize(uart->handle); if (uart->rx_fifo != NULL) { free(uart->rx_fifo); uart->rx_fifo = NULL; } if (uart->tx_fifo != NULL) { free(uart->tx_fifo); uart->tx_fifo = NULL; } if (uart != NULL) { free(uart); uart = NULL; } return ret; } void uart_reset(int id) { struct aic_uart *uart = NULL; uart = uart_dev[id]; if (uart->rx_fifo) ringbuf_reset(&(uart->rx_fifo->rb)); if (uart->tx_fifo) ringbuf_reset(&(uart->tx_fifo->rb)); } int uart_getchar(int id) { struct aic_uart *uart = NULL; struct uart_rx_fifo *rx_fifo = NULL; u64 start = 0, delta = 0; int timeout = 0; int ch = 0; uart = uart_dev[id]; if (!uart) return -ENODEV; rx_fifo = uart->rx_fifo; if (rx_fifo == NULL) { return hal_uart_getchar(uart->handle); } else { start = aic_get_time_ms(); while ((ringbuf_len(&(rx_fifo->rb)) < 1) && (delta < timeout)) { delta = aic_get_time_ms() - start; } if (!ringbuf_out(&(rx_fifo->rb), &ch, 1)) return -1; } return ch; } int uart_gets(int id, u8 *buf, int len) { struct aic_uart *uart = NULL; struct uart_rx_fifo *rx_fifo = NULL; u64 start = 0, delta = 0; int i, timeout = 0; uart = uart_dev[id]; if (!uart) return -ENODEV; rx_fifo = uart->rx_fifo; if (rx_fifo == NULL) { for (i = 0; i < len; i++) { start = aic_get_time_ms(); do { buf[i] = hal_uart_getchar(uart->handle); delta = aic_get_time_ms() - start; } while (delta < timeout && buf[i] == -1); if (buf[i] == -1) break; } return i; } else { start = aic_get_time_ms(); while ((ringbuf_len(&(rx_fifo->rb)) < len) && (delta < timeout)) { delta = aic_get_time_ms() - start; } return ringbuf_out(&(rx_fifo->rb), buf, len); } } int uart_putchar(int id, int c) { struct aic_uart *uart = NULL; struct uart_tx_fifo *tx_fifo = NULL; u64 start = 0, delta = 0; int timeout = 0; uart = uart_dev[id]; if (!uart) return -ENODEV; tx_fifo = uart->tx_fifo; if (tx_fifo == NULL) { return hal_usart_putchar(uart->handle, (uint8_t)c); } else { start = aic_get_time_ms(); while ((ringbuf_avail(&(tx_fifo->rb))) < 1 && (delta < timeout)) { delta = aic_get_time_ms() - start; } if (ringbuf_in(&(tx_fifo->rb), &c, 1) <= 0) return -1; hal_usart_set_interrupt(uart->handle, USART_INTR_WRITE, 1); } return 0; } int uart_puts(int id, u8 *buf, int len) { struct aic_uart *uart = NULL; struct uart_tx_fifo *tx_fifo = NULL; u64 start = 0, delta = 0; int i, timeout = 0; uart = uart_dev[id]; if (!uart) return -ENODEV; tx_fifo = uart->tx_fifo; if (tx_fifo == NULL) { for (i = 0; i < len; i++) { if (hal_usart_putchar(uart->handle, (uint8_t)buf[i])) break; } return i; } else { start = aic_get_time_ms(); while ((ringbuf_avail(&(tx_fifo->rb))) < len && (delta < timeout)) { delta = aic_get_time_ms() - start; } i = ringbuf_in(&(tx_fifo->rb), buf, len); if (i <= 0) return 0; hal_usart_set_interrupt(uart->handle, USART_INTR_WRITE, 1); return i; } } int uart_get_cur_baudrate(int id) { usart_handle_t handle = NULL; handle = hal_usart_initialize(id, NULL, NULL); return hal_usart_get_cur_baudrate(handle); }