/* * Copyright (c) CompanyNameMagicTag 2012-2019. All rights reserved. * Description: Adjust the directory structure to prepare for compiling the SDK. uart_drv.c code * Author: CompanyName * Create: 2012-07-14 */ #include "uart_drv.h" #include "uart.h" #include "soc_mdm_isr.h" #include "errno.h" #include #include #include #include #include #include #include #define UART_RBR 0x00 /* Receive buff. */ #define UART_THR 0x00 /* Transmit Holding */ #define UART_DLL 0x00 /* Divisor Latch Low */ #define UART_DLH 0x04 /* Divisor Latch High */ #define UART_IER 0x04 /* int enable */ #define UART_IIR 0x08 /* interrupt indentification REG */ #define UART_FCR 0x08 /* FIFO control */ #define UART_LCR 0x0C /* Line control */ #define UART_MCR 0x10 /* Line control */ #define UART_LSR 0x14 /* Line statue */ #define UART_USR 0x7C /* Uart statues */ #define UART_TFL 0x80 #define UART_RFL 0x84 #define UART_HTX 0xA4 /* Halt Tx */ #define UART_FCR_RXFIFO_ONEBYTE 0 #define UART_FCR_RXFIFO_QUARTER (1 << 6) #define UART_FCR_RXFIFO_HALF (2 << 6) #define UART_FCR_RXFIFO_2TOFULL (3 << 6) #define UART_FCR_TXFIFO_EMPTY 0 #define UART_FCR_TXFIFO_2BYTE (1 << 4) #define UART_FCR_TXFIFO_QUARTER (2 << 4) #define UART_FCR_TXFIFO_HALF (3 << 4) #define UART_FCR_RESET_TXFIFO (1 << 2) #define UART_FCR_RESET_RXFIFO (1 << 1) #define UART_FCR_ENABLE_FIFO (1 << 0) #define FCR_CONFIG_VAL \ (UART_FCR_RXFIFO_QUARTER | UART_FCR_TXFIFO_2BYTE | UART_FCR_RESET_TXFIFO | UART_FCR_RESET_RXFIFO | \ UART_FCR_ENABLE_FIFO) #define UART_FIFO_TX_SIZE 16 #define UART_IER_TX (0x01 << 1) #define UART_IER_RX (0x01 << 0) #define UART_IER_TF (0x01 << 8) /* Tx finish */ #define UART_IER_MASK 0x18F #define UART_IIR_RX_TIMEOUT (0x03 << 2) #define UART_IIR_RX (0x01 << 2) #define UART_IIR_TX (0x01 << 1) #define UART_IIR_TF 0xE /* Tx finish */ #define UART_USR_BUSY (0x01 << 0) #define UART_USR_TXF_NF (0x01 << 1) #define UART_USR_TXF_E (0x01 << 2) #define UART_USR_RXF_NE (0X01 << 3) #define UART_LSR_OE 0x02 /* overrun error */ #define UART_LSR_PE 0x04 /* parity error */ #define UART_LSR_FE 0x08 /* framing error */ #define UART_LSR_BI 0x10 /* break interrupt */ int g_irq_send_count = 0; #define ext_uart_buffer_lock(x) uapi_int_lock() #define ext_uart_buffer_unlock(x, val) uapi_int_restore(val) int uart_circ_buf_empty(EXT_CONST uart_circ_buf_t *transfer) { if (transfer->flags & BUF_CIRCLED) { return 0; } if (transfer->wp == transfer->rp) { return 1; } else { return 0; } } __init unsigned int uart_init_circ_buf(uart_driver_data_t *udd, unsigned int rx_buf_size, unsigned int tx_buf_size) { if ((rx_buf_size == 0) || (tx_buf_size == 0)) { return EXT_ERR_INVALID_PARAMETER; } udd->rx_transfer = (uart_circ_buf_t *)uapi_malloc(EXT_MOD_ID_UART, sizeof(uart_circ_buf_t)); if (udd->rx_transfer == NULL) { goto END; } (td_void)memset_s((td_void *)udd->rx_transfer, sizeof(uart_circ_buf_t), 0, sizeof(uart_circ_buf_t)); udd->rx_transfer->data = (char *)uapi_malloc(EXT_MOD_ID_UART, rx_buf_size); if (udd->rx_transfer->data == NULL) { goto FREE_RX_TRANSFER; } udd->rx_transfer->size = rx_buf_size; udd->tx_transfer = (uart_circ_buf_t *)uapi_malloc(EXT_MOD_ID_UART, sizeof(uart_circ_buf_t)); if (udd->tx_transfer == NULL) { goto FREE_RX_CIRC_BUF; } (td_void)memset_s((td_void *)udd->tx_transfer, sizeof(uart_circ_buf_t), 0, sizeof(uart_circ_buf_t)); udd->tx_transfer->data = (char *)uapi_malloc(EXT_MOD_ID_UART, tx_buf_size); if (udd->tx_transfer->data == NULL) { goto FREE_TX_CIRC_BUF; } udd->tx_transfer->size = tx_buf_size; return EXT_ERR_SUCCESS; FREE_TX_CIRC_BUF: uapi_free(EXT_MOD_ID_UART, (VOID *)udd->tx_transfer); udd->tx_transfer = TD_NULL; FREE_RX_CIRC_BUF: uapi_free(EXT_MOD_ID_UART, (VOID *)udd->rx_transfer->data); udd->rx_transfer->data = TD_NULL; FREE_RX_TRANSFER: uapi_free(EXT_MOD_ID_UART, (VOID *)udd->rx_transfer); udd->rx_transfer = TD_NULL; END: return EXT_ERR_FAILURE; } __init void uart_deinit_circ_buf(uart_driver_data_t *udd) { if (udd->rx_transfer != TD_NULL) { if (udd->rx_transfer->data != TD_NULL) { uapi_free(EXT_MOD_ID_UART, udd->rx_transfer->data); udd->rx_transfer->data = TD_NULL; } uapi_free(EXT_MOD_ID_UART, udd->rx_transfer); udd->rx_transfer = TD_NULL; } if (udd->tx_transfer != TD_NULL) { if (udd->tx_transfer->data != TD_NULL) { uapi_free(EXT_MOD_ID_UART, udd->tx_transfer->data); udd->tx_transfer->data = TD_NULL; } uapi_free(EXT_MOD_ID_UART, udd->tx_transfer); udd->tx_transfer = TD_NULL; } } int uart_read_circ_buf(uart_circ_buf_t *transfer, char *buf, size_t count) { if (transfer == NULL) { return -EFAULT; } td_u32 last_int_val = ext_uart_buffer_lock(transfer); unsigned int wp = transfer->wp; unsigned int rp = transfer->rp; unsigned int flags = transfer->flags; ext_uart_buffer_unlock(transfer, last_int_val); unsigned long data = (unsigned long)(uintptr_t)transfer->data; unsigned int buf_size = transfer->size; if ((flags & BUF_CIRCLED) == 0) { if (count >= (wp - rp)) { count = wp - rp; } if (count > 0 && memcpy_s(buf, count, (void *)((uintptr_t)data + rp), count) != EOK) { return -EFAULT; } transfer->rp += count; return (int)count; } else { if (count < (buf_size - rp)) { if (count > 0 && memcpy_s(buf, count, (void *)((uintptr_t)data + rp), count) != EOK) { return -EFAULT; } transfer->rp += count; return (int)count; } else { unsigned int copy_size = buf_size - rp; unsigned int left_size = count - copy_size; if (copy_size > 0 && memcpy_s(buf, copy_size, (void *)((uintptr_t)data + rp), copy_size) != EOK) { return -EFAULT; } rp = 0; if (left_size > wp) { left_size = wp; } if (left_size > 0 && memcpy_s((void *)(buf + copy_size), left_size, (void *)((uintptr_t)data + rp), left_size) != EOK) { return -EFAULT; } last_int_val = ext_uart_buffer_lock(transfer); transfer->rp = left_size; transfer->flags &= ~BUF_CIRCLED; ext_uart_buffer_unlock(transfer, last_int_val); return (int)(copy_size + left_size); } } } unsigned char uart_rx_fifo_num(EXT_CONST uart_driver_data_t *udd) { return (unsigned char)UAPI_REG_READ_VAL32((uintptr_t)udd->phys_base + UART_RFL); } int uart_write_circ_buf(uart_circ_buf_t *transfer, const char *buf, size_t count) { if (transfer == NULL) { return -EFAULT; } td_u32 last_int_val = ext_uart_buffer_lock(transfer); unsigned int wp = transfer->wp; unsigned int rp = transfer->rp; unsigned int flags = transfer->flags; ext_uart_buffer_unlock(transfer, last_int_val); unsigned long data = (unsigned long)(uintptr_t)transfer->data; unsigned int buf_size = transfer->size; if ((flags & BUF_CIRCLED) == 0) { if (count < (buf_size - wp)) { if (count > 0 && memcpy_s((void *)((uintptr_t)data + wp), count, buf, count) != EOK) { return -EFAULT; } transfer->wp += count; return (int)count; } else { unsigned int copy_size = buf_size - wp; unsigned int left_size = count - copy_size; if (copy_size > 0 && memcpy_s((void *)((uintptr_t)data + wp), copy_size, buf, copy_size) != EOK) { return -EFAULT; } wp = 0; if (left_size > rp) { /* overflowed. some new data will be discarded */ left_size = rp; } if (left_size > 0 && memcpy_s((void *)((uintptr_t)data + wp), left_size, (void *)(buf + copy_size), left_size) != EOK) { return -EFAULT; } last_int_val = ext_uart_buffer_lock(transfer); transfer->wp = left_size; transfer->flags |= BUF_CIRCLED; ext_uart_buffer_unlock(transfer, last_int_val); return (int)(copy_size + left_size); } } else { if (count > (rp - wp)) { /* overflowed. some new data will be discarded */ count = rp - wp; } if (count > 0 && memcpy_s((void *)((uintptr_t)data + wp), count, buf, count) != EOK) { return -EFAULT; } transfer->wp += count; return (int)count; } } void uart_tx_interrupt_disable(EXT_CONST uart_driver_data_t *udd) { unsigned int tx_status = UAPI_REG_READ_VAL32(udd->phys_base + UART_IER); UAPI_REG_WRITE32((uintptr_t)udd->phys_base + UART_IER, (~UART_IER_TX) & tx_status); } void uart_tx_interrupt_enable(EXT_CONST uart_driver_data_t *udd) { unsigned int tx_status = UAPI_REG_READ_VAL32(udd->phys_base + UART_IER); UAPI_REG_WRITE32((uintptr_t)udd->phys_base + UART_IER, (unsigned short)(UART_IER_TX | tx_status)); } void uart_rx_interrupt_disable(EXT_CONST uart_driver_data_t *udd) { unsigned int tx_status = UAPI_REG_READ_VAL32(udd->phys_base + UART_IER); UAPI_REG_WRITE32((uintptr_t)udd->phys_base + UART_IER, ~(UART_IER_RX)&tx_status); } EXT_PRV void uart_rx_interrupt_enable(EXT_CONST uart_driver_data_t *udd) { unsigned int tx_status = UAPI_REG_READ_VAL32(udd->phys_base + UART_IER); UAPI_REG_WRITE32((uintptr_t)udd->phys_base + UART_IER, UART_IER_RX | tx_status); } void uart_tf_interrupt_disable(EXT_CONST uart_driver_data_t *udd) { unsigned int tx_status = UAPI_REG_READ_VAL32((uintptr_t)udd->phys_base + UART_IER); UAPI_REG_WRITE32((uintptr_t)udd->phys_base + UART_IER, ~(UART_IER_TF)&tx_status); } EXT_PRV void uart_tf_interrupt_enable(EXT_CONST uart_driver_data_t *udd) { unsigned int tx_status = UAPI_REG_READ_VAL32((uintptr_t)udd->phys_base + UART_IER); UAPI_REG_WRITE32((uintptr_t)udd->phys_base + UART_IER, UART_IER_TF | tx_status); } EXT_PRV td_u32 uart_interrupt_disable(EXT_CONST uart_driver_data_t *udd) { unsigned int int_val = UAPI_REG_READ_VAL32((uintptr_t)udd->phys_base + UART_IER); UAPI_REG_WRITE32((uintptr_t)udd->phys_base + UART_IER, int_val & (~UART_IER_MASK)); return int_val; } EXT_PRV void uart_interrupt_restore(EXT_CONST uart_driver_data_t *udd, td_u32 int_val) { UAPI_REG_WRITE32((uintptr_t)udd->phys_base + UART_IER, int_val); } EXT_PRV void get_recv_irq_err(unsigned int int_status, uart_drv_stat_info_t *uart_stat_info) { if ((int_status & UART_LSR_OE) != 0) { (uart_stat_info->recv_irq_err_overrun)++; } if ((int_status & UART_LSR_PE) != 0) { (uart_stat_info->recv_irq_err_parity)++; } if ((int_status & UART_LSR_FE) != 0) { (uart_stat_info->recv_irq_err_frame)++; } if ((int_status & UART_LSR_BI) != 0) { (uart_stat_info->recv_irq_err_break)++; } } EXT_PRV void uart_drv_iir_tx_proc(uart_driver_data_t *udd, char *buf, td_u32 len) { ext_unref_param(len); if (udd->tx_transfer == TD_NULL || udd->ops == TD_NULL) { return; } if (uart_circ_buf_empty(udd->tx_transfer) == TD_TRUE) { uart_tx_interrupt_disable(udd); uart_tf_interrupt_enable(udd); } else { unsigned int tfl = UAPI_REG_READ_VAL32(udd->phys_base + UART_TFL); EXT_CONST unsigned int tx_fifo_cnt = UART_FIFO_TX_SIZE - tfl; unsigned int count = (unsigned int)udd->tx_send(udd->tx_transfer, buf, tx_fifo_cnt); if (udd->ops->start_tx) { (void)udd->ops->start_tx(udd, buf, count); } if ((udd->flags & UART_FLG_WD_BLOCK) != 0) { (void)LOS_EventWrite(&udd->uart_event, UART_WD_EVENT); } } #ifdef UART_DEBUG_INFO udd->uart_stat_info.send_irq_cnt++; #endif } #define BUFF_SIZE 64 EXT_PRV void uart_drv_irq_stat_count(uart_driver_data_t *udd, unsigned int count, unsigned int rcv_suc_cnt) { udd->uart_stat_info.recv_irq_cnt++; udd->uart_stat_info.recv_irq_data_cnt += count; udd->uart_stat_info.recv_irq_data_suc_cnt += rcv_suc_cnt; if (rcv_suc_cnt < count) { udd->uart_stat_info.recv_buf_overflow_cnt++; } } EXT_PRV void uart_drv_irq(td_u32 data) { char buf[BUFF_SIZE] = { 0 }; unsigned int count = 0; unsigned int rcv_suc_cnt = 0; uintptr_t data_t = (uintptr_t)data; uart_driver_data_t *udd = (uart_driver_data_t *)data_t; if (udd == NULL || udd->rx_transfer == TD_NULL) { return; } uart_drv_stat_info_t *uart_stat_info = &(udd->uart_stat_info); unsigned int status = UAPI_REG_READ_VAL32(udd->phys_base + UART_IIR) & 0xf; if ((status == UART_IIR_RX_TIMEOUT) || (status == UART_IIR_RX)) { get_recv_irq_err(UAPI_REG_READ_VAL32(udd->phys_base + UART_LSR), uart_stat_info); do { unsigned int usr = UAPI_REG_READ_VAL32(udd->phys_base + UART_USR); /* if uart hardware rx fifo is empty, go out of circle */ if (((usr & UART_USR_RXF_NE) == 0) && (count == 0)) { UAPI_REG_READ_VAL32((td_u32)(udd->phys_base + (td_u32)UART_RBR)); (udd->uart_stat_info.recv_irq_err_emptyfifo_cnt)++; } if ((usr & UART_USR_RXF_NE) == 0) { break; } buf[count] = (char)UAPI_REG_READ_VAL32(udd->phys_base + UART_RBR); if (udd->uart_stat_info.recv_last_context.num < EXT_UART_LAST_RECORD_BYTE_COUNT) { udd->uart_stat_info.recv_last_context.data[udd->uart_stat_info.recv_last_context.num] = (unsigned char)buf[count]; } udd->uart_stat_info.recv_last_context.num++; udd->uart_stat_info.recv_last_context.num &= (EXT_UART_LAST_RECORD_BYTE_COUNT - 1); count++; } while (count < BUFF_SIZE); if (count >= 1) { rcv_suc_cnt = (unsigned int)udd->rx_recv(udd->rx_transfer, buf, count); if ((udd->flags & UART_FLG_RD_BLOCK) != 0) { LOS_EventWrite(&udd->uart_event, UART_RD_EVENT); } } #ifdef UART_DEBUG_INFO uart_drv_irq_stat_count(udd, count, rcv_suc_cnt); #endif } if (status == UART_IIR_TX) { /* disable uart tx fifo interrupt */ uart_drv_iir_tx_proc(udd, buf, sizeof(buf)); } else if (status == UART_IIR_TF) { uart_tf_interrupt_disable(udd); } } #define WRITE_LCR_TIMEOUT 1000000 #define LCR_DLAB 0x80 EXT_PRV void writelcr(EXT_CONST uart_driver_data_t *udd, unsigned int val) { unsigned int usr = 0; unsigned int lcr; unsigned int timeout_cnt = 0; unsigned int i = 0; lcr = UAPI_REG_READ_VAL32((uintptr_t)udd->phys_base + UART_LCR); while (lcr != val) { if (timeout_cnt++ >= WRITE_LCR_TIMEOUT) { return; } usr = UAPI_REG_READ_VAL32((uintptr_t)udd->phys_base + UART_USR); while (usr & UART_USR_BUSY) { uapi_udelay(1); if (timeout_cnt++ >= WRITE_LCR_TIMEOUT) { return; } lcr = UAPI_REG_READ_VAL32((uintptr_t)udd->phys_base + UART_LCR); for (i = 0; i < UART_FIFO_TX_SIZE + 1 && !(lcr & LCR_DLAB); i++) { (void)UAPI_REG_READ_VAL32((uintptr_t)udd->phys_base + UART_RBR); } if ((lcr & LCR_DLAB)) { /* reset fifo */ UAPI_REG_WRITE32((uintptr_t)udd->phys_base + UART_FCR, FCR_CONFIG_VAL); } usr = UAPI_REG_READ_VAL32((uintptr_t)udd->phys_base + UART_USR); } UAPI_REG_WRITE32((uintptr_t)udd->phys_base + UART_LCR, val); lcr = UAPI_REG_READ_VAL32((uintptr_t)udd->phys_base + UART_LCR); } } __init static void uart_drv_set_attr(EXT_CONST uart_driver_data_t *udd) { unsigned int baudrate; unsigned int lcr = 0; unsigned int mcr; unsigned int divider; unsigned char divider_h; unsigned char divider_l; unsigned int uv_int_save; /* Caculate devide */ baudrate= uapi_min(CONFIG_MAX_BAUDRATE, udd->attr.baudrate); baudrate = 16 * baudrate; /* Calculate the baud rate and multiply by 16 */ divider = ((g_cfg_uart_clock * 10) / baudrate + 5) / 10; /* Serial clock *10/baud rate + 5 */ divider_h = ((divider) & 0xff00) >> 8; /* Exchange high and low 8 */ divider_l = ((divider) & 0x00ff); if (udd->attr.data_bits == 5) { /* data bit, 5bit */ lcr |= (DATA_BIT_5); } else if (udd->attr.data_bits == 6) { /* data 6bit */ lcr |= (DATA_BIT_6); } else if (udd->attr.data_bits == 7) { /* data 7bit */ lcr |= (DATA_BIT_7); } else { /* data 8bit */ lcr |= (DATA_BIT_8); } if (udd->attr.stop_bits == 1) { lcr |= (STOP_BIT_1); } else if (udd->attr.stop_bits == STOP_BIT_1P5 || udd->attr.stop_bits == STOP_BIT_2) { lcr |= (STOP_BIT_2); } if (udd->attr.parity == PARITY_NONE) { lcr |= (PARITY_NONE); } else if (udd->attr.parity == PARITY_ODD) { lcr |= (PARITY_ODD); } else if (udd->attr.parity == PARITY_EVEN) { lcr |= (PARITY_EVEN); } uv_int_save = uart_interrupt_disable(udd); /* loopback mode */ mcr = UAPI_REG_READ_VAL32((uintptr_t)udd->phys_base + UART_MCR); UAPI_REG_WRITE32((uintptr_t)udd->phys_base + UART_MCR, mcr | (1 << 4)); /* get 4 bit */ writelcr(udd, 0x80); /* configure DLL and DLH */ UAPI_REG_WRITE32((uintptr_t)udd->phys_base + UART_DLL, divider_l); UAPI_REG_WRITE32((uintptr_t)udd->phys_base + UART_DLH, divider_h); writelcr(udd, lcr); mcr = UAPI_REG_READ_VAL32((uintptr_t)udd->phys_base + UART_MCR); UAPI_REG_WRITE32((uintptr_t)udd->phys_base + UART_MCR, mcr & (~(1 << 4))); /* get low 4 bit */ uart_interrupt_restore(udd, uv_int_save); } /* uops */ __init static int uart_drv_startup(uart_driver_data_t *udd) { unsigned int ret; unsigned int fcr; /* disable interrupt */ UAPI_REG_WRITE32((uintptr_t)udd->phys_base + UART_IER, 0); /* set baudrate,data_bit,stop_bit,parity */ uart_drv_set_attr(udd); /* reset and enable FIFO */ fcr = FCR_CONFIG_VAL; UAPI_REG_WRITE32((uintptr_t)udd->phys_base + UART_FCR, fcr); /* creat interrupt function for uart */ ret = uapi_irq_request(udd->irq_num, 3, (lisr_proc_func)uart_drv_irq, (uintptr_t)udd); /* priority 3 */ if (ret == 0) { uart_rx_interrupt_enable(udd); uart_tf_interrupt_disable(udd); uapi_irq_enable((td_u32)udd->irq_num); } return (td_s32)ret; } __init static void uart_drv_shutdown(const struct uart_driver_data *udd) { uart_tf_interrupt_disable(udd); uart_tx_interrupt_disable(udd); uart_rx_interrupt_disable(udd); uapi_irq_disable((td_u32)udd->irq_num); LOS_HwiDelete(udd->irq_num); } EXT_PRV int uart_drv_start_tx(struct uart_driver_data *udd, const char *buf, size_t count) { unsigned int idx = 0; if (count > UART_FIFO_TX_SIZE) { return (int)EXT_ERR_FAILURE; } /* if tx fifo is empty, write data to fifo */ while (idx < count) { UAPI_REG_WRITE32((uintptr_t)udd->phys_base + UART_THR, (unsigned)(td_u8)buf[idx]); if (udd->uart_stat_info.send_last_context.num < EXT_UART_LAST_RECORD_BYTE_COUNT) { udd->uart_stat_info.send_last_context.data[udd->uart_stat_info.send_last_context.num] = (unsigned char)buf[idx]; } udd->uart_stat_info.send_last_context.num++; udd->uart_stat_info.send_last_context.num &= (EXT_UART_LAST_RECORD_BYTE_COUNT - 1); idx++; } #ifdef UART_DEBUG_INFO udd->uart_stat_info.send_irq_data_cnt += count; #endif return (int)count; } __init static int uart_drv_ioctl(uart_driver_data_t *udd) { unsigned int fcr; /* wait for send finish */ uart_drv_set_attr(udd); /* reset and enable FIFO */ fcr = FCR_CONFIG_VAL; UAPI_REG_WRITE32(udd->phys_base + UART_FCR, fcr); uart_rx_interrupt_enable(udd); return 0; } uart_ops_t g_uart_driver_uops = { .startup = uart_drv_startup, .shutdown = uart_drv_shutdown, .start_tx = uart_drv_start_tx, .ioctl = uart_drv_ioctl, };