1354 lines
36 KiB
C
1354 lines
36 KiB
C
/**
|
|
* Kneron Peripheral API - UART
|
|
*
|
|
* Copyright (C) 2019 Kneron, Inc. All rights reserved.
|
|
*
|
|
*/
|
|
|
|
//#define UART_DEBUG
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#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; i<write_len; i++)
|
|
{
|
|
outw(UART_PORT[pDrv->uart_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;
|
|
}
|