Files
TAIXIN/sdk/driver/uart/hguart_v4.c

727 lines
17 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* @file hguart_v4.c
* @author bxd
* @brief simple uart
* @version
* TXW81X
* @date 2023-08-02
*
* @copyright Copyright (c) 2023
*
*/
#include "typesdef.h"
#include "list.h"
#include "errno.h"
#include "dev.h"
#include "osal/irq.h"
#include "osal/semaphore.h"
#include "osal/mutex.h"
#include "osal/string.h"
#include "hal/uart.h"
#include "hal/gpio.h"
#include "dev/uart/hguart_v4.h"
#include "hguart_v4_hw.h"
#define SIMPLE_UART_LOCK(mutex, flag)\
do{\
if(flag){\
os_mutex_lock(mutex, osWaitForever);\
}\
}while(0);
#define SIMPLE_UART_UNLOCK(mutex, flag)\
do{\
if(flag){\
os_mutex_unlock(mutex);\
}\
}while(0);
/**********************************************************************************/
/* UART LOW LAYER FUNCTION */
/**********************************************************************************/
static int32 hguart_v4_set_dma(struct hguart_v4 *dev, uint32 enable)
{
if (enable) {
dev->use_dma = 1;
} else {
dev->use_dma = 0;
}
return RET_OK;
}
static int32 hguart_v4_dma_rx_config(struct hguart_v4_hw *p_uart, uint8 status)
{
uint32 _dmacon = p_uart->DMACON;
uint32 flag = 0;
if (status) {
_dmacon = 0x5;
} else {
_dmacon = 0;
}
/*!
* fix: kick 2 times to counteract an unnecessary rx dma done
*/
if (p_uart->CON & LL_SIMPLE_UART_CON_DMA_IE) {
p_uart->CON &=~ LL_SIMPLE_UART_CON_DMA_IE;
flag = 1;
//printf("<%x>", *(uint32 *)(0x40004b70));
}
p_uart->DMACON = _dmacon;
__NOP();__NOP();__NOP();__NOP();
//printf("<%x>", *(uint32 *)(0x40004b70));
//clear rx done pending
p_uart->CON |= LL_SIMPLE_UART_CON_CLRDMAPEND;
//kick again
p_uart->DMACON = _dmacon;
__NOP();__NOP();__NOP();__NOP();
//printf("<%x>", *(uint32 *)(0x40004b70));
//clear rx done pending
p_uart->CON |= LL_SIMPLE_UART_CON_CLRDMAPEND;
//printf("<%x>", *(uint32 *)(0x40004b70));
if (flag) {
flag = 0;
p_uart->CON |= LL_SIMPLE_UART_CON_DMA_IE;
}
return 0;
}
static int32 hguart_v4_dma_tx_config(struct hguart_v4_hw *p_uart, uint8 status)
{
uint32 _dmacon = p_uart->DMACON;
if (status) {
_dmacon = 0xA;
} else {
_dmacon = 0;
}
p_uart->DMACON = _dmacon;
return 0;
}
static int32 hguart_v4_set_time_out(struct hguart_v4_hw *p_uart, uint32 time_bit, uint32 enable)
{
if (enable) {
p_uart->CON |= LL_SIMPLE_UART_CON_TO_EN;
p_uart->TOCON = LL_SIMPLE_UART_TOCON(time_bit);
} else {
p_uart->CON &=~ LL_SIMPLE_UART_CON_TO_EN;
p_uart->TOCON = 0;
}
return RET_OK;
}
/**********************************************************************************/
/* UART ATTCH FUNCTION */
/**********************************************************************************/
static int32 hguart_v4_open(struct uart_device *uart, uint32 baudrate) {
struct hguart_v4 *dev = (struct hguart_v4 *)uart;
struct hguart_v4_hw *hw = (struct hguart_v4_hw *)dev->hw;
if (dev->opened) {
if (!dev->dsleep) {
return -EBUSY;
}
}
/* pin config */
if (pin_func(dev->dev.dev.dev_id , 1) != RET_OK) {
return RET_ERR;
}
/* reg config */
hw->BAUD = (peripheral_clock_get(HG_APB0_PT_UART4) / baudrate) - 1;
hw->CON = LL_SIMPLE_UART_CON_UARTEN;
dev->opened = 1;
dev->irq_dma_rx = 0;
dev->irq_dma_tx = 0;
dev->use_dma = 0;
dev->dsleep = 0;
return RET_OK;
}
static int32 hguart_v4_close(struct uart_device *uart) {
struct hguart_v4 *dev = (struct hguart_v4 *)uart;
struct hguart_v4_hw *hw = (struct hguart_v4_hw *)dev->hw;
if (!dev->opened) {
return RET_OK;
}
irq_disable(dev->irq_num );
pin_func(dev->dev.dev.dev_id, 0);
hw->CON &= ~ LL_SIMPLE_UART_CON_UARTEN;
dev->opened = 0;
dev->irq_dma_rx = 0;
dev->irq_dma_tx = 0;
dev->use_dma = 0;
dev->dsleep = 0;
return RET_OK;
}
static int32 hguart_v4_putc(struct uart_device *uart, int8 value) {
struct hguart_v4 *dev = (struct hguart_v4 *)uart;
struct hguart_v4_hw *hw = (struct hguart_v4_hw *)dev->hw;
if ((!dev->opened) || (dev->dsleep)) {
return RET_ERR;
}
SIMPLE_UART_LOCK(&dev->mutex_tx, dev->debug_uart);
if (dev->opened && (hw->CON & LL_SIMPLE_UART_CON_UARTEN)) {
while(!(hw->CON & LL_SIMPLE_UART_CON_TXBUFEMPTY));
hw->DATA = value;
SIMPLE_UART_UNLOCK(&dev->mutex_tx, dev->debug_uart);
return RET_OK;
} else {
SIMPLE_UART_UNLOCK(&dev->mutex_tx, dev->debug_uart);
return -EIO;
}
}
static uint8 hguart_v4_getc(struct uart_device *uart) {
struct hguart_v4 *dev = (struct hguart_v4 *)uart;
struct hguart_v4_hw *hw = (struct hguart_v4_hw *)dev->hw;
if ((!dev->opened) || (dev->dsleep)) {
return RET_ERR;
}
SIMPLE_UART_LOCK(&dev->mutex_rx, dev->debug_uart);
while(!(hw->CON & LL_SIMPLE_UART_CON_RXBUFNOTEMPTY));
hw->CON |= LL_SIMPLE_UART_CON_CLRRXDONE;
SIMPLE_UART_UNLOCK(&dev->mutex_rx, dev->debug_uart);
return hw->DATA;
}
static int32 hguart_v4_rs485_de_set(void)
{
if(PIN_UART4_DE != 255) {
gpio_set_val(PIN_UART4_DE, 1);
return RET_OK;
}
if(PIN_UART5_DE != 255) {
gpio_set_val(PIN_UART5_DE, 1);
return RET_OK;
}
return RET_ERR;
}
static int32 hguart_v4_rs485_de_reset(void)
{
if(PIN_UART4_DE != 255) {
gpio_set_val(PIN_UART4_DE, 0);
return RET_OK;
}
if(PIN_UART5_DE != 255) {
gpio_set_val(PIN_UART5_DE, 0);
return RET_OK;
}
return RET_ERR;
}
static int32 hguart_v4_rs485_re_set(void)
{
if(PIN_UART4_RE != 255) {
gpio_set_val(PIN_UART4_RE, 1);
return RET_OK;
}
if(PIN_UART5_RE != 255) {
gpio_set_val(PIN_UART5_RE, 1);
return RET_OK;
}
return RET_ERR;
}
static int32 hguart_v4_rs485_re_reset(void)
{
if(PIN_UART4_RE != 255) {
gpio_set_val(PIN_UART4_RE, 0);
return RET_OK;
}
if(PIN_UART5_RE != 255) {
gpio_set_val(PIN_UART5_RE, 0);
return RET_OK;
}
return RET_ERR;
}
static int32 hguart_v4_puts(struct uart_device *uart, uint8 *buf, uint32 size) {
struct hguart_v4 *dev = (struct hguart_v4 *)uart;
struct hguart_v4_hw *hw = (struct hguart_v4_hw *)dev->hw;
uint32 i = 0;
if ((!dev->opened) || (dev->dsleep)) {
return RET_ERR;
}
SIMPLE_UART_LOCK(&dev->mutex_tx, dev->debug_uart);
if (dev->use_dma) {
if (dev->debug_uart) {
//enable uart 1Byte tx done irq
hw->CON |= LL_SIMPLE_UART_CON_UARTTXIE;
dev->p_tx_buf = (uint8 *)buf;
dev->tx_total_byte = size;
dev->tx_cur_byte = 0;
//send 1Byte to trigger 1Byte tx done irq
hw->DATA = buf[0];
os_sema_down(&dev->sema_tx, osWaitForever);
//disable uart 1Byte tx done irq
hw->CON &=~ LL_SIMPLE_UART_CON_UARTTXIE;
} else if (dev->rs485_set) {
hguart_v4_dma_tx_config(hw, 0);
hw->CON |= LL_SIMPLE_UART_CON_CLRDMAPEND;
hguart_v4_rs485_de_set();
hguart_v4_rs485_re_set();
hw->DMAADR = (uint32)buf;
hw->DMALEN = size;
hguart_v4_dma_tx_config(hw, 1);
/* waiting for tx done */
while (!(hw->CON & LL_SIMPLE_UART_CON_DMAPEND));
hguart_v4_rs485_re_reset();
hguart_v4_rs485_de_reset();
} else {
for (i = 0; i < size; i++) {
hguart_v4_putc(uart, buf[i]);
}
}
} else {
for (i = 0; i < size; i++) {
hguart_v4_putc(uart, buf[i]);
}
}
SIMPLE_UART_UNLOCK(&dev->mutex_tx, dev->debug_uart);
return RET_OK;
}
static int32 hguart_v4_gets(struct uart_device *uart, uint8 *buf, uint32 size) {
struct hguart_v4 *dev = (struct hguart_v4 *)uart;
struct hguart_v4_hw *hw = (struct hguart_v4_hw *)dev->hw;
uint32 i = 0;
if ((!dev->opened) || (dev->dsleep)) {
return RET_ERR;
}
SIMPLE_UART_LOCK(&dev->mutex_rx, dev->debug_uart);
if (dev->use_dma) {
hw->DMAADR = (uint32)buf;
hw->DMALEN = size;
hguart_v4_dma_rx_config(hw, 1);
} else {
for (i = 0; i < size; i++) {
hguart_v4_getc(uart);
}
}
SIMPLE_UART_UNLOCK(&dev->mutex_rx, dev->debug_uart);
return i;
}
static int32 hguart_v4_ioctl(struct uart_device *uart, enum uart_ioctl_cmd ioctl_cmd, uint32 param1, uint32 param2) {
int32 ret_val = RET_OK;
struct hguart_v4 *dev = (struct hguart_v4 *)uart;
if ((!dev->opened) || (dev->dsleep)) {
return RET_ERR;
}
switch (ioctl_cmd) {
case (UART_IOCTL_CMD_USE_DMA):
ret_val = hguart_v4_set_dma(dev, param1);
break;
case (UART_IOCTL_CMD_SET_TIME_OUT):
ret_val = hguart_v4_set_time_out((struct hguart_v4_hw *)dev->hw, param1, param2);
break;
case (UART_IOCTL_CMD_DISABLE_DEBUG_SELECTION):
dev->debug_uart = (param1) ? (1) : (0);
ret_val = RET_OK;
break;
case (UART_IOCTL_CMD_SET_RS485_EN):
dev->rs485_set = (param1) ? (1) : (0);
ret_val = RET_OK;
break;
default:
ret_val = -ENOTSUPP;
break;
}
return ret_val;
}
/* interrupt handler */
static void hguart_v4_irq_handler(void *data) {
struct hguart_v4 *dev = (struct hguart_v4 *)data;
struct hguart_v4_hw *hw = (struct hguart_v4_hw *)dev->hw;
if ((hw->CON & LL_SIMPLE_UART_CON_UARTTXIE) && (hw->CON & LL_SIMPLE_UART_CON_TXDONE)) {
hw->CON |= LL_SIMPLE_UART_CON_CLRTXDONE;
dev->tx_cur_byte++;
//printf("cur len:%d\r\n", dev->tx_cur_byte);
if (dev->tx_cur_byte == dev->tx_total_byte) {
//printf("up\r\n");
os_sema_up(&dev->sema_tx);
//diable uart tx 1byte done irq
hw->CON &=~ LL_SIMPLE_UART_CON_UARTTXIE;
} else {
hw->DATA = dev->p_tx_buf[dev->tx_cur_byte];
}
}
if ((hw->CON & LL_SIMPLE_UART_CON_UARTRXIE) && (hw->CON & LL_SIMPLE_UART_CON_RXBUFNOTEMPTY)) {
hw->CON |= LL_SIMPLE_UART_CON_CLRRXDONE;
if (dev->irq_hdl) {
dev->irq_hdl(UART_IRQ_FLAG_RX_BYTE, dev->irq_data, hw->DATA, 0);
}
}
if ((hw->CON & LL_SIMPLE_UART_CON_FERRIE) && (hw->CON & LL_SIMPLE_UART_CON_FERR)) {
hw->CON |= LL_SIMPLE_UART_CON_CLRFERR;
if (dev->irq_hdl) {
dev->irq_hdl(UART_IRQ_FLAG_FRAME_ERR, dev->irq_data, 0, 0);
}
}
//UART4/5 TIMEOUT来了之后DMA DONE也会起来故DMA DONE中断之前要判断是否TIMEOUT
if (!(hw->CON & LL_SIMPLE_UART_CON_TO_PENDING)) {
if ((hw->CON & LL_SIMPLE_UART_CON_DMA_IE) && (hw->CON & LL_SIMPLE_UART_CON_DMAPEND)) {
hw->CON |= LL_SIMPLE_UART_CON_CLRDMAPEND;
if (dev->irq_hdl) {
if (dev->irq_dma_rx) {
dev->irq_hdl(UART_IRQ_FLAG_DMA_RX_DONE, dev->irq_data, hw->DMACNT, 0);
} else {
dev->irq_hdl(UART_IRQ_FLAG_DMA_TX_DONE, dev->irq_data, hw->DMACNT, 0);
}
}
}
} else {
if ((hw->CON & LL_SIMPLE_UART_CON_TO_IE) && (hw->CON & LL_SIMPLE_UART_CON_TO_PENDING)) {
hw->CON |= (LL_SIMPLE_UART_CON_CLRTOPEND | LL_SIMPLE_UART_CON_CLRDMAPEND);
if (dev->irq_hdl) {
dev->irq_hdl(UART_IRQ_FLAG_TIME_OUT, dev->irq_data, hw->DMACNT, 0);
}
}
}
}
/* request interrupt */
static int32 hguart_v4_request_irq(struct uart_device *uart, uart_irq_hdl irqhdl, uint32 irq_flag, uint32 data) {
struct hguart_v4 *dev = (struct hguart_v4 *)uart;
struct hguart_v4_hw *hw = (struct hguart_v4_hw *)dev->hw;
if ((!dev->opened) || (dev->dsleep)) {
return RET_ERR;
}
dev->irq_hdl = irqhdl;
dev->irq_data = data ;
if (irq_flag & UART_IRQ_FLAG_RX_BYTE) {
hw->CON |= LL_SIMPLE_UART_CON_UARTRXIE;
}
if (irq_flag & UART_IRQ_FLAG_FRAME_ERR) {
hw->CON |= LL_SIMPLE_UART_CON_FERRIE;
}
if (irq_flag & UART_IRQ_FLAG_DMA_RX_DONE) {
hw->CON |= LL_SIMPLE_UART_CON_DMA_IE;
dev->irq_dma_tx = 0;
dev->irq_dma_rx = 1;
}
if (irq_flag & UART_IRQ_FLAG_DMA_TX_DONE) {
hw->CON |= LL_SIMPLE_UART_CON_DMA_IE;
dev->irq_dma_tx = 1;
dev->irq_dma_rx = 0;
}
if (irq_flag & UART_IRQ_FLAG_TIME_OUT) {
hw->CON |= LL_SIMPLE_UART_CON_TO_IE;
}
#if defined(TXW81X)
irq_enable(dev->comm_irq_num);
#elif defined(TXW82X)
irq_enable(dev->irq_num);
#endif
return RET_OK;
}
static int32 hguart_v4_release_irq(struct uart_device *uart, uint32 irq_flag) {
struct hguart_v4 *dev = (struct hguart_v4 *)uart;
struct hguart_v4_hw *hw = (struct hguart_v4_hw *)dev->hw;
if ((!dev->opened) || (dev->dsleep)) {
return RET_ERR;
}
if (irq_flag & UART_IRQ_FLAG_RX_BYTE) {
hw->CON &= ~ LL_SIMPLE_UART_CON_UARTRXIE;
}
if (irq_flag & UART_IRQ_FLAG_FRAME_ERR) {
hw->CON &= ~ LL_SIMPLE_UART_CON_FERRIE;
}
if (irq_flag & UART_IRQ_FLAG_DMA_RX_DONE) {
hw->CON &=~ LL_SIMPLE_UART_CON_DMA_IE;
}
if (irq_flag & UART_IRQ_FLAG_TIME_OUT) {
hw->CON &=~ LL_SIMPLE_UART_CON_TO_IE;
}
#if defined(TXW81X)
irq_disable(dev->comm_irq_num);
#elif defined(TXW82X)
irq_disable(dev->irq_num);
#endif
return RET_OK;
}
#ifdef CONFIG_SLEEP
int32 hguart_v4_suspend(struct dev_obj *obj)
{
struct hguart_v4 *dev = (struct hguart_v4 *)obj;
struct hguart_v4_hw *hw = (struct hguart_v4_hw *)dev->hw;
if (!dev->opened) {
return RET_OK;
}
if (0 > os_mutex_lock(&dev->bp_suspend_lock, 10000)) {
return RET_ERR;
}
/*!
* Close the UART
*/
hw->CON &= ~ BIT(4);
// pin_func(dev->dev.dev.dev_id, 0);
#if defined(TXW81X)
irq_disable(dev->comm_irq_num);
#elif defined(TXW82X)
irq_disable(dev->irq_num);
#endif
/*
* clear pending
*/
hw->CON |= 0x3f000000;
os_memset((void *)&dev->bp_regs, 0, sizeof(dev->bp_regs));
/*
* save the reglist
*/
dev->bp_regs.con = hw->CON;
dev->bp_regs.baud = hw->BAUD;
dev->bp_regs.tocon = hw->TOCON;
dev->bp_regs.dmaadr = hw->DMAADR;
dev->bp_regs.dmalen = hw->DMALEN;
dev->bp_regs.dmacon = hw->DMACON;
/*
* save the irq_hdl created by user
*/
dev->bp_irq_hdl = dev->irq_hdl;
dev->bp_irq_data = dev->irq_data;
//venus v2uart4&5 clk and uart0 clk are share the same source
if (UART4_BASE == (uint32)hw) {
// sysctrl_uart4_clk_close();
} else if (UART5_BASE == (uint32)hw) {
// sysctrl_uart5_clk_close();
}
dev->dsleep = 1;
os_mutex_unlock(&dev->bp_suspend_lock);
return RET_OK;
}
int32 hguart_v4_resume(struct dev_obj *obj)
{
struct hguart_v4 *dev = (struct hguart_v4 *)obj;
struct hguart_v4_hw *hw = (struct hguart_v4_hw *)dev->hw;
if (!dev->opened) {
return RET_OK;
}
if (0 > os_mutex_lock(&dev->bp_resume_lock, 10000)) {
return RET_ERR;
}
/* pin config */
// if (pin_func(dev->dev.dev.dev_id, 1) != RET_OK) {
// return RET_ERR;
// }
/*
* recovery the UART clk
*/
if (UART4_BASE == (uint32)hw) {
// sysctrl_uart4_clk_open();
} else if (UART5_BASE == (uint32)hw) {
// sysctrl_uart5_clk_open();
}
/*
* recovery the reglist from sram
*/
hw->CON = dev->bp_regs.con;
hw->BAUD = dev->bp_regs.baud;
hw->TOCON = dev->bp_regs.tocon;
hw->DMAADR = dev->bp_regs.dmaadr;
hw->DMALEN = dev->bp_regs.dmalen;
hw->DMACON = dev->bp_regs.dmacon;
/*
* recovery the irq handle and data
*/
dev->irq_hdl = dev->bp_irq_hdl;
dev->irq_data = dev->bp_irq_data;
os_memset((void *)&dev->bp_regs, 0, sizeof(dev->bp_regs));
/*!
* Open the UART
*/
hw->CON |= BIT(4);
if (hw->DMACON & 1) {
hguart_v4_dma_rx_config(hw, 1);
}
#if defined(TXW81X)
irq_enable(dev->comm_irq_num);
#elif defined(TXW82X)
irq_enable(dev->irq_num);
#endif
dev->dsleep = 0;
os_mutex_unlock(&dev->bp_resume_lock);
return RET_OK;
}
#endif
static const struct uart_hal_ops uart_v4_ops = {
.open = hguart_v4_open,
.close = hguart_v4_close,
.getc = hguart_v4_getc,
.putc = hguart_v4_putc,
.gets = hguart_v4_gets,
.puts = hguart_v4_puts,
.ioctl = hguart_v4_ioctl,
.request_irq = hguart_v4_request_irq,
.release_irq = hguart_v4_release_irq,
#ifdef CONFIG_SLEEP
.ops.suspend = hguart_v4_suspend,
.ops.resume = hguart_v4_resume,
#endif
};
int32 hguart_v4_attach(uint32 dev_id, struct hguart_v4 *uart) {
uart->irq_data = 0;
uart->irq_hdl = NULL;
uart->opened = 0;
uart->irq_dma_rx = 0;
uart->irq_dma_tx = 0;
uart->use_dma = 0;
uart->debug_uart = 0;
uart->rs485_set = 0;
uart->dsleep = 0;
uart->p_tx_buf = NULL;
uart->tx_cur_byte = 0;
uart->tx_total_byte = 0;
uart->dev.dev.ops = (const struct devobj_ops *)&uart_v4_ops;
os_sema_init(&uart->sema_tx, 0);
os_mutex_init(&uart->mutex_rx);
os_mutex_init(&uart->mutex_tx);
#ifdef CONFIG_SLEEP
os_mutex_init(&uart->bp_suspend_lock);
os_mutex_init(&uart->bp_resume_lock);
#endif
request_irq(uart->irq_num, hguart_v4_irq_handler, uart);
dev_register(dev_id, (struct dev_obj *)uart);
return RET_OK;
}