blob: 4be4c03c00298445d3037592d9471c073c2a448d [file] [log] [blame]
/*
* Copyright (C) 2014-2018 Amlogic, Inc. All rights reserved.
*
* All information contained herein is Amlogic confidential.
*
* This software is provided to you pursuant to Software License Agreement
* (SLA) with Amlogic Inc ("Amlogic"). This software may be used
* only in accordance with the terms of this agreement.
*
* Redistribution and use in source and binary forms, with or without
* modification is strictly prohibited without prior written permission from
* Amlogic.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* UART driver
*/
#include "FreeRTOS.h"
#include "timers.h"
#include "common.h"
#include "FreeRTOSConfig.h"
#include "uart.h"
#include "register.h"
#include "soc.h"
#include "string.h"
#include "timer_source.h"
//#define UART_PORT_CONS UART_B_WFIFO
#define UART_STP_BIT UART_MODE_MASK_STP_1BIT
#define UART_PRTY_BIT 0
#define UART_CHAR_LEN UART_MODE_MASK_CHAR_8BIT
#define UART_MODE_RESET_MASK \
(UART_MODE_MASK_RST_TX \
| UART_MODE_MASK_RST_RX \
| UART_MODE_MASK_CLR_ERR)
#define UART_WFIFO (0<<2)
#define UART_RFIFO (1<<2)
#define UART_MODE (2<<2)
#define UART_STATUS (3<<2)
#define UART_IRQCTL (4<<2)
#define UART_CTRL (5<<2)
#define UART_MODE_MASK_STP_1BIT (0<<16)
#define UART_MODE_MASK_CHAR_8BIT (0<<20)
#define UART_MODE_MASK_TX_EN (1<<12)
#define UART_MODE_MASK_RX_EN (1<<13)
#define UART_MODE_MASK_RST_TX (1<<22)
#define UART_MODE_MASK_RST_RX (1<<23)
#define UART_MODE_MASK_CLR_ERR (1<<24)
#define UART_CTRL_USE_XTAL_CLK (1<<24)
#define UART_CTRL_USE_NEW_BAUD_RATE (1<<23)
#define UART_STAT_MASK_RFIFO_FULL (1<<19)
#define UART_STAT_MASK_RFIFO_EMPTY (1<<20)
#define UART_STAT_MASK_TFIFO_FULL (1<<21)
#define UART_STAT_MASK_TFIFO_EMPTY (1<<22)
#define P_UART(uart_base, reg) (uart_base+reg)
#define P_UART_WFIFO(uart_base) P_UART(uart_base, UART_WFIFO)
#define P_UART_MODE(uart_base) P_UART(uart_base, UART_MODE)
#define P_UART_CTRL(uart_base) P_UART(uart_base, UART_CTRL)
#define P_UART_STATUS(uart_base) P_UART(uart_base, UART_STATUS)
#define UART_TX_BUF_SIZE (512)
#define POLL_TX_FIFO_PRD (portTICK_PERIOD_MS)
#define POLL_TX_BUF_PRD (portTICK_PERIOD_MS * 10)
#define UART_TXBUF_TASK_PRI (3)
#define UART_TX_FIFO_FULL_WAIT (5000) //us
struct xUartTxBuf_t {
char *pcTxBuf;
uint32_t ulRdPtr;
uint32_t ulWrPtr;
uint32_t ulBufCnt;
uint32_t ulTimerPeriod;
TimerHandle_t xTimer;
uint8_t ucWarnMsgPrtEn;
uint8_t ucBufReady;
};
static const char cWarnMsg[] = "\r\nWarning: AOCPU log is lost due to TX FIFO full!\n";
static struct xUartTxBuf_t xUartTxBuf;
#ifdef ACS_DIS_PRINT_FLAG
static uint8_t bl30_print_en;
void enable_bl30_print(uint8_t enable)
{
/* Applied to output important logs */
bl30_print_en = enable;
}
#if configSUPPORT_STICK_MEM
#include "stick_mem.h"
#include "mailbox-api.h"
/* Applied to enable or disable bl30 start logs before first
* suspend or shutdown when compile with '--noverbose'.
*/
static void *xMboxBL30PrintEn(void *msg)
{
stick_mem_write(STICK_BL30_PRINT_EN, *(uint32_t *)msg);
return NULL;
}
void vBL30PrintControlInit(void)
{
xInstallRemoteMessageCallbackFeedBack(AOREE_CHANNEL,
MBX_CMD_SET_BL30_PRINT, xMboxBL30PrintEn, 0);
}
#endif
#endif /* ACS_DIS_PRINT_FLAG */
static int prvUartTxIsFull(void)
{
return REG32(P_UART_STATUS(UART_PORT_CONS)) & UART_STAT_MASK_TFIFO_FULL;
}
void vUartTxFlush(void)
{
while (!
(REG32(P_UART_STATUS(UART_PORT_CONS)) &
UART_STAT_MASK_TFIFO_EMPTY));
}
static int prvFillTxBuf(char c)
{
char *pcBuf;
taskENTER_CRITICAL();
if (xUartTxBuf.ulBufCnt >= UART_TX_BUF_SIZE) {
xUartTxBuf.ucWarnMsgPrtEn = 1;
taskEXIT_CRITICAL();
return 1;
}
pcBuf = xUartTxBuf.pcTxBuf;
pcBuf[xUartTxBuf.ulWrPtr] = c;
xUartTxBuf.ulWrPtr++;
xUartTxBuf.ulBufCnt++;
if (xUartTxBuf.ulWrPtr >= UART_TX_BUF_SIZE)
xUartTxBuf.ulWrPtr = 0;
taskEXIT_CRITICAL();
return 0;
}
static int prvFillTxBufStr(const char *s)
{
const char *pcChar = s;
int iCnt = 0;
while (*pcChar) {
if ('\n' == *pcChar) {
prvFillTxBuf('\r');
iCnt++;
}
prvFillTxBuf(*s);
iCnt++;
pcChar++;
}
return iCnt;
}
static uint8_t prvChkTxFifoBusy(void)
{
uint8_t ucIsTxBusy = 0;
uint32_t time_start;
uint32_t time_end;
uint32_t time_elapse;
/* Check if UART TX FIFO is busy */
time_start = timere_read_us();
while (prvUartTxIsFull()) {
time_end = timere_read_us();
time_elapse = time_end - time_start;
if (time_elapse > UART_TX_FIFO_FULL_WAIT) {
ucIsTxBusy = 1;
break;
}
}
return ucIsTxBusy;
}
void vUartPutc(const char c)
{
#ifdef ACS_DIS_PRINT_FLAG
#if configSUPPORT_STICK_MEM
unsigned int stick_mem_bl30_print_en;
stick_mem_read(STICK_BL30_PRINT_EN, &stick_mem_bl30_print_en);
#endif
if ((REG32(ACS_DIS_PRINT_REG) & ACS_DIS_PRINT_FLAG) && !bl30_print_en
#if configSUPPORT_STICK_MEM
&& (stick_mem_bl30_print_en != STICK_MEM_EN_BL30_PRINT_FLAG)
#endif
)
return;
#endif /* ACS_DIS_PRINT_FLAG */
if (c == '\n')
vUartPutc('\r');
while (prvUartTxIsFull());
REG32(P_UART_WFIFO(UART_PORT_CONS)) = (char)c;
}
void vUartPuts(const char *s)
{
uint8_t ucIsTxBusy;
while (*s) {
ucIsTxBusy = prvChkTxFifoBusy();
/* If Uart TX FIFO is busy, send string to TX Buffer */
if (ucIsTxBusy && xUartTxBuf.ucBufReady) {
prvFillTxBufStr(s);
break;
}
if (!ucIsTxBusy || !xUartTxBuf.ucBufReady) {
vUartPutc(*s);
s++;
}
}
}
void vUartTxStart(void)
{
/* Do not allow deep sleep while transmit in progress */
#ifdef CONFIG_LOW_POWER_IDLE
disable_sleep(SLEEP_MASK_UART);
#endif
//uart_flush_output();
}
void vUartTxStop(void)
{
}
long lUartTxReady(void)
{
return !(REG32(P_UART_STATUS(UART_PORT_CONS)) &
UART_STAT_MASK_TFIFO_FULL);
}
#if 0
void vUartWriteChar(char c)
{
vUartPutc(c);
}
int uart_tx_char(int c)
{
vUartPutc(c);
return c;
}
/*print BCD*/
void print_u32_dec(unsigned int num) {
char buf[16];
char *s = buf + (sizeof(buf) / sizeof(buf[0])) - 1;
char *e = s;
do {
*--s = '0' + num % 10;
} while (num /= 10);
while (s < e)
uart_tx_char(*s++);
}
void serial_put_hex(unsigned long data, unsigned int bitlen)
{
int i;
unsigned char s;
for (i = bitlen - 4; i >= 0; i -= 4)
{
s = (data >> i) & 0xf;
if (s < 10)
vUartPutc(0x30 + s);
else if (s < 16)
vUartPutc(0x61 + s - 10);
}
}
#endif
#if 0
/* Interrupt handler for console UART */
void uart_interrupt(void)
{
/* Fill output FIFO */
uart_process_output();
}
DECLARE_IRQ(IRQ_AO_UART_NUM, uart_interrupt, 1);
#endif
static void prvUartTxBufHandle(TimerHandle_t xTimer)
{
char cTxChar;
uint32_t ulIsTxFifoEmpty;
(void)xTimer;
taskENTER_CRITICAL();
if (xUartTxBuf.ulBufCnt == 0) {
if (xUartTxBuf.ulTimerPeriod != POLL_TX_BUF_PRD) {
xUartTxBuf.ulTimerPeriod = POLL_TX_BUF_PRD;
xTimerChangePeriod(xUartTxBuf.xTimer, xUartTxBuf.ulTimerPeriod, 0);
}
ulIsTxFifoEmpty = (REG32(P_UART_STATUS(UART_PORT_CONS)) &
UART_STAT_MASK_TFIFO_EMPTY);
if (ulIsTxFifoEmpty && xUartTxBuf.ucWarnMsgPrtEn) {
xUartTxBuf.ucWarnMsgPrtEn = 0;
vUartPuts(cWarnMsg);
}
} else {
if (xUartTxBuf.ulTimerPeriod != POLL_TX_FIFO_PRD) {
xUartTxBuf.ulTimerPeriod = POLL_TX_FIFO_PRD;
xTimerChangePeriod(xUartTxBuf.xTimer, xUartTxBuf.ulTimerPeriod, 0);
}
while (xUartTxBuf.ulBufCnt) {
cTxChar = xUartTxBuf.pcTxBuf[xUartTxBuf.ulRdPtr];
vUartPutc(cTxChar);
xUartTxBuf.ulRdPtr++;
xUartTxBuf.ulBufCnt--;
if (xUartTxBuf.ulRdPtr >= UART_TX_BUF_SIZE)
xUartTxBuf.ulRdPtr = 0;
}
}
taskEXIT_CRITICAL();
}
/*
* Set UART to 115200-8-N-1
*
* Using 24M XTAL as UART reference clock, *NOT* clk81
* So the clk81 can be dynamically changed and not
* disturb UART transfers.
*/
void vUartInit(void)
{
TimerHandle_t xUartTimer = NULL;
xUartTxBuf.pcTxBuf = pvPortMalloc(UART_TX_BUF_SIZE);
if (xUartTxBuf.pcTxBuf) {
memset(xUartTxBuf.pcTxBuf, 0, UART_TX_BUF_SIZE);
xUartTimer = xTimerCreate("UartTimer", pdMS_TO_TICKS(POLL_TX_BUF_PRD),
pdTRUE, NULL, prvUartTxBufHandle);
if (xUartTimer) {
xUartTxBuf.ulRdPtr = 0;
xUartTxBuf.ulWrPtr = 0;
xUartTxBuf.ulBufCnt = 0;
xUartTxBuf.ucWarnMsgPrtEn = 0;
xUartTxBuf.ucBufReady = 1;
xUartTxBuf.xTimer = xUartTimer;
xUartTxBuf.ulTimerPeriod = POLL_TX_BUF_PRD;
xTimerStart(xUartTxBuf.xTimer, 0);
} else {
vPortFree(xUartTxBuf.pcTxBuf);
vUartPuts("Warning: xUartTimer create fail\n");
}
}
}