Files
BPI-CM9-Linux/aml-comp/uboot/bl30/rtos_sdk/drivers_aocpu/uart/uart.c
T

592 lines
15 KiB
C

/*
* Copyright (c) 2021-2022 Amlogic, Inc. All rights reserved.
*
* SPDX-License-Identifier: MIT
*/
#include "FreeRTOS.h"
#include "timers.h"
#include "common.h"
#include "uart.h"
#include "register.h"
#include "soc.h"
#include "interrupt.h"
#include "suspend.h"
#include "string.h"
#include <stdio.h>
#include "drv_errno.h"
#include "timer_source.h"
#ifdef CONFIG_SOC_A4
#include "uart-plat.h"
#endif
#ifdef CONFIG_BL30_LOG_SAVE
#include "ring_buffer.h"
#endif
//#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_CTRL (2 << 2)
#define UART_STATUS (3 << 2)
#define UART_MISC (4 << 2)
#define UART_REG5 (5 << 2)
#define UART_MODE_MASK_2WIRE (1<<15)
#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_CTRL_XTAL_CLK_24M (1 << 26)
#define UART_CTRL_XTAL_CLK_DIV2 (1<<27)
#define UART_MISC_CTS_FILTER (1<<27)
#define UART_MISC_RX_FILTER (1<<19)
#define UART_STAT_MASK_RFIFO_FULL (1 << 19)
#define UART_STAT_MASK_RX_FIFO_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_RFIFO(uart_base) P_UART(uart_base, UART_RFIFO)
#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 P_UART_MISC(uart_base) P_UART(uart_base, UART_MISC)
#define P_UART_REG5(uart_base) P_UART(uart_base, UART_REG5)
#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
#define ERR_UART(errno) (DRV_ERRNO_UART_BASE | errno)
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!";
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;
}
#ifdef CONFIG_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(REE2AO_CHANNEL,
MBX_CMD_SET_BL30_PRINT, xMboxBL30PrintEn, 0);
}
#endif
#endif /* ACS_DIS_PRINT_FLAG */
#ifdef CONFIG_BL30_LOG_SAVE
static void *xMboxBL30LogBufferRefresh(void *msg)
{
copy_ringbuffer_to_sram((unsigned char *)BL30MSG_LOCAL_BUF_BASE, BL30MSG_LOCAL_BUF_SIZE);
memset(msg, 0, MBOX_BUF_LEN);
*(uint32_t *)msg = MBOX_BL30_LOG_COPY_DONE;
return NULL;
}
void vBL30RealLogRefreshInit(void)
{
xInstallRemoteMessageCallbackFeedBack(REE2AO_CHANNEL,
MBX_CMD_REFRESH_BL30_LOG, xMboxBL30LogBufferRefresh, 1);
}
#endif
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
#ifdef CONFIG_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
#ifdef CONFIG_STICK_MEM
&& (stick_mem_bl30_print_en != STICK_MEM_EN_BL30_PRINT_FLAG)
#endif
)
return;
#endif /* ACS_DIS_PRINT_FLAG */
while (prvUartTxIsFull())
;
REG32(P_UART_WFIFO(UART_PORT_CONS)) = (char)c;
}
int vUartPuts(const char *s)
{
int n = 0;
#ifdef CONFIG_BL30_LOG_SAVE
write_ring_buffer((unsigned char *)s, strlen(s));
/* Dump by Linux system, no need for '\ r' */
write_ring_buffer((unsigned char *)"\n", 1);
#endif
while (*s) {
vUartPutc(*s++);
n++;
}
vUartPutc('\r');
n++;
vUartPutc('\n');
n++;
return n;
}
int iUartBufPuts(const char *s)
{
int iCnt = 0;
int iTxBufCnt = 0;
uint8_t ucIsTxBusy;
#ifdef CONFIG_BL30_LOG_SAVE
write_ring_buffer((unsigned char *)s, strlen(s));
#endif
while (*s) {
ucIsTxBusy = prvChkTxFifoBusy();
/* If Uart TX FIFO is busy, send string to TX Buffer */
if (ucIsTxBusy && xUartTxBuf.ucBufReady) {
iTxBufCnt = prvFillTxBufStr(s);
iCnt += iTxBufCnt;
break;
}
if (!ucIsTxBusy || !xUartTxBuf.ucBufReady) {
if ('\n' == *s) {
vUartPutc('\r');
iCnt++;
}
vUartPutc(*s);
iCnt++;
s++;
}
}
return iCnt;
}
void vUartEnTxBuf(void)
{
taskENTER_CRITICAL();
xUartTxBuf.ucBufReady = 1;
xTimerStart(xUartTxBuf.xTimer, 0);
taskEXIT_CRITICAL();
}
void vUartDisTxBuf(void)
{
taskENTER_CRITICAL();
xUartTxBuf.ucBufReady = 0;
xTimerStop(xUartTxBuf.xTimer, 0);
taskEXIT_CRITICAL();
}
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 vUart_Debug(uint32_t RegBase)
{
int uart_reg[6] = {0};
uart_reg[2] = REG32(P_UART_CTRL(RegBase));
printf("reg2 contrl 0x%x = %x\n", P_UART_CTRL(RegBase), uart_reg[2]);
uart_reg[3] = REG32(P_UART_STATUS(RegBase));
printf("reg3 status 0x%x = %x\n", P_UART_STATUS(RegBase), uart_reg[3]);
uart_reg[4] = REG32(P_UART_MISC(RegBase));
printf("reg4 misc 0x%x = %x\n", P_UART_MISC(RegBase), uart_reg[4]);
uart_reg[5] = REG32(P_UART_REG5(RegBase));
printf("reg5 0x%x = %x\n", P_UART_REG5(RegBase), uart_reg[5]);
}
void vUartChangeBaudrate_suspend(unsigned long source, unsigned long baud)
{
unsigned long baud_para = 0;
while (!(REG32(P_UART_STATUS(UART_PORT_CONS)) & UART_STAT_MASK_RX_FIFO_EMPTY))
;
if (source == 24000000) {
baud_para = (source / 2 + baud / 2) / baud - 1;
REG32(P_UART_REG5(UART_PORT_CONS)) = baud_para | UART_CTRL_USE_NEW_BAUD_RATE |
UART_CTRL_USE_XTAL_CLK | UART_CTRL_XTAL_CLK_DIV2;
} else {
baud_para = ((source * 10 / (baud * 4) + 5) / 10) - 1;
/* recommend from VLSI */
REG32(P_UART_MISC(UART_PORT_CONS)) = REG32(P_UART_MISC(UART_PORT_CONS)) |
(UART_MISC_RX_FILTER | UART_MISC_CTS_FILTER);
REG32(P_UART_REG5(UART_PORT_CONS)) = baud_para | UART_CTRL_USE_NEW_BAUD_RATE;
}
}
void vUartChangeBaudrate_resume(unsigned long baud)
{
unsigned long baud_para = 0;
while (!(REG32(P_UART_STATUS(UART_PORT_CONS)) & UART_STAT_MASK_RX_FIFO_EMPTY))
;
REG32(P_UART_MISC(UART_PORT_CONS)) = REG32(P_UART_MISC(UART_PORT_CONS)) &
(~(UART_MISC_RX_FILTER | UART_MISC_CTS_FILTER));
baud_para = (12000000 + baud / 2) / baud - 1;
REG32(P_UART_REG5(UART_PORT_CONS)) = baud_para | UART_CTRL_USE_XTAL_CLK |
UART_CTRL_USE_NEW_BAUD_RATE | UART_CTRL_XTAL_CLK_DIV2;
}
void vUartTxStop(void)
{
}
long lUartTxReady(void)
{
return !(REG32(P_UART_STATUS(UART_PORT_CONS)) & UART_STAT_MASK_TFIFO_FULL);
}
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
* diturb UART transfers.
*/
int iUartInit(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);
return ERR_UART(DRV_ERROR_UNSUPPORTED);
}
}
return 0;
}
#ifdef CONFIG_UART_WAKEUP
#define UART_TASK_PRI 3
static uint32_t UartWakeupRegBase = UART_PORT_WAKEUP_REG_BASE;
static uint32_t UartWakeupIrq = UART_PORT_WAKEUP_IRQ;
static uint32_t reg_uart[6];
static TaskHandle_t UartPollTask;
void vUartWakeupMatchHandler(void)
{
uint32_t buf[4] = {0};
int ch;
while (REG32(P_UART_STATUS(UartWakeupRegBase)) & 0xff) {
ch = REG32(P_UART_RFIFO(UartWakeupRegBase));
/* Todo: set the match string,then wake up sample is "w" */
/* printf("%s : ch=%x\n", __func__, ch); */
if ((ch & 0xff) == 'w') {
buf[0] = UART_RX_WAKEUP;
STR_Wakeup_src_Queue_Send_FromISR(buf);
DisableIrq(UartWakeupIrq);
return;
}
}
}
static void vUartPollTask(void *pvParameters)
{
uint32_t buf[4] = {0};
char ch;
while (1) {
while (REG32(P_UART_STATUS(UartWakeupRegBase)) & 0xff) {
ch = REG32(P_UART_RFIFO(UartWakeupRegBase));
/* Todo: set the match string,then wake up sample is "w" */
/* printf("%s : ch=%x\n", __func__, ch); */
if ((ch & 0xff) == 'w') {
buf[0] = UART_RX_WAKEUP;
STR_Wakeup_src_Queue_Send(buf);
break;
}
}
/* The time can be adjusted as needed */
vTaskDelay(pdMS_TO_TICKS(200));
}
}
void vUartWakeupInit(uint16_t GpioRx, uint16_t GpioTx,
enum PinMuxType func, uint8_t reinit,
void (*vIRQHandler)(void), uint32_t baudrate, uint32_t source)
{
int ch;
uint32_t baud_para = 0;
/* Save uart port value suspend from AP core */
reg_uart[2] = REG32(P_UART_CTRL(UartWakeupRegBase));
reg_uart[3] = REG32(P_UART_STATUS(UartWakeupRegBase));
reg_uart[4] = REG32(P_UART_MISC(UartWakeupRegBase));
reg_uart[5] = REG32(P_UART_REG5(UartWakeupRegBase));
/* vUart_Debug(UartWakeupRegBase); */
/* The uart port of AP core debug, don't set pinmux.This pinmux is fixed */
if (UartWakeupRegBase != UART_PORT_CONS) {
/* Todo: set the fix pinmux */
xPinmuxSet(GpioRx, func);
xPinconfSet(GpioRx, PINF_CONFIG_BIAS_PULL_UP | PINF_CONFIG_DRV_STRENGTH_3);
xPinmuxSet(GpioTx, func);
xPinconfSet(GpioTx, PINF_CONFIG_BIAS_PULL_UP | PINF_CONFIG_DRV_STRENGTH_3);
}
/* todo: if uart is not use before,you should turn on this code */
if (reinit) {
REG32(P_UART_CTRL(UartWakeupRegBase)) = UART_STP_BIT |
UART_PRTY_BIT |
UART_CHAR_LEN |
UART_MODE_MASK_RST_TX |
UART_MODE_MASK_RST_RX |
UART_MODE_MASK_CLR_ERR |
UART_MODE_MASK_TX_EN |
UART_MODE_MASK_RX_EN |
UART_MODE_MASK_2WIRE;
if (source == 24000000) {
baud_para = (source / 2 + baudrate / 2) / baudrate - 1;
REG32(P_UART_REG5(UartWakeupRegBase)) = baud_para |
UART_CTRL_USE_NEW_BAUD_RATE |
UART_CTRL_USE_XTAL_CLK |
UART_CTRL_XTAL_CLK_DIV2;
} else if (source == 32000) {
baud_para = (source / 2 + baudrate / 2) / baudrate - 1;
REG32(P_UART_REG5(UartWakeupRegBase)) = baud_para |
UART_CTRL_USE_NEW_BAUD_RATE |
UART_CTRL_USE_XTAL_CLK |
UART_CTRL_XTAL_CLK_DIV2;
} else {
baud_para = ((source * 10 / (baudrate * 4) + 5) / 10) - 1;
/* recommend from VLSI */
REG32(P_UART_MISC(UartWakeupRegBase)) |= UART_MISC_RX_FILTER |
UART_MISC_CTS_FILTER;
REG32(P_UART_REG5(UartWakeupRegBase)) = baud_para |
UART_CTRL_USE_NEW_BAUD_RATE;
}
ch = REG32(P_UART_CTRL(UartWakeupRegBase));
ch &= ~(UART_MODE_MASK_RST_TX | UART_MODE_MASK_RST_RX | UART_MODE_MASK_CLR_ERR);
REG32(P_UART_CTRL(UartWakeupRegBase)) = ch;
}
if (vIRQHandler) {
ch = REG32(P_UART_CTRL(UartWakeupRegBase));
ch |= (1 << 27);
REG32(P_UART_CTRL(UartWakeupRegBase)) = ch;
ch = REG32(P_UART_MISC(UartWakeupRegBase));
ch &= 0xffffff00;
ch |= (1 << 0);
REG32(P_UART_MISC(UartWakeupRegBase)) = ch;
}
if (vIRQHandler) {
RegisterIrq(UartWakeupIrq, 2, vIRQHandler);
EnableIrq(UartWakeupIrq);
} else {
xTaskCreate(vUartPollTask, "UartPollTask", configMINIMAL_STACK_SIZE,
NULL, UART_TASK_PRI, &UartPollTask);
}
#ifdef CONFIG_SOC_A4
UART_AO_WakeUp_Setting();
#endif
/* vUart_Debug(UartWakeupRegBase); */
}
void vUartWakeupDeinit(void (*vIRQHandler)(void))
{
if (vIRQHandler) {
DisableIrq(UartWakeupIrq);
UnRegisterIrq(UartWakeupIrq);
} else {
vTaskDelete(UartPollTask);
}
/* restore uart value */
REG32(P_UART_CTRL(UartWakeupRegBase)) = reg_uart[2];
REG32(P_UART_STATUS(UartWakeupRegBase)) = reg_uart[3];
REG32(P_UART_MISC(UartWakeupRegBase)) = reg_uart[4];
REG32(P_UART_REG5(UartWakeupRegBase)) = reg_uart[5];
}
#endif