/** * Kneron Peripheral API - UART * * Copyright (C) 2019 Kneron, Inc. All rights reserved. * */ //#define UART_DEBUG #include #include #include #include #include "kdrv_uart.h" #include "kdrv_scu_ext.h" #include "kdrv_clock.h" // for delay_us() static kdrv_uart_handle_t handle0 = 0; #define SERIAL_THR 0x00 /* Transmitter Holding Register(Write).*/ #define SERIAL_RBR 0x00 /* Receive Buffer register (Read).*/ #define SERIAL_IER 0x04 /* Interrupt Enable register.*/ #define SERIAL_IIR 0x08 /* Interrupt Identification register(Read).*/ #define SERIAL_FCR 0x08 /* FIFO control register(Write).*/ #define SERIAL_LCR 0x0C /* Line Control register.*/ #define SERIAL_MCR 0x10 /* Modem Control Register.*/ #define SERIAL_LSR 0x14 /* Line status register(Read) .*/ #define SERIAL_MSR 0x18 /* Modem Status register (Read).*/ #define SERIAL_SPR 0x1C /* Scratch pad register */ #define SERIAL_DLL 0x0 /* Divisor Register LSB */ #define SERIAL_DLM 0x4 /* Divisor Register MSB */ #define SERIAL_PSR 0x8 /* Prescale Divison Factor */ #define SERIAL_MDR 0x20 #define SERIAL_ACR 0x24 #define SERIAL_TXLENL 0x28 #define SERIAL_TXLENH 0x2C #define SERIAL_MRXLENL 0x30 #define SERIAL_MRXLENH 0x34 #define SERIAL_PLR 0x38 #define SERIAL_FMIIR_PIO 0x3C #define SERIAL_FEATURE 0x68 /* IER Register */ #define SERIAL_IER_DR 0x1 /* Data ready Enable */ #define SERIAL_IER_TE 0x2 /* THR Empty Enable */ #define SERIAL_IER_RLS 0x4 /* Receive Line Status Enable */ #define SERIAL_IER_MS 0x8 /* Modem Staus Enable */ /* IIR Register */ #define SERIAL_IIR_NONE 0x1 /* No interrupt pending */ #define SERIAL_IIR_RLS 0x6 /* Receive Line Status */ #define SERIAL_IIR_DR 0x4 /* Receive Data Ready */ #define SERIAL_IIR_TIMEOUT 0xc /* Receive Time Out */ #define SERIAL_IIR_TE 0x2 /* THR Empty */ #define SERIAL_IIR_MODEM 0x0 /* Modem Status */ /* FCR Register */ #define SERIAL_FCR_FE 0x1 /* FIFO Enable */ #define SERIAL_FCR_RXFR 0x2 /* Rx FIFO Reset */ #define SERIAL_FCR_TXFR 0x4 /* Tx FIFO Reset */ /* LCR Register */ #define SERIAL_LCR_LEN5 0x0 #define SERIAL_LCR_LEN6 0x1 #define SERIAL_LCR_LEN7 0x2 #define SERIAL_LCR_LEN8 0x3 #define SERIAL_LCR_STOP 0x4 #define SERIAL_LCR_EVEN 0x18 /* Even Parity */ #define SERIAL_LCR_ODD 0x8 /* Odd Parity */ #define SERIAL_LCR_PE 0x8 /* Parity Enable */ #define SERIAL_LCR_SETBREAK 0x40 /* Set Break condition */ #define SERIAL_LCR_STICKPARITY 0x20 /* Stick Parity Enable */ #define SERIAL_LCR_DLAB 0x80 /* Divisor Latch Access Bit */ /* LSR Register */ #define SERIAL_LSR_DR 0x1 /* Data Ready */ #define SERIAL_LSR_OE 0x2 /* Overrun Error */ #define SERIAL_LSR_PE 0x4 /* Parity Error */ #define SERIAL_LSR_FE 0x8 /* Framing Error */ #define SERIAL_LSR_BI 0x10 /* Break Interrupt */ #define SERIAL_LSR_THRE 0x20 /* THR Empty */ #define SERIAL_LSR_TE 0x40 /* Transmitte Empty */ #define SERIAL_LSR_DE 0x80 /* FIFO Data Error */ /* MCR Register */ #define SERIAL_MCR_DTR 0x1 /* Data Terminal Ready */ #define SERIAL_MCR_RTS 0x2 /* Request to Send */ #define SERIAL_MCR_OUT1 0x4 /* output 1 */ #define SERIAL_MCR_OUT2 0x8 /* output2 or global interrupt enable */ #define SERIAL_MCR_LPBK 0x10 /* loopback mode */ /* MSR Register */ #define SERIAL_MSR_DELTACTS 0x1 /* Delta CTS */ #define SERIAL_MSR_DELTADSR 0x2 /* Delta DSR */ #define SERIAL_MSR_TERI 0x4 /* Trailing Edge RI */ #define SERIAL_MSR_DELTACD 0x8 /* Delta CD */ #define SERIAL_MSR_CTS 0x10 /* Clear To Send */ #define SERIAL_MSR_DSR 0x20 /* Data Set Ready */ #define SERIAL_MSR_RI 0x40 /* Ring Indicator */ #define SERIAL_MSR_DCD 0x80 /* Data Carrier Detect */ /* MDR register */ #define SERIAL_MDR_MODE_SEL 0x03 #define SERIAL_MDR_UART 0x0 #define SERIAL_MDR_SIR 0x1 #define SERIAL_MDR_FIR 0x2 /* ACR register */ #define SERIAL_ACR_TXENABLE 0x1 #define SERIAL_ACR_RXENABLE 0x2 #define SERIAL_ACR_SET_EOT 0x4 #define IIR_CODE_MASK 0xf #define SERIAL_IIR_TX_FIFO_FULL 0x10 #define UART_INITIALIZED (1 << 0) #define UART_BASIC_CONFIGURED (1 << 2) #define UART_FIFO_RX_CONFIGURED (1 << 3) #define UART_FIFO_TX_CONFIGURED (1 << 4) #define UART_TX_ENABLED (1 << 5) #define UART_RX_ENABLED (1 << 6) #define UART_LOOPBACK_ENABLED (1 << 7) #define SERIAL_FIFO_DEPTH_REG 0x68 #define SERIAL_FIFO_DEPTH_16B 0x1 #define SERIAL_FIFO_DEPTH_32B 0x2 #define SERIAL_FIFO_DEPTH_64B 0x4 #define SERIAL_FIFO_DEPTH_128B 0x8 #define SERIAL_FIFO_TRIG_LVEL_1 0x0 #define SERIAL_FIFO_TRIG_LVEL_4 0x1 #define SERIAL_FIFO_TRIG_LVEL_8 0x2 #define SERIAL_FIFO_TRIG_LVEL_14 0x3 #define DEFAULT_SYNC_TIMEOUT_CHARS_TIME 0 //default is no timeout #define UART_IRQ_CNT_DBG 0 /*********************************************************************************** Global variables ************************************************************************************/ void UART0_ISR(void); void UART1_ISR(void); void UART2_ISR(void); void UART3_ISR(void); void UART4_ISR(void); uart_drv_ctx_t gDrvCtx; uint32_t UART_PORT[5] = {UART_FTUART010_0_PA_BASE, UART_FTUART010_1_PA_BASE, UART_FTUART010_1_1_PA_BASE, UART_FTUART010_1_2_PA_BASE, UART_FTUART010_1_3_PA_BASE}; uint32_t uart_baud_rate_map[11] = {BAUD_1200,BAUD_2400,BAUD_4800,BAUD_9600,BAUD_14400 ,BAUD_19200 ,BAUD_38400 ,BAUD_57600 ,BAUD_115200,BAUD_460800,BAUD_921600}; /*********************************************************************************** local variables ************************************************************************************/ IRQn_Type gUartIRQTbl[5] = { UART_FTUART010_0_IRQ, //UART0 UART_FTUART010_1_IRQ, //UART1 UART_FTUART010_1_1_IRQ, //UART2 UART_FTUART010_1_2_IRQ, //UART3 UART_FTUART010_1_3_IRQ //UART4 }; uart_isr_t gUartISRs[5] = { UART0_ISR, UART1_ISR, UART2_ISR, UART3_ISR, UART4_ISR}; uint32_t gUartClk[5] = { UART_CLOCK, UART_CLOCK_2, UART_CLOCK_2, UART_CLOCK, UART_CLOCK}; static bool gDriverInitialized = false; #if (defined(UART_IRQ_CNT_DBG) && UART_IRQ_CNT_DBG == 1) volatile uint32_t tx_isr_cnt[TOTAL_UART_DEV]={0}; volatile uint32_t rx_isr_cnt[TOTAL_UART_DEV]={0}; #endif /*********************************************************************************** local functions ************************************************************************************/ static void uart_serial_init(DRVUART_PORT port_no, uint32_t baudrate, uint32_t parity, uint32_t num, uint32_t len, uint32_t interruptMode) { uint32_t lcr; lcr = inw(UART_PORT[port_no] + SERIAL_LCR) & ~SERIAL_LCR_DLAB; /* Set DLAB=1 */ outw(UART_PORT[port_no] + SERIAL_LCR, SERIAL_LCR_DLAB); /* Set baud rate */ outw(UART_PORT[port_no] + SERIAL_DLM, ((baudrate & 0xff00) >> 8)); //ycmo090930 outw(UART_PORT[port_no] + SERIAL_DLL, (baudrate & 0xff)); lcr &= 0xc0; switch (parity) { case PARITY_NONE: //do nothing break; case PARITY_ODD: lcr |= SERIAL_LCR_ODD; break; case PARITY_EVEN: lcr |= SERIAL_LCR_EVEN; break; case PARITY_MARK: lcr |= (SERIAL_LCR_STICKPARITY | SERIAL_LCR_ODD); break; case PARITY_SPACE: lcr |= (SERIAL_LCR_STICKPARITY | SERIAL_LCR_EVEN); break; default: break; } if (num == 2) lcr |= SERIAL_LCR_STOP; len -= 5; lcr |= len; outw(UART_PORT[port_no] + SERIAL_LCR, lcr); if (1 == interruptMode) outw(UART_PORT[port_no] + SERIAL_FCR, SERIAL_FCR_FE); } static char uart_get_serial_char(DRVUART_PORT port_no) { char Ch; uint32_t status; do { status = inw(UART_PORT[port_no] + SERIAL_LSR); } while (!((status & SERIAL_LSR_DR) == SERIAL_LSR_DR)); // wait until Rx ready Ch = inw(UART_PORT[port_no] + SERIAL_RBR); return (Ch); } uint32_t uart_get_status(DRVUART_PORT port_no) { uint32_t status; status = inw(UART_PORT[port_no] + SERIAL_LSR); return status; } #if 0 static int32_t kdp_uart_get_default_timeout(uint32_t baud) { int32_t timeout; switch (baud) { case BAUD_921600: { timeout = (DEFAULT_SYNC_TIMEOUT_CHARS_TIME * 100) / 9216; break; } case BAUD_460800: { timeout = (DEFAULT_SYNC_TIMEOUT_CHARS_TIME * 100) / 4608; break; } case BAUD_115200: { timeout = (DEFAULT_SYNC_TIMEOUT_CHARS_TIME * 100) / 1152; break; } case BAUD_57600: { timeout = (DEFAULT_SYNC_TIMEOUT_CHARS_TIME * 100) / 576; break; } case BAUD_38400: { timeout = (DEFAULT_SYNC_TIMEOUT_CHARS_TIME * 100) / 384; break; } case BAUD_19200: { timeout = (DEFAULT_SYNC_TIMEOUT_CHARS_TIME * 100) / 192; break; } case BAUD_14400: { timeout = (DEFAULT_SYNC_TIMEOUT_CHARS_TIME * 100) / 144; break; } case BAUD_9600: { timeout = (DEFAULT_SYNC_TIMEOUT_CHARS_TIME * 100) / 96; break; } case BAUD_4800: { timeout = (DEFAULT_SYNC_TIMEOUT_CHARS_TIME * 100) / 48; break; } case BAUD_2400: { timeout = (DEFAULT_SYNC_TIMEOUT_CHARS_TIME * 100) / 240; break; } case BAUD_1200: { timeout = (DEFAULT_SYNC_TIMEOUT_CHARS_TIME * 100) / 12; break; } default: timeout = DEFAULT_SYNC_TIMEOUT_CHARS_TIME; } return timeout; } #endif static uart_driver_handle_t *uart_get_drv_hdl(uint16_t port) { #ifdef UART_DEBUG if (port >= MAX_UART_INST) { dbg_msg("Error: invalid port number\n"); return NULL; } #endif return gDrvCtx.uart_dev[port]; } /* calculate fifo config: depth and trigger level pCfg->bEnFifo and pCfg->fifo_depth was set by client before calling in */ static kdrv_status_t kdp_calculate_fifo_cfg(uart_driver_handle_t *const pDrv, uint32_t val, kdrv_uart_fifo_config_t *pCfg) { int32_t depth; if (pCfg->bEnFifo == false) { pCfg->fifo_trig_level = SERIAL_FIFO_TRIG_LVEL_1; return KDRV_STATUS_OK; } depth = pDrv->res.fifo_depth; if (val <= depth * 4) { pCfg->fifo_trig_level = SERIAL_FIFO_TRIG_LVEL_1; } else if ((val > depth * 4) && (val <= depth * 8)) { pCfg->fifo_trig_level = SERIAL_FIFO_TRIG_LVEL_4; } else if ((val > depth * 8) && (val <= depth * 14)) { pCfg->fifo_trig_level = SERIAL_FIFO_TRIG_LVEL_8; } else if (val > depth * 14) { pCfg->fifo_trig_level = SERIAL_FIFO_TRIG_LVEL_14; } return KDRV_STATUS_OK; } /** \fn void UART_IRQHandler (UART_RESOURCES *uart) \brief UART Interrupt handler. \param[in] uart Pointer to UART resources */ static void UART_TX_ISR(uart_driver_handle_t *const uart) { uint32_t status; uint32_t ww; uint16_t port_no; bool bFifo; ww = uart->iir; bFifo = ((ww & 0xc0) != 0) ? true : false; port_no = uart->uart_port; uart->info.xfer.tx_cnt += uart->info.xfer.batch_tx_num; if (uart->info.xfer.tx_num > uart->info.xfer.tx_cnt) { if (bFifo == false) //NO FIFO { do { status = inw(UART_PORT[port_no] + SERIAL_LSR); } while (!((status & SERIAL_LSR_THRE) == SERIAL_LSR_THRE)); // wait until Tx ready outw(UART_PORT[port_no] + SERIAL_THR, *(uart->info.xfer.tx_buf++)); } else //FIFO mode { uint32_t tx_num = MAX_FIFO_TX; if (uart->info.xfer.tx_num < uart->info.xfer.tx_cnt+MAX_FIFO_TX) { tx_num = uart->info.xfer.tx_num - uart->info.xfer.tx_cnt; } for(uint32_t i = 0; i < tx_num ; i++) { outw(UART_PORT[port_no] + SERIAL_THR, *uart->info.xfer.tx_buf); uart->info.xfer.tx_buf++; } uart->info.xfer.batch_tx_num = tx_num; } } if (uart->info.xfer.tx_num <= uart->info.xfer.tx_cnt) { // need to add: determine if TX fifo is empty status = inw(UART_PORT[port_no] + SERIAL_LSR); if ((status & SERIAL_LSR_THRE) == SERIAL_LSR_THRE) //TX empty to make sure FIFO data to tranmit shift { uint32_t ww = inw(UART_PORT[port_no] + SERIAL_IER); ww &= ~SERIAL_IER_TE; // disable Tx empty interrupt outw(UART_PORT[port_no] + SERIAL_IER, ww); if (uart->info.cb_event) uart->info.cb_event(UART_TX_DONE); uart->info.status.tx_busy = 0; } } #if (defined(UART_IRQ_CNT_DBG) && UART_IRQ_CNT_DBG == 1) tx_isr_cnt[uart->uart_port]++; #endif } static void UART_RX_ISR(uart_driver_handle_t *const uart) { uint32_t ww; uint16_t port_no; ww = uart->iir; port_no = uart->uart_port; if (((ww & SERIAL_IIR_DR) == SERIAL_IIR_DR) || ((ww & SERIAL_IIR_TIMEOUT) == SERIAL_IIR_TIMEOUT)) { if ((ww & 0xc0) == 0) //non FIFO mode { *(uart->info.xfer.rx_buf++) = (uint8_t)uart_get_serial_char((DRVUART_PORT)port_no); uart->info.xfer.rx_cnt++; if (uart->info.xfer.rx_cnt == uart->info.xfer.rx_num) { ww = inw(UART_PORT[port_no] + SERIAL_IER); ww &= ~SERIAL_IER_DR; // disable Rx empty interrupt outw(UART_PORT[port_no] + SERIAL_IER, ww); if (uart->info.cb_event) uart->info.cb_event(UART_RX_DONE); } } else //FIFO mode { /* Read the data from FIFO buffer */ ww = inw(UART_PORT[port_no] + SERIAL_LSR); if ((ww & SERIAL_LSR_DR) == SERIAL_LSR_DR) { while((inw(UART_PORT[port_no] + SERIAL_LSR) & SERIAL_LSR_DR) == SERIAL_LSR_DR) { *(uart->info.xfer.rx_buf++) = (uint8_t)inw(UART_PORT[port_no] + SERIAL_RBR); uart->info.xfer.rx_cnt++; } if (uart->info.xfer.rx_num <= uart->info.xfer.rx_cnt) { ww = inw(UART_PORT[port_no] + SERIAL_IER); ww &= ~SERIAL_IER_DR; // disable Rx empty interrupt outw(UART_PORT[port_no] + SERIAL_IER, ww); if (uart->info.cb_event) uart->info.cb_event(UART_RX_DONE); } } else { ww = uart->iir; if ((ww & SERIAL_IIR_TIMEOUT) == SERIAL_IIR_TIMEOUT) // means end of frame { while((inw(UART_PORT[port_no] + SERIAL_LSR) & SERIAL_LSR_DR) == SERIAL_LSR_DR) { *(uart->info.xfer.rx_buf++) = (uint8_t)inw(UART_PORT[port_no] + SERIAL_RBR); uart->info.xfer.rx_cnt++; } if (uart->info.xfer.rx_num <= uart->info.xfer.rx_cnt) { ww = inw(UART_PORT[port_no] + SERIAL_IER); ww &= ~SERIAL_IER_DR; // disable Rx empty interrupt outw(UART_PORT[port_no] + SERIAL_IER, ww); if (uart->info.cb_event) uart->info.cb_event(UART_RX_TIMEOUT); } } } } uart->info.status.rx_busy = 0; #if (defined(UART_IRQ_CNT_DBG) && UART_IRQ_CNT_DBG == 1) rx_isr_cnt[uart->uart_port]++; #endif } } /*********************************************************************************** global functions ************************************************************************************/ void UART_ISR(uint8_t port_no) { uart_driver_handle_t *pHdl = uart_get_drv_hdl(port_no); uint32_t ww = inw(UART_PORT[port_no] + SERIAL_IIR); pHdl->iir = ww; if ((ww & IIR_CODE_MASK) == SERIAL_IIR_RLS) // errors: overrun/parity/framing/break { ww = inw(UART_PORT[port_no] + SERIAL_LSR); //Read LSR to reset interrupt if (ww & SERIAL_LSR_OE) { pHdl->info.status.rx_overflow = 1; } if (ww & SERIAL_LSR_PE) { pHdl->info.status.rx_parity_error = 1; } if (ww & SERIAL_LSR_BI) { pHdl->info.status.rx_break = 1; } if (ww & SERIAL_LSR_FE) { pHdl->info.status.rx_framing_error = 1; } } if (((ww & IIR_CODE_MASK) == SERIAL_IIR_DR) // Rx data ready in FIFO || ((ww & IIR_CODE_MASK) == SERIAL_IIR_TIMEOUT)) // Character Reception Timeout { if (pHdl->info.status.rx_busy) { UART_RX_ISR(pHdl); } else { ww = inw(UART_PORT[port_no] + SERIAL_RBR); //Read RBR to reset interrupt } } if ((ww & IIR_CODE_MASK) == SERIAL_IIR_TE) // Transmitter Holding Register Empty { if (pHdl->info.status.tx_busy) { UART_TX_ISR(pHdl); } } NVIC_ClearPendingIRQ((IRQn_Type)pHdl->res.irq_num); NVIC_EnableIRQ((IRQn_Type)pHdl->res.irq_num); } void UART0_ISR(void) { UART_ISR(0); } void UART1_ISR(void) { UART_ISR(1); } void UART2_ISR(void) { UART_ISR(2); } void UART3_ISR(void) { UART_ISR(3); } void UART4_ISR(void) { UART_ISR(4); } /* Init the UART device driver, it shall be called once in lifecycle */ kdrv_status_t kdrv_uart_initialize(void) { if (gDriverInitialized == false) { memset(&gDrvCtx, 0, sizeof(gDrvCtx)); gDriverInitialized = true; } return KDRV_STATUS_OK; } kdrv_status_t kdrv_uart_console_init(uint8_t uart_dev, uint32_t baudrate, kdrv_uart_callback_t cb) { kdrv_uart_handle_t handle; kdrv_status_t sts = kdrv_uart_open(&handle, uart_dev, UART_MODE_SYNC_RX | UART_MODE_SYNC_TX, cb); if (sts != KDRV_STATUS_OK) return sts; kdrv_uart_config_t cfg; cfg.baudrate = uart_baud_rate_map[baudrate]; cfg.data_bits = 8; cfg.frame_length = 0; cfg.stop_bits = 1; cfg.parity_mode = PARITY_NONE; cfg.fifo_en = false; #ifdef UART_RX_IRQ cfg.irq_en = false; #endif sts = kdrv_uart_configure(handle, UART_CTRL_CONFIG, (void *)&cfg); if (sts != KDRV_STATUS_OK) return sts; return KDRV_STATUS_OK; } kdrv_status_t kdrv_uart_uninitialize(void) { return KDRV_STATUS_OK; } /* Open one UART port Input: com_port: UART port id mode: that is a combination of Tx mode|Rx mode, make sure set both, such as UART_MODE_ASYN_RX|UART_MODE_SYNC_TX cb: callback function Output: return device handle: >=0 means success; -1 means open fail */ kdrv_status_t kdrv_uart_open(kdrv_uart_handle_t *handle, uint8_t com_port, uint32_t mode, kdrv_uart_callback_t cb) { uint32_t ww; #ifdef UART_DEBUG if (com_port >= TOTAL_UART_DEV) { dbg_msg("Invalid com_port\n"); return KDRV_STATUS_ERROR; } #endif if (gDrvCtx.active_dev[com_port] == true) { #ifdef UART_DEBUG dbg_msg("This UART device has been opened\n"); #endif return KDRV_STATUS_ERROR; } uart_driver_handle_t *pDrv = (uart_driver_handle_t *)malloc(sizeof(uart_driver_handle_t)); if (pDrv == NULL) { #ifdef UART_DEBUG err_msg("Error: memory alloc failed\n"); #endif return KDRV_STATUS_ERROR; } if (((mode & UART_MODE_ASYN_TX) || (mode & UART_MODE_ASYN_RX)) && (cb == NULL)) { #ifdef UART_DEBUG err_msg("Error: Async mode needs callback function\n"); #endif return KDRV_STATUS_ERROR; } gDrvCtx.uart_dev[com_port] = pDrv; pDrv->uart_port = com_port; pDrv->state = UART_UNINIT; // now use a common capbilities for all UARTs, may introduce an array if diff UART has diff capbilities pDrv->info.cb_event = cb; pDrv->info.mode = mode; pDrv->info.status.tx_busy = 0; pDrv->info.status.rx_busy = 0; pDrv->info.status.tx_underflow = 0; pDrv->info.status.rx_overflow = 0; pDrv->info.status.rx_break = 0; pDrv->info.status.rx_framing_error = 0; pDrv->info.status.rx_parity_error = 0; pDrv->info.xfer.tx_num = 0; pDrv->info.xfer.rx_num = 0; pDrv->info.xfer.tx_cnt = 0; pDrv->info.xfer.rx_cnt = 0; pDrv->res.irq_num = gUartIRQTbl[com_port]; pDrv->res.isr = gUartISRs[com_port]; ww = inw(UART_PORT[com_port] + SERIAL_FIFO_DEPTH_REG); ww &= 0x0f; pDrv->res.fifo_depth = ww; pDrv->res.fifo_len = 16 * ww; pDrv->res.hw_base = UART_PORT[com_port]; pDrv->res.clock = gUartClk[com_port]; pDrv->nTimeOutRx = DEFAULT_SYNC_TIMEOUT_CHARS_TIME; //suppose default baud = 115200 pDrv->nTimeOutTx = DEFAULT_SYNC_TIMEOUT_CHARS_TIME; //suppose default baud = 115200 pDrv->res.rx_fifo_threshold = SERIAL_FIFO_TRIG_LVEL_14; pDrv->res.tx_fifo_threshold = SERIAL_FIFO_TRIG_LVEL_14; pDrv->config.fifo_en = 0; gDrvCtx.total_open_uarts++; gDrvCtx.active_dev[com_port] = true; pDrv->info.flags |= UART_INITIALIZED; // enable clocks / power / irq { uint32_t clock_enable_reg2 = inw(SCU_EXTREG_PA_BASE + 0x1c); if (com_port == UART0_DEV) clock_enable_reg2 |= (0x1 << 8); else clock_enable_reg2 |= (0x1 << (11 + com_port)); outw(SCU_EXTREG_PA_BASE + 0x1c, clock_enable_reg2); kdrv_uart_fifo_config_t cfg; /* config FIFO trigger value with default value or client set via control API*/ cfg.fifo_trig_level = pDrv->res.rx_fifo_threshold; cfg.bEnFifo = pDrv->config.fifo_en; kdrv_uart_configure(com_port, UART_CTRL_FIFO_RX, (void *)&cfg); cfg.fifo_trig_level = pDrv->res.tx_fifo_threshold; cfg.bEnFifo = (cfg.fifo_trig_level == 0) ? false : true; kdrv_uart_configure(com_port, UART_CTRL_FIFO_TX, (void *)&cfg); NVIC_ClearPendingIRQ((IRQn_Type)pDrv->res.irq_num); } *handle = (kdrv_uart_handle_t)com_port; return KDRV_STATUS_OK; } /* close the device Input: handle: device handle return: 0 - success; -1 - failure */ kdrv_status_t kdrv_uart_close(kdrv_uart_handle_t handle) { uint32_t com_port = handle; #ifdef UART_DEBUG if (com_port >= TOTAL_UART_DEV) { dbg_msg("Invalid parameter\n"); return KDRV_STATUS_ERROR; } if (gDrvCtx.active_dev[com_port] == false) { dbg_msg("This UART device has been closed\n"); return KDRV_STATUS_ERROR; } #endif uart_driver_handle_t *pDrv = gDrvCtx.uart_dev[com_port]; if (pDrv->info.flags == 0) { // Driver not initialized return KDRV_STATUS_ERROR; } // Reset USART status flags pDrv->info.flags = 0; free(gDrvCtx.uart_dev[com_port]); gDrvCtx.uart_dev[com_port] = NULL; gDrvCtx.active_dev[com_port] = false; gDrvCtx.total_open_uarts--; return KDRV_STATUS_OK; } /* Set control for the device Input: handle: device handle prop: control enumeration pVal: pointer to control value/structure return: None */ kdrv_status_t kdrv_uart_configure(kdrv_uart_handle_t handle, kdrv_uart_control_t prop, uint8_t *pVal) { uint32_t com_port = handle; #ifdef UART_DEBUG if (com_port >= TOTAL_UART_DEV) { dbg_msg("Invalid parameter\n"); return KDRV_STATUS_ERROR; } if (gDrvCtx.active_dev[com_port] == false) { dbg_msg("This UART device has been closed\n"); return KDRV_STATUS_ERROR; } #endif uart_driver_handle_t *pDrv = gDrvCtx.uart_dev[com_port]; #ifdef UART_DEBUG if ((pDrv->info.flags & UART_INITIALIZED) == 0) { // Return error, if USART is not initialized return KDRV_STATUS_ERROR; } #endif DRVUART_PORT port_no = (DRVUART_PORT)pDrv->uart_port; switch (prop) { case UART_CTRL_CONFIG: { kdrv_uart_config_t cfg = *(kdrv_uart_config_t *)pVal; uint32_t baudrate = cfg.baudrate; uint32_t parity = cfg.parity_mode; uint32_t num = cfg.stop_bits; uint32_t len = cfg.data_bits; uart_serial_init(port_no, baudrate, parity, num, len, 0); pDrv->config.baudrate = baudrate; pDrv->config.data_bits = cfg.data_bits; pDrv->config.stop_bits = num; pDrv->config.parity_mode = parity; pDrv->config.fifo_en = cfg.fifo_en; pDrv->res.tx_fifo_threshold = 0; pDrv->res.rx_fifo_threshold = 0; pDrv->nTimeOutTx = 0; //kdp_uart_get_default_timeout(baudrate); pDrv->nTimeOutRx = 0; //kdp_uart_get_default_timeout(baudrate); pDrv->info.flags |= UART_BASIC_CONFIGURED; pDrv->state = UART_INIT_DONE; break; } case UART_CTRL_TX_EN: { uint32_t ww = inw(UART_PORT[port_no] + SERIAL_IER); if (*pVal == 0) { ww &= ~SERIAL_IER_TE; // disable TX outw(UART_PORT[port_no] + SERIAL_IER, ww); pDrv->info.flags &= ~UART_TX_ENABLED; } else { ww |= SERIAL_IER_TE; // enable TX outw(UART_PORT[port_no] + SERIAL_IER, ww); pDrv->info.flags |= UART_TX_ENABLED; } break; } case UART_CTRL_RX_EN: { uint32_t ww = inw(UART_PORT[port_no] + SERIAL_IER); if (*pVal == 0) { ww &= ~SERIAL_IER_DR; // disable Rx outw(UART_PORT[port_no] + SERIAL_IER, ww); pDrv->info.flags &= ~UART_RX_ENABLED; } else { ww |= SERIAL_IER_DR; // enable Rx outw(UART_PORT[port_no] + SERIAL_IER, ww); pDrv->info.flags |= UART_RX_ENABLED; } break; } case UART_CTRL_ABORT_TX: { /* disable Tx interrupt */ uint32_t ww = inw(UART_PORT[port_no] + SERIAL_IER); ww &= ~SERIAL_IER_TE; outw(UART_PORT[port_no] + SERIAL_IER, ww); /* reset Tx FIFO */ ww = inw(UART_PORT[port_no] + SERIAL_FCR); ww |= (SERIAL_FCR_TXFR | SERIAL_FCR_FE); outw(UART_PORT[port_no] + SERIAL_FCR, ww); pDrv->info.status.tx_busy = 0; break; } case UART_CTRL_ABORT_RX: { /* disable Rx interrupt */ uint32_t ww = inw(UART_PORT[port_no] + SERIAL_IER); ww &= ~SERIAL_IER_DR; outw(UART_PORT[port_no] + SERIAL_IER, ww); /* reset Rx FIFO */ ww = inw(UART_PORT[port_no] + SERIAL_FCR); ww |= (SERIAL_FCR_RXFR | SERIAL_FCR_FE); outw(UART_PORT[port_no] + SERIAL_FCR, ww); pDrv->info.status.rx_busy = 0; break; } case UART_CTRL_FIFO_RX: { uint32_t ww; kdrv_uart_fifo_config_t *pCfg = (kdrv_uart_fifo_config_t *)pVal; uint8_t trig_lvl = 0x3 & pCfg->fifo_trig_level; if(pCfg->bEnFifo) { ww = inw(UART_PORT[port_no] + SERIAL_FCR); ww &= ~(0x3 << 6); ww |= (trig_lvl << 6) | SERIAL_FCR_RXFR | SERIAL_FCR_FE; // val + reset + enable outw(UART_PORT[port_no] + SERIAL_FCR, ww); } else { /* becasue RX/TX FIFO can only be enable/disabled simutanously, cannot be set individually, so set trigger val to 0 for FIFO disable */ ww = inw(UART_PORT[port_no] + SERIAL_FCR); ww &= ~(0x3 << 6); ww |= (trig_lvl << 6) | SERIAL_FCR_RXFR | SERIAL_FCR_FE; // val + reset + enable outw(UART_PORT[port_no] + SERIAL_FCR, ww); } pDrv->info.flags |= UART_FIFO_RX_CONFIGURED; break; } case UART_CTRL_FIFO_TX: { uint32_t ww; kdrv_uart_fifo_config_t *pCfg = (kdrv_uart_fifo_config_t *)pVal; uint8_t trig_lvl = 0x3 & pCfg->fifo_trig_level; if (pCfg->bEnFifo) { ww = inw(UART_PORT[port_no] + SERIAL_FCR); ww &= ~(0x3 << 4); ww |= (trig_lvl << 4) | SERIAL_FCR_TXFR | SERIAL_FCR_FE; // val + reset + enable outw(UART_PORT[port_no] + SERIAL_FCR, ww); } else { /* becasue RX/TX FIFO can only be enable/disabled simutanously, cannot be set individually, so set trigger val to 0 for FIFO disable */ ww = inw(UART_PORT[port_no] + SERIAL_FCR); ww &= ~(0x3 << 4); ww |= (trig_lvl << 4) | SERIAL_FCR_TXFR | SERIAL_FCR_FE; // val + reset + enable outw(UART_PORT[port_no] + SERIAL_FCR, ww); } pDrv->info.flags |= UART_FIFO_TX_CONFIGURED; break; } case UART_CTRL_LOOPBACK: { uint32_t ww = inw(UART_PORT[port_no] + SERIAL_MCR); if (*pVal == 0) { ww &= ~SERIAL_MCR_LPBK; // disable loopback outw(UART_PORT[port_no] + SERIAL_MCR, ww); } else { ww |= SERIAL_MCR_LPBK; // enable loopback outw(UART_PORT[port_no] + SERIAL_MCR, ww); } pDrv->info.flags |= UART_LOOPBACK_ENABLED; break; } case UART_CTRL_TIMEOUT_RX: { pDrv->nTimeOutRx = (int32_t)*pVal; break; } case UART_CTRL_TIMEOUT_TX: { pDrv->nTimeOutTx = (int32_t)*pVal; break; } default: break; } return KDRV_STATUS_OK; } /* Write data to KL520 device, such as command, parameters, but not suitable for chunk data Input: hdl: device handle buf: data buffer len: data buffer length return: driver status */ kdrv_status_t kdrv_uart_write(kdrv_uart_handle_t handle, uint8_t *data, uint32_t len) { uint32_t com_port = handle; #ifdef UART_DEBUG if (com_port >= TOTAL_UART_DEV) { dbg_msg("Invalid parameter\n"); return KDRV_STATUS_ERROR; } if (gDrvCtx.active_dev[com_port] == false) { dbg_msg("This UART device has been closed\n"); return KDRV_STATUS_ERROR; } #endif uart_driver_handle_t *pDrv = gDrvCtx.uart_dev[com_port]; #ifdef UART_DEBUG if ((data == NULL) || (len == 0)) { // Invalid parameters return KDRV_STATUS_ERROR; } #endif if (pDrv->info.status.tx_busy == 1) { // Send is not completed yet return KDRV_STATUS_UART_TX_RX_BUSY; } pDrv->info.status.tx_busy = 1; uint8_t enable = 1; uint32_t write_len; write_len = (len>MAX_FIFO_TX)? MAX_FIFO_TX : len; for(uint32_t i = 0; iuart_port] + SERIAL_THR, *data++); } // Save transmit buffer info pDrv->info.xfer.tx_buf = (uint8_t *)data; pDrv->info.xfer.tx_num = len; pDrv->info.xfer.batch_tx_num = write_len; pDrv->info.xfer.tx_cnt = 0; NVIC_SetVector((IRQn_Type)pDrv->res.irq_num, (uint32_t)pDrv->res.isr); NVIC_EnableIRQ((IRQn_Type)pDrv->res.irq_num); kdrv_uart_configure(pDrv->uart_port, UART_CTRL_TX_EN, &enable); if (pDrv->info.mode & UART_MODE_ASYN_TX) { /* setup TX FIFO re-calculate FIFO trigger value based on buffer length */ if (pDrv->config.fifo_en == true) { kdrv_uart_fifo_config_t cfg; cfg.bEnFifo = true; cfg.fifo_trig_level = 0; // init with 0 kdp_calculate_fifo_cfg(pDrv, len, &cfg); kdrv_uart_configure(com_port, UART_CTRL_FIFO_TX, (uint8_t *)&cfg); } return KDRV_STATUS_UART_TX_RX_BUSY; } else if (pDrv->info.mode & UART_MODE_SYNC_TX) { if (pDrv->info.cb_event) pDrv->info.cb_event(UART_TRANSFER_COMPLETE); else { while(pDrv->info.status.tx_busy) { __WFI(); } } return KDRV_STATUS_OK; } else { #ifdef UART_DEBUG dbg_msg("Error: Sync/Async mode was not set\n"); #endif return KDRV_STATUS_ERROR; } } /* Read character data from KL520 device Input: handle: device handle return: character data */ kdrv_status_t kdrv_uart_get_char(kdrv_uart_handle_t handle, char *ch) { uint32_t com_port = handle; #ifdef UART_DEBUG if (com_port >= TOTAL_UART_DEV) { dbg_msg("Invalid parameter\n"); return KDRV_STATUS_ERROR; } if (gDrvCtx.active_dev[com_port] == false) { dbg_msg("This UART device has been closed\n"); return KDRV_STATUS_ERROR; } #endif uart_driver_handle_t *pDrv = gDrvCtx.uart_dev[com_port]; // Check if receiver is busy if (pDrv->info.status.rx_busy == 1) { return KDRV_STATUS_UART_TX_RX_BUSY; } pDrv->info.status.rx_busy = 1; // Clear RX status pDrv->info.status.rx_break = 0; pDrv->info.status.rx_framing_error = 0; pDrv->info.status.rx_overflow = 0; pDrv->info.status.rx_parity_error = 0; // Save receive buffer info pDrv->info.xfer.rx_buf = (uint8_t *)ch; pDrv->info.xfer.rx_cnt = 0; // Save number of data to be received pDrv->info.xfer.rx_num = 1; uint8_t enable = 1; kdrv_uart_configure(pDrv->uart_port, UART_CTRL_RX_EN, &enable); NVIC_SetVector((IRQn_Type)pDrv->res.irq_num, (uint32_t)pDrv->res.isr); NVIC_EnableIRQ((IRQn_Type)pDrv->res.irq_num); if (pDrv->info.mode & UART_MODE_ASYN_RX) { return KDRV_STATUS_UART_TX_RX_BUSY; } else if (pDrv->info.mode & UART_MODE_SYNC_RX) { if (pDrv->info.cb_event) pDrv->info.cb_event(UART_REVEIVE_COMPLETE); else { while(pDrv->info.status.rx_busy) { __WFI(); } } } return KDRV_STATUS_OK; } /** Read data from UART receiver. Input data: buffer for receving data len: size Data buffer size in bytes handle: Driver handle return driver status */ kdrv_status_t kdrv_uart_read(kdrv_uart_handle_t handle, uint8_t *data, uint32_t len) { uint32_t com_port = handle; #ifdef UART_DEBUG if (com_port >= TOTAL_UART_DEV) { dbg_msg("Invalid parameter\n"); return KDRV_STATUS_ERROR; } if (gDrvCtx.active_dev[com_port] == false) { dbg_msg("This UART device has been closed\n"); return KDRV_STATUS_ERROR; } #endif uart_driver_handle_t *pDrv = gDrvCtx.uart_dev[com_port]; #ifdef UART_DEBUG if ((data == NULL) || (len == 0)) { // Invalid parameters return UART_API_INVALID_PARAM; } #endif // Check if receiver is busy if (pDrv->info.status.rx_busy == 1) { return KDRV_STATUS_UART_TX_RX_BUSY; } pDrv->info.status.rx_busy = 1; // Clear RX status pDrv->info.status.rx_break = 0; pDrv->info.status.rx_framing_error = 0; pDrv->info.status.rx_overflow = 0; pDrv->info.status.rx_parity_error = 0; // Save receive buffer info pDrv->info.xfer.rx_buf = (uint8_t *)data; pDrv->info.xfer.rx_cnt = 0; // Save number of data to be received pDrv->info.xfer.rx_num = len; uint8_t enable = 1; kdrv_uart_configure(pDrv->uart_port, UART_CTRL_RX_EN, &enable); NVIC_SetVector((IRQn_Type)pDrv->res.irq_num, (uint32_t)pDrv->res.isr); NVIC_EnableIRQ((IRQn_Type)pDrv->res.irq_num); if (pDrv->info.mode & UART_MODE_ASYN_RX) { return KDRV_STATUS_UART_TX_RX_BUSY; } else if (pDrv->info.mode & UART_MODE_SYNC_RX) { if (pDrv->info.cb_event) pDrv->info.cb_event(UART_REVEIVE_COMPLETE); else { while(pDrv->info.status.rx_busy) { __WFI(); } } return KDRV_STATUS_OK; } else { #ifdef UART_DEBUG dbg_msg("Error: Sync/Async mode was not set\n"); #endif return KDRV_STATUS_ERROR; } } /* get char number in Rx buffer Input: handle: device handle Return: Received bytes */ uint32_t kdrv_uart_get_rx_count(kdrv_uart_handle_t handle) { uint32_t data; uint32_t com_port = handle; uart_driver_handle_t *pDrv = gDrvCtx.uart_dev[com_port]; data = pDrv->info.xfer.rx_cnt; return data; } uint32_t kdrv_uart_get_tx_count(kdrv_uart_handle_t handle) { uint32_t data; uint32_t com_port = handle; uart_driver_handle_t *pDrv = gDrvCtx.uart_dev[com_port]; data = pDrv->info.xfer.tx_cnt; return data; } int32_t fputc(int ch, FILE *f) { char cc; if (ch != '\0') { cc = ch; kdrv_uart_write(handle0, (uint8_t *)&cc, 1); } if (ch == '\n') { cc = '\r'; kdrv_uart_write(handle0, (uint8_t *)&cc, 1); } return ch; } int32_t fgetc(FILE *f) { char c; kdrv_uart_read(handle0, (uint8_t *)&c, 1); return c; }