935 lines
24 KiB
C
935 lines
24 KiB
C
/*
|
|
* ===========================================================================================
|
|
*
|
|
* Filename: hal_uart.c
|
|
*
|
|
* Description: hal impl. of uart.
|
|
*
|
|
* Version: Melis3.0
|
|
* Create: 2019-11-14 14:20:56
|
|
* Revision: none
|
|
* Compiler: GCC:version 9.2.1 20170904 (release),ARM/embedded-7-branch revision 255204
|
|
*
|
|
* Author: bantao@allwinnertech.com
|
|
* Organization: SWC-BPD
|
|
* Last Modified: 2020-04-29 15:17:36
|
|
*
|
|
* ===========================================================================================
|
|
*/
|
|
|
|
#include <hal_uart.h>
|
|
#include <hal_interrupt.h>
|
|
#include <hal_queue.h>
|
|
#include <hal_clk.h>
|
|
#include <hal_reset.h>
|
|
#include <hal_gpio.h>
|
|
#ifdef CONFIG_DRIVER_SYSCONFIG
|
|
#include <hal_cfg.h>
|
|
#include <script.h>
|
|
#endif
|
|
#include "uart.h"
|
|
|
|
#if (0)
|
|
#define UART_LOG_DEBUG
|
|
#endif
|
|
#define UART_INIT(fmt, ...) printf("uart: "fmt, ##__VA_ARGS__)
|
|
#define UART_ERR(fmt, ...) printf("uart: "fmt, ##__VA_ARGS__)
|
|
|
|
#ifdef UART_LOG_DEBUG
|
|
#define UART_INFO(fmt, ...) printf("[%s %d]"fmt, __func__, __LINE__, ##__VA_ARGS__)
|
|
#define UART_INFO_IRQ(fmt, ...) printf("[%s %d]"fmt, __func__, __LINE__, ##__VA_ARGS__)
|
|
#else
|
|
#define UART_INFO(fmt, ...)
|
|
#define UART_INFO_IRQ(fmt, ...)
|
|
#endif
|
|
|
|
static unsigned long sunxi_uart_port[] =
|
|
{
|
|
SUNXI_UART0_BASE, SUNXI_UART1_BASE, SUNXI_UART2_BASE, SUNXI_UART3_BASE
|
|
};
|
|
static const uint32_t g_uart_irqn[] = {SUNXI_IRQ_UART0, SUNXI_IRQ_UART1,
|
|
SUNXI_IRQ_UART2, SUNXI_IRQ_UART3
|
|
};
|
|
static sunxi_hal_version_t hal_uart_driver =
|
|
{
|
|
SUNXI_HAL_UART_API_VERSION,
|
|
SUNXI_HAL_UART_DRV_VERSION
|
|
};
|
|
static uart_priv_t g_uart_priv[UART_MAX];
|
|
|
|
static hal_mailbox_t uart_mailbox[UART_MAX];
|
|
|
|
static const uint32_t g_uart_baudrate_map[] =
|
|
{
|
|
300,
|
|
600,
|
|
1200,
|
|
2400,
|
|
4800,
|
|
9600,
|
|
19200,
|
|
38400,
|
|
57600,
|
|
115200,
|
|
230400,
|
|
576000,
|
|
921600,
|
|
500000,
|
|
1000000,
|
|
1500000,
|
|
3000000,
|
|
4000000,
|
|
};
|
|
|
|
//driver capabilities, support uart function only.
|
|
static const sunxi_hal_uart_capabilities_t driver_capabilities =
|
|
{
|
|
1, /* supports UART (Asynchronous) mode */
|
|
0, /* supports Synchronous Master mode */
|
|
0, /* supports Synchronous Slave mode */
|
|
0, /* supports UART Single-wire mode */
|
|
0, /* supports UART IrDA mode */
|
|
0, /* supports UART Smart Card mode */
|
|
0, /* Smart Card Clock generator available */
|
|
0, /* RTS Flow Control available */
|
|
0, /* CTS Flow Control available */
|
|
0, /* Transmit completed event: \ref ARM_UARTx_EVENT_TX_COMPLETE */
|
|
0, /* Signal receive character timeout event: \ref ARM_UARTx_EVENT_RX_TIMEOUT */
|
|
0, /* RTS Line: 0=not available, 1=available */
|
|
0, /* CTS Line: 0=not available, 1=available */
|
|
0, /* DTR Line: 0=not available, 1=available */
|
|
0, /* DSR Line: 0=not available, 1=available */
|
|
0, /* DCD Line: 0=not available, 1=available */
|
|
0, /* RI Line: 0=not available, 1=available */
|
|
0, /* Signal CTS change event: \ref ARM_UARTx_EVENT_CTS */
|
|
0, /* Signal DSR change event: \ref ARM_UARTx_EVENT_DSR */
|
|
0, /* Signal DCD change event: \ref ARM_UARTx_EVENT_DCD */
|
|
0, /* Signal RI change event: \ref ARM_UARTx_EVENT_RI */
|
|
0 /* Reserved */
|
|
};
|
|
|
|
#ifdef CONFIG_SUNXI_UART_SUPPORT_POLL
|
|
|
|
static poll_wakeup_func uart_drv_poll_wakeup = NULL;
|
|
|
|
int32_t hal_uart_check_poll_state(int32_t dev_id, short key)
|
|
{
|
|
int ret = -1;
|
|
int32_t mask = 0;
|
|
|
|
if (key & POLLIN)
|
|
{
|
|
ret = hal_is_mailbox_empty((hal_mailbox_t)uart_mailbox[dev_id]);
|
|
if (ret == 1)
|
|
{
|
|
mask = 0;
|
|
}
|
|
else
|
|
{
|
|
mask |= POLLIN;
|
|
}
|
|
}
|
|
|
|
if (key & POLLOUT)
|
|
{
|
|
mask |= POLLOUT;
|
|
}
|
|
return mask;
|
|
}
|
|
|
|
int32_t hal_uart_poll_wakeup(int32_t dev_id, short key)
|
|
{
|
|
int ret = -1;
|
|
|
|
if (uart_drv_poll_wakeup)
|
|
{
|
|
ret = uart_drv_poll_wakeup(dev_id, key);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
int32_t hal_uart_register_poll_wakeup(poll_wakeup_func poll_wakeup)
|
|
{
|
|
uart_drv_poll_wakeup = poll_wakeup;
|
|
|
|
return 0;
|
|
}
|
|
|
|
#endif
|
|
|
|
static bool uart_port_is_valid(uart_port_t uart_port)
|
|
{
|
|
return (uart_port < UART_MAX);
|
|
}
|
|
|
|
static bool uart_config_is_valid(const _uart_config_t *config)
|
|
{
|
|
return ((config->baudrate < UART_BAUDRATE_MAX) &&
|
|
(config->word_length <= UART_WORD_LENGTH_8) &&
|
|
(config->stop_bit <= UART_STOP_BIT_2) &&
|
|
(config->parity <= UART_PARITY_EVEN));
|
|
}
|
|
|
|
sunxi_hal_version_t hal_uart_get_version(int32_t dev)
|
|
{
|
|
HAL_ARG_UNUSED(dev);
|
|
return hal_uart_driver;
|
|
}
|
|
|
|
sunxi_hal_uart_capabilities_t hal_uart_get_capabilities(int32_t dev)
|
|
{
|
|
HAL_ARG_UNUSED(dev);
|
|
return driver_capabilities;
|
|
}
|
|
|
|
static void uart_set_format(uart_port_t uart_port, uart_word_length_t word_length,
|
|
uart_stop_bit_t stop_bit, uart_parity_t parity)
|
|
{
|
|
const unsigned long uart_base = sunxi_uart_port[uart_port];
|
|
uart_priv_t *uart_priv = &g_uart_priv[uart_port];
|
|
uint32_t value;
|
|
|
|
value = hal_readb(uart_base + UART_LCR);
|
|
|
|
/* set word length */
|
|
value &= ~(UART_LCR_DLEN_MASK);
|
|
switch (word_length)
|
|
{
|
|
case UART_WORD_LENGTH_5:
|
|
value |= UART_LCR_WLEN5;
|
|
break;
|
|
case UART_WORD_LENGTH_6:
|
|
value |= UART_LCR_WLEN6;
|
|
break;
|
|
case UART_WORD_LENGTH_7:
|
|
value |= UART_LCR_WLEN7;
|
|
break;
|
|
case UART_WORD_LENGTH_8:
|
|
default:
|
|
value |= UART_LCR_WLEN8;
|
|
break;
|
|
}
|
|
|
|
/* set stop bit */
|
|
switch (stop_bit)
|
|
{
|
|
case UART_STOP_BIT_1:
|
|
default:
|
|
value &= ~(UART_LCR_STOP);
|
|
break;
|
|
case UART_STOP_BIT_2:
|
|
value |= UART_LCR_STOP;
|
|
break;
|
|
}
|
|
|
|
/* set parity bit */
|
|
value &= ~(UART_LCR_PARITY_MASK);
|
|
switch (parity)
|
|
{
|
|
case UART_PARITY_NONE:
|
|
value &= ~(UART_LCR_PARITY);
|
|
break;
|
|
case UART_PARITY_ODD:
|
|
value |= UART_LCR_PARITY;
|
|
break;
|
|
case UART_PARITY_EVEN:
|
|
value |= UART_LCR_PARITY;
|
|
value |= UART_LCR_EPAR;
|
|
break;
|
|
}
|
|
|
|
uart_priv->lcr = value;
|
|
hal_writeb(uart_priv->lcr, uart_base + UART_LCR);
|
|
}
|
|
|
|
#define CCM_UART_RST_OFFSET (16)
|
|
#define CCM_UART_GATING_OFFSET (0)
|
|
static void uart_reset(uart_port_t uart_port)
|
|
{
|
|
int i;
|
|
unsigned int reg_val = 0;
|
|
|
|
/* 0x3001000(CCU) + 0x90c(UART_BGR_REG: UART Bus Gating Reset Register) */
|
|
/* reset */
|
|
reg_val = readl(0x0300190c);
|
|
reg_val &= ~(1 << (CCM_UART_RST_OFFSET + uart_port));
|
|
writel(reg_val, 0x0300190c);
|
|
for (i = 0; i < 100; i++)
|
|
;
|
|
|
|
reg_val = readl(0x300190c);
|
|
reg_val |= (1 << (CCM_UART_RST_OFFSET + uart_port));
|
|
writel(reg_val, 0x300190c);
|
|
|
|
/* gating */
|
|
reg_val = readl(0x300190c);
|
|
reg_val &= ~(1 << (CCM_UART_GATING_OFFSET + uart_port));
|
|
writel(reg_val, 0x300190c);
|
|
for (i = 0; i < 100; i++)
|
|
;
|
|
reg_val = readl(0x300190c);
|
|
reg_val |= (1 << (CCM_UART_GATING_OFFSET + uart_port));
|
|
writel(reg_val, 0x300190c);
|
|
}
|
|
|
|
static void uart_set_baudrate(uart_port_t uart_port, uart_baudrate_t baudrate)
|
|
{
|
|
const unsigned long uart_base = sunxi_uart_port[uart_port];
|
|
uart_priv_t *uart_priv = &g_uart_priv[uart_port];
|
|
uint32_t actual_baudrate = g_uart_baudrate_map[baudrate];
|
|
uint32_t quot, uart_clk;
|
|
|
|
uart_clk = 24000000; /* FIXME: fixed to 24MHz */
|
|
|
|
quot = (uart_clk + 8 * actual_baudrate) / (16 * actual_baudrate);
|
|
|
|
UART_INFO("baudrate: %d, quot = %d\r\n", actual_baudrate, quot);
|
|
|
|
uart_priv->dlh = quot >> 8;
|
|
uart_priv->dll = quot & 0xff;
|
|
|
|
/* hold tx so that uart will update lcr and baud in the gap of tx */
|
|
hal_writeb(UART_HALT_HTX | UART_HALT_FORCECFG, uart_base + UART_HALT);
|
|
hal_writeb(uart_priv->lcr | UART_LCR_DLAB, uart_base + UART_LCR);
|
|
hal_writeb(uart_priv->dlh, uart_base + UART_DLH);
|
|
hal_writeb(uart_priv->dll, uart_base + UART_DLL);
|
|
hal_writeb(UART_HALT_HTX | UART_HALT_FORCECFG | UART_HALT_LCRUP, uart_base + UART_HALT);
|
|
/* FIXME: implement timeout */
|
|
while (hal_readb(uart_base + UART_HALT) & UART_HALT_LCRUP)
|
|
;
|
|
|
|
/* In fact there are two DLABs(DLAB and DLAB_BAK) in the hardware implementation.
|
|
* The DLAB_BAK is sellected only when SW_UART_HALT_FORCECFG is set to 1,
|
|
* and this bit can be access no matter uart is busy or not.
|
|
* So we select the DLAB_BAK always by leaving SW_UART_HALT_FORCECFG to be 1. */
|
|
hal_writeb(uart_priv->lcr, uart_base + UART_LCR);
|
|
hal_writeb(UART_HALT_FORCECFG, uart_base + UART_HALT);
|
|
}
|
|
|
|
static void uart_set_fifo(uart_port_t uart_port, uint32_t value)
|
|
{
|
|
const unsigned long uart_base = sunxi_uart_port[uart_port];
|
|
uart_priv_t *uart_priv = &g_uart_priv[uart_port];
|
|
|
|
uart_priv->fcr = value;
|
|
hal_writeb(uart_priv->fcr, uart_base + UART_FCR);
|
|
}
|
|
|
|
void hal_uart_set_hardware_flowcontrol(uart_port_t uart_port)
|
|
{
|
|
const unsigned long uart_base = sunxi_uart_port[uart_port];
|
|
uart_priv_t *uart_priv = &g_uart_priv[uart_port];
|
|
uint32_t value;
|
|
|
|
value = hal_readb(uart_base + UART_MCR);
|
|
value |= UART_MCR_DTR | UART_MCR_RTS | UART_MCR_AFE;
|
|
uart_priv->mcr = value;
|
|
hal_writeb(uart_priv->mcr, uart_base + UART_MCR);
|
|
|
|
/* enable with modem status interrupts */
|
|
value = hal_readb(uart_base + UART_IER);
|
|
value |= UART_IER_MSI;
|
|
uart_priv->ier = value;
|
|
hal_writeb(uart_priv->ier, uart_base + UART_IER);
|
|
}
|
|
|
|
void hal_uart_disable_flowcontrol(uart_port_t uart_port)
|
|
{
|
|
const unsigned long uart_base = sunxi_uart_port[uart_port];
|
|
uart_priv_t *uart_priv = &g_uart_priv[uart_port];
|
|
uint32_t value;
|
|
|
|
value = hal_readb(uart_base + UART_MCR);
|
|
value &= ~(UART_MCR_DTR | UART_MCR_RTS | UART_MCR_AFE);
|
|
uart_priv->mcr = value;
|
|
hal_writeb(uart_priv->mcr, uart_base + UART_MCR);
|
|
|
|
/* disable with modem status interrupts */
|
|
value = hal_readb(uart_base + UART_IER);
|
|
value &= ~(UART_IER_MSI);
|
|
uart_priv->ier = value;
|
|
hal_writeb(uart_priv->ier, uart_base + UART_IER);
|
|
}
|
|
|
|
static void uart_force_idle(uart_port_t uart_port)
|
|
{
|
|
const unsigned long uart_base = sunxi_uart_port[uart_port];
|
|
uart_priv_t *uart_priv = &g_uart_priv[uart_port];
|
|
|
|
if (uart_priv->fcr & UART_FCR_FIFO_EN)
|
|
{
|
|
hal_writeb(UART_FCR_FIFO_EN, uart_base + UART_FCR);
|
|
hal_writeb(UART_FCR_TXFIFO_RST
|
|
| UART_FCR_RXFIFO_RST
|
|
| UART_FCR_FIFO_EN, uart_base + UART_FCR);
|
|
hal_writeb(0, uart_base + UART_FCR);
|
|
}
|
|
|
|
hal_writeb(uart_priv->fcr, uart_base + UART_FCR);
|
|
(void)hal_readb(uart_base + UART_FCR);
|
|
}
|
|
|
|
static void uart_handle_busy(uart_port_t uart_port)
|
|
{
|
|
const unsigned long uart_base = sunxi_uart_port[uart_port];
|
|
uart_priv_t *uart_priv = &g_uart_priv[uart_port];
|
|
|
|
(void)hal_readb(uart_base + UART_USR);
|
|
|
|
/*
|
|
* Before reseting lcr, we should ensure than uart is not in busy
|
|
* state. Otherwise, a new busy interrupt will be introduced.
|
|
* It is wise to set uart into loopback mode, since it can cut down the
|
|
* serial in, then we should reset fifo(in my test, busy state
|
|
* (UART_USR_BUSY) can't be cleard until the fifo is empty).
|
|
*/
|
|
hal_writeb(uart_priv->mcr | UART_MCR_LOOP, uart_base + UART_MCR);
|
|
uart_force_idle(uart_port);
|
|
hal_writeb(uart_priv->lcr, uart_base + UART_LCR);
|
|
hal_writeb(uart_priv->mcr, uart_base + UART_MCR);
|
|
}
|
|
|
|
rt_weak void hal_uart_handler_hook(uart_port_t uart_port)
|
|
{
|
|
}
|
|
|
|
static uint32_t uart_handle_rx(uart_port_t uart_port, uint32_t lsr)
|
|
{
|
|
const unsigned long uart_base = sunxi_uart_port[uart_port];
|
|
uint8_t ch = 0;
|
|
|
|
UART_INFO("IRQ uart%d handle rx \n", uart_port);
|
|
do
|
|
{
|
|
if (lsr & UART_LSR_DR)
|
|
{
|
|
ch = hal_readb(uart_base + UART_RBR);
|
|
hal_mailbox_send((hal_mailbox_t)uart_mailbox[uart_port], ch);
|
|
#ifdef CONFIG_SUNXI_UART_SUPPORT_POLL
|
|
hal_uart_poll_wakeup(uart_port, POLLIN);
|
|
#endif
|
|
}
|
|
lsr = hal_readb(uart_base + UART_LSR);
|
|
} while ((lsr & (UART_LSR_DR | UART_LSR_BI)));
|
|
|
|
/* wakeup console here */
|
|
hal_uart_handler_hook(uart_port);
|
|
|
|
return lsr;
|
|
}
|
|
|
|
|
|
static irqreturn_t uart_irq_handler(int irq, void *dev_id)
|
|
{
|
|
uart_priv_t *uart_priv = dev_id;
|
|
uart_port_t uart_port = uart_priv->uart_port;
|
|
const unsigned long uart_base = sunxi_uart_port[uart_port];
|
|
uint32_t iir, lsr;
|
|
|
|
iir = hal_readb(uart_base + UART_IIR) & UART_IIR_IID_MASK;
|
|
lsr = hal_readb(uart_base + UART_LSR);
|
|
|
|
UART_INFO_IRQ("IRQ uart%d lsr is %08x \n", uart_port, lsr);
|
|
if (iir == UART_IIR_IID_BUSBSY)
|
|
{
|
|
uart_handle_busy(uart_port);
|
|
}
|
|
else
|
|
{
|
|
if (lsr & (UART_LSR_DR | UART_LSR_BI))
|
|
{
|
|
lsr = uart_handle_rx(uart_port, lsr);
|
|
}
|
|
else if (iir & UART_IIR_IID_CHARTO)
|
|
/* has charto irq but no dr lsr? just read and ignore */
|
|
{
|
|
hal_readb(uart_base + UART_RBR);
|
|
}
|
|
|
|
/* if (lsr & UART_LSR_THRE)
|
|
{
|
|
uart_handle_tx(uart_port);
|
|
}*/
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void uart_enable_irq(uart_port_t uart_port, uint32_t irq_type)
|
|
{
|
|
const unsigned long uart_base = sunxi_uart_port[uart_port];
|
|
uint32_t value;
|
|
|
|
value = hal_readb(uart_base + UART_IER);
|
|
value |= irq_type;
|
|
hal_writeb(value, uart_base + UART_IER);
|
|
|
|
}
|
|
|
|
static void uart_enable_busy_cfg(uart_port_t uart_port)
|
|
{
|
|
const unsigned long uart_base = sunxi_uart_port[uart_port];
|
|
uint32_t value;
|
|
|
|
value = hal_readb(uart_base + UART_HALT);
|
|
value |= UART_HALT_FORCECFG;
|
|
hal_writeb(value, uart_base + UART_HALT);
|
|
}
|
|
|
|
static int uart_clk_init(int bus, bool enable)
|
|
{
|
|
hal_clk_status_t ret;
|
|
hal_reset_type_t reset_type = HAL_SUNXI_RESET;
|
|
u32 reset_id;
|
|
hal_clk_type_t clk_type = HAL_SUNXI_CCU;
|
|
hal_clk_id_t clk_id;
|
|
hal_clk_t clk;
|
|
struct reset_control *reset;
|
|
|
|
switch (bus)
|
|
{
|
|
case 0:
|
|
clk_id = SUNXI_CLK_UART0;
|
|
reset_id = SUNXI_RST_UART0;
|
|
break;
|
|
case 1:
|
|
clk_id = SUNXI_CLK_UART1;
|
|
reset_id = SUNXI_RST_UART1;
|
|
break;
|
|
case 2:
|
|
clk_id = SUNXI_CLK_UART2;
|
|
reset_id = SUNXI_RST_UART2;
|
|
break;
|
|
case 3:
|
|
clk_id = SUNXI_CLK_UART3;
|
|
reset_id = SUNXI_RST_UART3;
|
|
break;
|
|
default:
|
|
UART_ERR("uart%d is invalid\n", bus);
|
|
return -1;
|
|
}
|
|
if (enable)
|
|
{
|
|
reset = hal_reset_control_get(reset_type, reset_id);
|
|
hal_reset_control_deassert(reset);
|
|
hal_reset_control_put(reset);
|
|
|
|
clk = hal_clock_get(clk_type, clk_id);
|
|
ret = hal_clock_enable(clk);
|
|
if (ret)
|
|
{
|
|
UART_ERR("[uart%d] couldn't enable clk!\n", bus);
|
|
return -1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
clk = hal_clock_get(clk_type, clk_id);
|
|
ret = hal_clock_disable(clk);
|
|
if (ret)
|
|
{
|
|
UART_ERR("[uart%d] couldn't disable clk!\n", bus);
|
|
return -1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void uart_pinctrl_init(uart_port_t uart_port)
|
|
{
|
|
#ifdef CONFIG_DRIVER_SYSCONFIG
|
|
user_gpio_set_t gpio_cfg[4];
|
|
int count, i;
|
|
char uart_name[16];
|
|
gpio_pin_t uart_pin[4];
|
|
gpio_muxsel_t uart_muxsel[4];
|
|
|
|
memset(gpio_cfg, 0, sizeof(gpio_cfg));
|
|
sprintf(uart_name, "uart%d", uart_port);
|
|
count = Hal_Cfg_GetGPIOSecKeyCount(uart_name);
|
|
if (!count)
|
|
{
|
|
UART_ERR("[uart%d] not support in sys_config\n", uart_port);
|
|
return ;
|
|
}
|
|
Hal_Cfg_GetGPIOSecData(uart_name, gpio_cfg, count);
|
|
|
|
|
|
for (i = 0; i < count; i++)
|
|
{
|
|
uart_pin[i] = (gpio_cfg[i].port - 1) * 32 + gpio_cfg[i].port_num;
|
|
uart_muxsel[i] = gpio_cfg[i].mul_sel;
|
|
hal_gpio_pinmux_set_function(uart_pin[i], uart_muxsel[i]);
|
|
}
|
|
#else
|
|
/* TODO:use sys_config instead it, but DSP does not support sys_config */
|
|
switch (uart_port)
|
|
{
|
|
case UART_0:
|
|
hal_gpio_set_pull(UART0_RX, GPIO_PULL_UP);
|
|
hal_gpio_pinmux_set_function(UART0_TX, UART0_GPIO_FUNCTION);//TX
|
|
hal_gpio_pinmux_set_function(UART0_RX, UART0_GPIO_FUNCTION);//RX
|
|
break;
|
|
case UART_1:
|
|
hal_gpio_set_pull(UART1_RX, GPIO_PULL_UP);
|
|
hal_gpio_pinmux_set_function(UART1_TX, UART1_GPIO_FUNCTION);//TX
|
|
hal_gpio_pinmux_set_function(UART1_RX, UART1_GPIO_FUNCTION);//RX
|
|
break;
|
|
case UART_2:
|
|
hal_gpio_set_pull(UART2_RX, GPIO_PULL_UP);
|
|
hal_gpio_pinmux_set_function(UART2_TX, UART2_GPIO_FUNCTION);//TX
|
|
hal_gpio_pinmux_set_function(UART2_RX, UART2_GPIO_FUNCTION);//RX
|
|
break;
|
|
case UART_3:
|
|
hal_gpio_set_pull(UART3_RX, GPIO_PULL_UP);
|
|
hal_gpio_pinmux_set_function(UART3_TX, UART3_GPIO_FUNCTION);//TX
|
|
hal_gpio_pinmux_set_function(UART3_RX, UART3_GPIO_FUNCTION);//RX
|
|
break;
|
|
default:
|
|
UART_ERR("[uart%d] not support \n", uart_port);
|
|
break;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static void uart_pinctrl_uninit(uart_port_t uart_port)
|
|
{
|
|
switch (uart_port)
|
|
{
|
|
case UART_0:
|
|
hal_gpio_pinmux_set_function(UART0_TX, GPIO_MUXSEL_DISABLED);//TX
|
|
hal_gpio_pinmux_set_function(UART0_RX, GPIO_MUXSEL_DISABLED);//RX
|
|
break;
|
|
case UART_1:
|
|
hal_gpio_pinmux_set_function(UART1_TX, GPIO_MUXSEL_DISABLED);//TX
|
|
hal_gpio_pinmux_set_function(UART1_RX, GPIO_MUXSEL_DISABLED);//RX
|
|
break;
|
|
case UART_2:
|
|
hal_gpio_pinmux_set_function(UART2_TX, GPIO_MUXSEL_DISABLED);//TX
|
|
hal_gpio_pinmux_set_function(UART2_RX, GPIO_MUXSEL_DISABLED);//RX
|
|
break;
|
|
case UART_3:
|
|
hal_gpio_pinmux_set_function(UART3_TX, GPIO_MUXSEL_DISABLED);//TX
|
|
hal_gpio_pinmux_set_function(UART3_RX, GPIO_MUXSEL_DISABLED);//RX
|
|
break;
|
|
default:
|
|
UART_ERR("[uart%d] not support \n", uart_port);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* default uart config */
|
|
_uart_config_t uart_defconfig =
|
|
{
|
|
.baudrate = UART_BAUDRATE_115200,
|
|
.word_length = UART_WORD_LENGTH_8,
|
|
.stop_bit = UART_STOP_BIT_1,
|
|
.parity = UART_PARITY_NONE,
|
|
};
|
|
|
|
int32_t hal_uart_init(int32_t uart_port)
|
|
{
|
|
uart_priv_t *uart_priv = &g_uart_priv[uart_port];
|
|
uint32_t irqn = g_uart_irqn[uart_port];
|
|
uint32_t value = 0;
|
|
_uart_config_t *uart_config = &uart_defconfig;
|
|
char uart_name[12] = {0};
|
|
|
|
if ((!uart_port_is_valid(uart_port)) ||
|
|
(!uart_config_is_valid(uart_config)))
|
|
{
|
|
return HAL_UART_STATUS_ERROR_PARAMETER;
|
|
}
|
|
|
|
/* enable clk */
|
|
uart_clk_init(uart_port, true);
|
|
|
|
/* request gpio */
|
|
uart_pinctrl_init(uart_port);
|
|
|
|
/* config uart attributes */
|
|
uart_set_format(uart_port, uart_config->word_length,
|
|
uart_config->stop_bit, uart_config->parity);
|
|
|
|
/* force reset controller to disable transfer */
|
|
uart_reset(uart_port);
|
|
|
|
uart_set_baudrate(uart_port, uart_config->baudrate);
|
|
|
|
value |= UART_FCR_RXTRG_1_2 | UART_FCR_TXTRG_1_2 | UART_FCR_FIFO_EN;
|
|
uart_set_fifo(uart_port, value);
|
|
|
|
sprintf(uart_name, "uart%d", (int)uart_port);
|
|
if (uart_priv->uart_port == uart_port && uart_priv->irqn == irqn)
|
|
{
|
|
UART_ERR("irq for uart%ld already enabled\n", (long int)uart_port);
|
|
}
|
|
else
|
|
{
|
|
uart_priv->uart_port = uart_port;
|
|
uart_priv->irqn = irqn;
|
|
|
|
#if 1
|
|
if (request_irq(irqn, uart_irq_handler, 0, uart_name, uart_priv) < 0)
|
|
{
|
|
UART_ERR("request irq error\n");
|
|
return -1;
|
|
}
|
|
enable_irq(irqn);
|
|
#endif
|
|
|
|
#if 0
|
|
rt_hw_interrupt_install(irqn, uart_irq_handler, uart_priv, uart_name);
|
|
rt_hw_interrupt_umask(irqn);
|
|
#endif
|
|
}
|
|
|
|
uart_mailbox[uart_port] = hal_mailbox_create(uart_name, UART_FIFO_SIZE);
|
|
if (uart_mailbox[uart_port] == NULL)
|
|
{
|
|
UART_ERR("create mailbox fail\n");
|
|
return HAL_UART_STATUS_ERROR;
|
|
}
|
|
|
|
/* set uart IER */
|
|
uart_enable_irq(uart_port, UART_IER_RDI | UART_IER_RLSI);
|
|
|
|
/* force config */
|
|
uart_enable_busy_cfg(uart_port);
|
|
|
|
return SUNXI_HAL_OK;
|
|
}
|
|
|
|
int32_t hal_uart_deinit(int32_t uart_port)
|
|
{
|
|
/* disable clk */
|
|
uart_clk_init(uart_port, false);
|
|
uart_pinctrl_uninit(uart_port);
|
|
|
|
return SUNXI_HAL_OK;
|
|
}
|
|
|
|
int32_t hal_uart_power_control(int32_t dev, sunxi_hal_power_state_e state)
|
|
{
|
|
return SUNXI_HAL_OK;
|
|
}
|
|
|
|
static int _uart_putc(int devid, char c)
|
|
{
|
|
volatile uint32_t *sed_buf;
|
|
volatile uint32_t *sta;
|
|
|
|
sed_buf = (uint32_t *)(sunxi_uart_port[devid] + UART_THR);
|
|
sta = (uint32_t *)(sunxi_uart_port[devid] + UART_USR);
|
|
|
|
/* FIFO status, contain valid data */
|
|
while (!(*sta & 0x02));
|
|
*sed_buf = c;
|
|
|
|
return 1;
|
|
}
|
|
|
|
int32_t hal_uart_put_char(int32_t dev, char c)
|
|
{
|
|
return _uart_putc(dev, c);
|
|
}
|
|
|
|
int32_t hal_uart_send(int32_t dev, const uint8_t *data, uint32_t num)
|
|
{
|
|
int size;
|
|
|
|
hal_assert(data != NULL);
|
|
|
|
size = num;
|
|
while (num)
|
|
{
|
|
_uart_putc(dev, *data);
|
|
|
|
++ data;
|
|
-- num;
|
|
}
|
|
|
|
return size - num;
|
|
}
|
|
|
|
static int _uart_getc(int devid)
|
|
{
|
|
int ch = -1;
|
|
volatile uint32_t *rec_buf;
|
|
volatile uint32_t *sta;
|
|
volatile uint32_t *fifo;
|
|
|
|
rec_buf = (uint32_t *)(sunxi_uart_port[devid] + UART_RHB);
|
|
sta = (uint32_t *)(sunxi_uart_port[devid] + UART_USR);
|
|
fifo = (uint32_t *)(sunxi_uart_port[devid] + UART_RFL);
|
|
|
|
while (!(*fifo & 0x1ff));
|
|
|
|
/* Receive Data Available */
|
|
if (*sta & 0x08)
|
|
{
|
|
ch = *rec_buf & 0xff;
|
|
}
|
|
|
|
return ch;
|
|
}
|
|
|
|
uint8_t hal_uart_get_char(int32_t dev)
|
|
{
|
|
return _uart_getc(dev);
|
|
}
|
|
|
|
int32_t hal_uart_receive_polling(int32_t dev, uint8_t *data, uint32_t num)
|
|
{
|
|
int ch;
|
|
int size;
|
|
|
|
hal_assert(data != NULL);
|
|
size = num;
|
|
|
|
while (num)
|
|
{
|
|
ch = _uart_getc(dev);
|
|
if (ch == -1)
|
|
{
|
|
break;
|
|
}
|
|
|
|
*data = ch;
|
|
data ++;
|
|
num --;
|
|
|
|
/* FIXME: maybe only used for console? move it away! */
|
|
if (ch == '\n')
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
return size - num;
|
|
}
|
|
|
|
int32_t hal_uart_receive(int32_t dev, uint8_t *data, uint32_t num)
|
|
{
|
|
unsigned int data_rev;
|
|
int i = 0;
|
|
int32_t ret = -1, rev_count = 0;
|
|
|
|
hal_assert(data != NULL);
|
|
|
|
for (i = 0; i < num; i++)
|
|
{
|
|
ret = hal_mailbox_recv((hal_mailbox_t)uart_mailbox[dev], &data_rev, -1);
|
|
if (ret == 0)
|
|
{
|
|
rev_count++;
|
|
*(data + i) = (uint8_t)data_rev;
|
|
}
|
|
else
|
|
{
|
|
UART_ERR("receive error");
|
|
break;
|
|
}
|
|
}
|
|
|
|
return rev_count;
|
|
}
|
|
|
|
int32_t hal_uart_receive_no_block(int32_t dev, uint8_t *data, uint32_t num, int32_t timeout)
|
|
{
|
|
unsigned int data_rev;
|
|
int i = 0;
|
|
int32_t ret = -1, rev_count = 0;
|
|
|
|
hal_assert(data != NULL);
|
|
|
|
for (i = 0; i < num; i++)
|
|
{
|
|
ret = hal_mailbox_recv((hal_mailbox_t)uart_mailbox[dev], &data_rev, timeout);
|
|
if (ret == 0)
|
|
{
|
|
rev_count++;
|
|
*(data + i) = (uint8_t)data_rev;
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
return rev_count;
|
|
}
|
|
|
|
|
|
int32_t hal_uart_transfer(int32_t dev, const void *data_out,
|
|
void *data_in, uint32_t num)
|
|
{
|
|
return SUNXI_HAL_OK;
|
|
}
|
|
|
|
uint32_t hal_uart_get_tx_count(int32_t dev)
|
|
{
|
|
/* TODO: need verify */
|
|
return 0;
|
|
}
|
|
|
|
uint32_t hal_uart_get_rx_count(int32_t dev)
|
|
{
|
|
/* TODO: need verify */
|
|
return 0;
|
|
}
|
|
|
|
int32_t hal_uart_control(int32_t uart_port, int cmd, void *args)
|
|
{
|
|
_uart_config_t *uart_config;
|
|
uart_config = (_uart_config_t *)args;
|
|
|
|
/* config uart attributes */
|
|
uart_set_format(uart_port, uart_config->word_length,
|
|
uart_config->stop_bit, uart_config->parity);
|
|
uart_set_baudrate(uart_port, uart_config->baudrate);
|
|
|
|
return SUNXI_HAL_OK;
|
|
}
|
|
|
|
sunxi_hal_uart_status_t hal_uart_get_status(int32_t dev)
|
|
{
|
|
sunxi_hal_uart_status_t status = {1, 1, 0, 0, 0, 0, 0, 0};
|
|
|
|
return status;
|
|
}
|
|
|
|
int32_t hal_uart_set_modem_control(int32_t dev,
|
|
sunxi_hal_uart_modem_control_e control)
|
|
{
|
|
return SUNXI_HAL_OK;
|
|
}
|
|
|
|
sunxi_hal_uart_modem_status_t hal_uart_get_modem_status(int32_t dev)
|
|
{
|
|
sunxi_hal_uart_modem_status_t status = {0, 0, 0, 0, 0};
|
|
|
|
return status;
|
|
}
|
|
|
|
void hal_uart_set_loopback(uart_port_t uart_port, bool enable)
|
|
{
|
|
const unsigned long uart_base = sunxi_uart_port[uart_port];
|
|
uint32_t value;
|
|
|
|
value = hal_readb(uart_base + UART_MCR);
|
|
if (enable)
|
|
value |= UART_MCR_LOOP;
|
|
else
|
|
value &= ~(UART_MCR_LOOP);
|
|
hal_writeb(value, uart_base + UART_MCR);
|
|
|
|
}
|
|
|
|
int serial_driver_init(void)
|
|
{
|
|
UART_INFO("serial hal driver init");
|
|
return 0;
|
|
}
|
|
|