1356 lines
36 KiB
C
1356 lines
36 KiB
C
|
/***************************************************************************
|
||
|
*
|
||
|
* Copyright 2015-2019 BES.
|
||
|
* All rights reserved. All unpublished rights reserved.
|
||
|
*
|
||
|
* No part of this work may be used or reproduced in any form or by any
|
||
|
* means, or stored in a database or retrieval system, without prior written
|
||
|
* permission of BES.
|
||
|
*
|
||
|
* Use of this work is governed by a license granted by BES.
|
||
|
* This work contains confidential and proprietary information of
|
||
|
* BES. which is protected by copyright, trade secret,
|
||
|
* trademark and other intellectual property rights.
|
||
|
*
|
||
|
****************************************************************************/
|
||
|
#ifdef CHIP_HAS_UART
|
||
|
|
||
|
#include "plat_addr_map.h"
|
||
|
#include "hal_uart.h"
|
||
|
#include "reg_uart.h"
|
||
|
#include "cmsis_nvic.h"
|
||
|
#include "hal_bootmode.h"
|
||
|
#include "hal_cmu.h"
|
||
|
#include "hal_dma.h"
|
||
|
#include "hal_iomux.h"
|
||
|
#include "hal_timer.h"
|
||
|
#include "hal_trace.h"
|
||
|
#include "string.h"
|
||
|
|
||
|
#define UART_FLUSH_DELAY_DEFAULT 2
|
||
|
|
||
|
enum UART_RX_DMA_MODE_T {
|
||
|
UART_RX_DMA_MODE_NORMAL,
|
||
|
UART_RX_DMA_MODE_PINGPANG,
|
||
|
UART_RX_DMA_MODE_STREAM,
|
||
|
UART_RX_DMA_MODE_BUF_LIST,
|
||
|
};
|
||
|
|
||
|
struct HAL_UART_HW_DESC_T {
|
||
|
struct UART_T *base;
|
||
|
IRQn_Type irq;
|
||
|
enum HAL_CMU_MOD_ID_T mod;
|
||
|
enum HAL_CMU_MOD_ID_T apb;
|
||
|
enum HAL_DMA_PERIPH_T rx_periph;
|
||
|
enum HAL_DMA_PERIPH_T tx_periph;
|
||
|
};
|
||
|
|
||
|
static const struct HAL_UART_HW_DESC_T uart[HAL_UART_ID_QTY] = {
|
||
|
{
|
||
|
.base = (struct UART_T *)UART0_BASE,
|
||
|
.irq = UART0_IRQn,
|
||
|
.mod = HAL_CMU_MOD_O_UART0,
|
||
|
.apb = HAL_CMU_MOD_P_UART0,
|
||
|
.rx_periph = HAL_GPDMA_UART0_RX,
|
||
|
.tx_periph = HAL_GPDMA_UART0_TX,
|
||
|
},
|
||
|
#if (CHIP_HAS_UART > 1)
|
||
|
{
|
||
|
.base = (struct UART_T *)UART1_BASE,
|
||
|
.irq = UART1_IRQn,
|
||
|
.mod = HAL_CMU_MOD_O_UART1,
|
||
|
.apb = HAL_CMU_MOD_P_UART1,
|
||
|
.rx_periph = HAL_GPDMA_UART1_RX,
|
||
|
.tx_periph = HAL_GPDMA_UART1_TX,
|
||
|
},
|
||
|
#endif
|
||
|
#if (CHIP_HAS_UART > 2)
|
||
|
{
|
||
|
.base = (struct UART_T *)UART2_BASE,
|
||
|
.irq = UART2_IRQn,
|
||
|
.mod = HAL_CMU_MOD_O_UART2,
|
||
|
.apb = HAL_CMU_MOD_P_UART2,
|
||
|
.rx_periph = HAL_GPDMA_UART2_RX,
|
||
|
.tx_periph = HAL_GPDMA_UART2_TX,
|
||
|
},
|
||
|
#endif
|
||
|
#ifdef BT_UART
|
||
|
{
|
||
|
.base = (struct UART_T *)BT_UART_BASE,
|
||
|
.irq = INVALID_IRQn,
|
||
|
.mod = HAL_CMU_MOD_QTY,
|
||
|
.apb = HAL_CMU_MOD_QTY,
|
||
|
.rx_periph = HAL_DMA_PERIPH_NULL,
|
||
|
.tx_periph = HAL_DMA_PERIPH_NULL,
|
||
|
},
|
||
|
#endif
|
||
|
};
|
||
|
|
||
|
static bool init_done = false;
|
||
|
|
||
|
static HAL_UART_IRQ_HANDLER_T irq_handler[HAL_UART_ID_QTY] = { NULL };
|
||
|
|
||
|
static HAL_UART_IRQ_RXDMA_HANDLER_T rxdma_handler[HAL_UART_ID_QTY] = { NULL };
|
||
|
static HAL_UART_IRQ_TXDMA_HANDLER_T txdma_handler[HAL_UART_ID_QTY] = { NULL };
|
||
|
|
||
|
static uint8_t recv_dma_chan[HAL_UART_ID_QTY];
|
||
|
static uint8_t send_dma_chan[HAL_UART_ID_QTY];
|
||
|
|
||
|
static uint32_t recv_dma_size[HAL_UART_ID_QTY];
|
||
|
static uint32_t send_dma_size[HAL_UART_ID_QTY];
|
||
|
|
||
|
static union HAL_UART_IRQ_T recv_mask[HAL_UART_ID_QTY];
|
||
|
|
||
|
static enum UART_RX_DMA_MODE_T recv_dma_mode[HAL_UART_ID_QTY];
|
||
|
|
||
|
static const char * const err_invalid_id = "Invalid UART ID: %d";
|
||
|
static const char * const err_recv_dma_api = "%s: Set RT irq in hal_uart_dma_recv_mask... to avoid data lost";
|
||
|
|
||
|
static const struct HAL_UART_CFG_T default_cfg = {
|
||
|
.parity = HAL_UART_PARITY_NONE,
|
||
|
.stop = HAL_UART_STOP_BITS_1,
|
||
|
.data = HAL_UART_DATA_BITS_8,
|
||
|
.flow = HAL_UART_FLOW_CONTROL_RTSCTS,
|
||
|
.tx_level = HAL_UART_FIFO_LEVEL_1_2,
|
||
|
.rx_level = HAL_UART_FIFO_LEVEL_1_4,
|
||
|
.baud = 921600,
|
||
|
.dma_rx = false,
|
||
|
.dma_tx = false,
|
||
|
.dma_rx_stop_on_err = false,
|
||
|
};
|
||
|
|
||
|
static void hal_uart_irq_handler(void);
|
||
|
|
||
|
static void set_baud_rate(enum HAL_UART_ID_T id, uint32_t rate)
|
||
|
{
|
||
|
uint32_t mod_clk;
|
||
|
uint32_t ibrd, fbrd;
|
||
|
uint32_t div;
|
||
|
|
||
|
mod_clk = 0;
|
||
|
#ifdef PERIPH_PLL_FREQ
|
||
|
if (PERIPH_PLL_FREQ / 2 > 2 * hal_cmu_get_crystal_freq()) {
|
||
|
// Init to OSC_X2
|
||
|
mod_clk = 2 * hal_cmu_get_crystal_freq() / 16;
|
||
|
if (cfg->baud > mod_clk) {
|
||
|
mod_clk = PERIPH_PLL_FREQ / 2 / 16;
|
||
|
|
||
|
if (id == HAL_UART_ID_0) {
|
||
|
hal_cmu_uart0_set_div(2);
|
||
|
#if (CHIP_HAS_UART > 1)
|
||
|
} else if (id == HAL_UART_ID_1) {
|
||
|
hal_cmu_uart1_set_div(2);
|
||
|
#endif
|
||
|
#if (CHIP_HAS_UART > 2)
|
||
|
} else if (id == HAL_UART_ID_2) {
|
||
|
hal_cmu_uart2_set_div(2);
|
||
|
#endif
|
||
|
}
|
||
|
} else {
|
||
|
mod_clk = 0;
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
if (mod_clk == 0) {
|
||
|
enum HAL_CMU_PERIPH_FREQ_T periph_freq;
|
||
|
|
||
|
// Init to OSC
|
||
|
mod_clk = hal_cmu_get_crystal_freq() / 16;
|
||
|
if (rate > mod_clk) {
|
||
|
mod_clk *= 2;
|
||
|
periph_freq = HAL_CMU_PERIPH_FREQ_52M;
|
||
|
} else {
|
||
|
periph_freq = HAL_CMU_PERIPH_FREQ_26M;
|
||
|
}
|
||
|
|
||
|
if (id == HAL_UART_ID_0) {
|
||
|
hal_cmu_uart0_set_freq(periph_freq);
|
||
|
#if (CHIP_HAS_UART > 1)
|
||
|
} else if (id == HAL_UART_ID_1) {
|
||
|
hal_cmu_uart1_set_freq(periph_freq);
|
||
|
#endif
|
||
|
#if (CHIP_HAS_UART > 2)
|
||
|
} else if (id == HAL_UART_ID_2) {
|
||
|
hal_cmu_uart2_set_freq(periph_freq);
|
||
|
#endif
|
||
|
}
|
||
|
}
|
||
|
|
||
|
div = (mod_clk * 64 + rate / 2) / rate;
|
||
|
ibrd = div / 64;
|
||
|
fbrd = div % 64;
|
||
|
if (ibrd == 0 || ibrd >= 65535) {
|
||
|
ASSERT(false, "Invalid baud param: %d", rate);
|
||
|
}
|
||
|
|
||
|
uart[id].base->UARTIBRD = ibrd;
|
||
|
uart[id].base->UARTFBRD = fbrd;
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
int hal_uart_open(enum HAL_UART_ID_T id, const struct HAL_UART_CFG_T *cfg)
|
||
|
{
|
||
|
uint32_t cr, lcr, dmacr;
|
||
|
int i;
|
||
|
|
||
|
ASSERT(id < HAL_UART_ID_QTY, err_invalid_id, id);
|
||
|
|
||
|
if (!init_done) {
|
||
|
init_done = true;
|
||
|
for (i = HAL_UART_ID_0; i < HAL_UART_ID_QTY; i++) {
|
||
|
recv_dma_chan[i] = HAL_DMA_CHAN_NONE;
|
||
|
send_dma_chan[i] = HAL_DMA_CHAN_NONE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (hal_uart_opened(id)) {
|
||
|
hal_uart_close(id);
|
||
|
}
|
||
|
|
||
|
if (cfg == NULL) {
|
||
|
cfg = &default_cfg;
|
||
|
}
|
||
|
|
||
|
hal_cmu_clock_enable(uart[id].mod);
|
||
|
hal_cmu_clock_enable(uart[id].apb);
|
||
|
hal_cmu_reset_clear(uart[id].mod);
|
||
|
hal_cmu_reset_clear(uart[id].apb);
|
||
|
|
||
|
cr = lcr = 0;
|
||
|
|
||
|
switch (cfg->parity) {
|
||
|
case HAL_UART_PARITY_NONE:
|
||
|
break;
|
||
|
case HAL_UART_PARITY_ODD:
|
||
|
lcr |= UARTLCR_H_PEN;
|
||
|
break;
|
||
|
case HAL_UART_PARITY_EVEN:
|
||
|
lcr |= UARTLCR_H_PEN
|
||
|
| UARTLCR_H_EPS;
|
||
|
break;
|
||
|
case HAL_UART_PARITY_FORCE1:
|
||
|
lcr |= UARTLCR_H_PEN
|
||
|
| UARTLCR_H_SPS;
|
||
|
break;
|
||
|
case HAL_UART_PARITY_FORCE0:
|
||
|
lcr |= UARTLCR_H_PEN
|
||
|
| UARTLCR_H_EPS
|
||
|
| UARTLCR_H_SPS;
|
||
|
break;
|
||
|
default:
|
||
|
ASSERT(false, "Invalid parity param: %d", cfg->parity);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (cfg->stop == HAL_UART_STOP_BITS_2) {
|
||
|
lcr |= UARTLCR_H_STP2;
|
||
|
} else if (cfg->stop != HAL_UART_STOP_BITS_1) {
|
||
|
ASSERT(false, "Invalid stop bits param: %d", cfg->stop);
|
||
|
}
|
||
|
|
||
|
switch (cfg->data) {
|
||
|
case HAL_UART_DATA_BITS_5:
|
||
|
lcr |= UARTLCR_H_WLEN_5;
|
||
|
break;
|
||
|
case HAL_UART_DATA_BITS_6:
|
||
|
lcr |= UARTLCR_H_WLEN_6;
|
||
|
break;
|
||
|
case HAL_UART_DATA_BITS_7:
|
||
|
lcr |= UARTLCR_H_WLEN_7;
|
||
|
break;
|
||
|
case HAL_UART_DATA_BITS_8:
|
||
|
lcr |= UARTLCR_H_WLEN_8;
|
||
|
break;
|
||
|
default:
|
||
|
ASSERT(false, "Invalid data bits param: %d", cfg->data);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
switch (cfg->flow) {
|
||
|
case HAL_UART_FLOW_CONTROL_NONE:
|
||
|
break;
|
||
|
case HAL_UART_FLOW_CONTROL_RTS:
|
||
|
cr |= UARTCR_RTSEN;
|
||
|
break;
|
||
|
case HAL_UART_FLOW_CONTROL_CTS:
|
||
|
cr |= UARTCR_CTSEN;
|
||
|
break;
|
||
|
case HAL_UART_FLOW_CONTROL_RTSCTS:
|
||
|
cr |= UARTCR_RTSEN
|
||
|
| UARTCR_CTSEN;
|
||
|
break;
|
||
|
default:
|
||
|
ASSERT(false, "Invalid flow control param: %d", cfg->flow);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
lcr |= UARTLCR_H_FEN | UARTLCR_H_DMA_RT_CNT(9);
|
||
|
cr |= UARTCR_UARTEN | UARTCR_TXE | UARTCR_RXE;
|
||
|
|
||
|
dmacr = 0;
|
||
|
if (cfg->dma_rx) {
|
||
|
dmacr |= UARTDMACR_RXDMAE;
|
||
|
}
|
||
|
if (cfg->dma_tx) {
|
||
|
dmacr |= UARTDMACR_TXDMAE;
|
||
|
}
|
||
|
if (cfg->dma_rx_stop_on_err) {
|
||
|
dmacr |= UARTDMACR_DMAONERR;
|
||
|
}
|
||
|
|
||
|
// Disable UART
|
||
|
uart[id].base->UARTCR &= ~UARTCR_UARTEN;
|
||
|
// Empty FIFO
|
||
|
uart[id].base->UARTLCR_H &= ~UARTLCR_H_FEN;
|
||
|
// Wait until UART becomes idle
|
||
|
while (((uart[id].base->UARTFR) & UARTFR_BUSY) != 0);
|
||
|
// Clear previous errors
|
||
|
uart[id].base->UARTECR = 1;
|
||
|
// Clear previous IRQs
|
||
|
uart[id].base->UARTIMSC = 0;
|
||
|
uart[id].base->UARTICR = ~0UL;
|
||
|
// Configure UART
|
||
|
set_baud_rate(id, cfg->baud);
|
||
|
uart[id].base->UARTLCR_H = lcr;
|
||
|
uart[id].base->UARTDMACR = dmacr;
|
||
|
uart[id].base->UARTIFLS = UARTIFLS_TXFIFO_LEVEL(cfg->tx_level) |
|
||
|
UARTIFLS_RXFIFO_LEVEL(cfg->rx_level);
|
||
|
uart[id].base->UARTCR = cr;
|
||
|
|
||
|
if (uart[id].irq != INVALID_IRQn) {
|
||
|
NVIC_SetVector(uart[id].irq, (uint32_t)hal_uart_irq_handler);
|
||
|
// The priority should be the same as DMA's
|
||
|
NVIC_SetPriority(uart[id].irq, IRQ_PRIORITY_NORMAL);
|
||
|
NVIC_ClearPendingIRQ(uart[id].irq);
|
||
|
NVIC_EnableIRQ(uart[id].irq);
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int hal_uart_reopen(enum HAL_UART_ID_T id, const struct HAL_UART_CFG_T *cfg)
|
||
|
{
|
||
|
uint32_t cr, dmacr;
|
||
|
int i;
|
||
|
|
||
|
ASSERT(id < HAL_UART_ID_QTY, err_invalid_id, id);
|
||
|
|
||
|
if (!init_done) {
|
||
|
init_done = true;
|
||
|
for (i = HAL_UART_ID_0; i < HAL_UART_ID_QTY; i++) {
|
||
|
recv_dma_chan[i] = HAL_DMA_CHAN_NONE;
|
||
|
send_dma_chan[i] = HAL_DMA_CHAN_NONE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
cr = 0;
|
||
|
switch (cfg->flow) {
|
||
|
case HAL_UART_FLOW_CONTROL_NONE:
|
||
|
break;
|
||
|
case HAL_UART_FLOW_CONTROL_RTS:
|
||
|
cr |= UARTCR_RTSEN;
|
||
|
break;
|
||
|
case HAL_UART_FLOW_CONTROL_CTS:
|
||
|
cr |= UARTCR_CTSEN;
|
||
|
break;
|
||
|
case HAL_UART_FLOW_CONTROL_RTSCTS:
|
||
|
cr |= UARTCR_RTSEN
|
||
|
| UARTCR_CTSEN;
|
||
|
break;
|
||
|
default:
|
||
|
ASSERT(false, "Invalid flow control param: %d", cfg->flow);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
dmacr = 0;
|
||
|
if (cfg->dma_rx) {
|
||
|
dmacr |= UARTDMACR_RXDMAE;
|
||
|
}
|
||
|
if (cfg->dma_tx) {
|
||
|
dmacr |= UARTDMACR_TXDMAE;
|
||
|
}
|
||
|
if (cfg->dma_rx_stop_on_err) {
|
||
|
dmacr |= UARTDMACR_DMAONERR;
|
||
|
}
|
||
|
|
||
|
// Configure UART
|
||
|
uart[id].base->UARTDMACR = dmacr;
|
||
|
uart[id].base->UARTCR = (uart[id].base->UARTCR & ~(UARTCR_RTSEN | UARTCR_CTSEN)) | cr;
|
||
|
|
||
|
if (uart[id].irq != INVALID_IRQn) {
|
||
|
NVIC_SetVector(uart[id].irq, (uint32_t)hal_uart_irq_handler);
|
||
|
// The priority should be the same as DMA's
|
||
|
NVIC_SetPriority(uart[id].irq, IRQ_PRIORITY_NORMAL);
|
||
|
NVIC_ClearPendingIRQ(uart[id].irq);
|
||
|
NVIC_EnableIRQ(uart[id].irq);
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int hal_uart_close(enum HAL_UART_ID_T id)
|
||
|
{
|
||
|
uint32_t lock;
|
||
|
|
||
|
ASSERT(id < HAL_UART_ID_QTY, err_invalid_id, id);
|
||
|
|
||
|
if (uart[id].irq != INVALID_IRQn) {
|
||
|
NVIC_DisableIRQ(uart[id].irq);
|
||
|
}
|
||
|
|
||
|
lock = int_lock();
|
||
|
if (recv_dma_chan[id] != HAL_DMA_CHAN_NONE) {
|
||
|
hal_gpdma_cancel(recv_dma_chan[id]);
|
||
|
hal_gpdma_free_chan(recv_dma_chan[id]);
|
||
|
recv_dma_chan[id] = HAL_DMA_CHAN_NONE;
|
||
|
}
|
||
|
if (send_dma_chan[id] != HAL_DMA_CHAN_NONE) {
|
||
|
hal_gpdma_cancel(send_dma_chan[id]);
|
||
|
hal_gpdma_free_chan(send_dma_chan[id]);
|
||
|
send_dma_chan[id] = HAL_DMA_CHAN_NONE;
|
||
|
}
|
||
|
int_unlock(lock);
|
||
|
|
||
|
// Disable UART
|
||
|
uart[id].base->UARTCR &= ~UARTCR_UARTEN;
|
||
|
// Empty FIFO
|
||
|
uart[id].base->UARTLCR_H &= ~UARTLCR_H_FEN;
|
||
|
|
||
|
hal_cmu_reset_set(uart[id].apb);
|
||
|
hal_cmu_reset_set(uart[id].mod);
|
||
|
hal_cmu_clock_disable(uart[id].apb);
|
||
|
hal_cmu_clock_disable(uart[id].mod);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int hal_uart_opened(enum HAL_UART_ID_T id)
|
||
|
{
|
||
|
ASSERT(id < HAL_UART_ID_QTY, err_invalid_id, id);
|
||
|
if (uart[id].apb < HAL_CMU_MOD_QTY &&
|
||
|
hal_cmu_clock_get_status(uart[id].apb) != HAL_CMU_CLK_ENABLED) {
|
||
|
return 0;
|
||
|
}
|
||
|
if (uart[id].base->UARTCR & UARTCR_UARTEN) {
|
||
|
return 1;
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int hal_uart_change_baud_rate(enum HAL_UART_ID_T id, uint32_t rate)
|
||
|
{
|
||
|
union HAL_UART_FLAG_T flag;
|
||
|
|
||
|
ASSERT(id < HAL_UART_ID_QTY, err_invalid_id, id);
|
||
|
|
||
|
if (!hal_uart_opened(id)) {
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
flag.reg = uart[id].base->UARTFR;
|
||
|
if (flag.BUSY) {
|
||
|
return 2;
|
||
|
}
|
||
|
|
||
|
uart[id].base->UARTCR &= ~UARTCR_UARTEN;
|
||
|
|
||
|
set_baud_rate(id, rate);
|
||
|
|
||
|
uart[id].base->UARTLCR_H = uart[id].base->UARTLCR_H;
|
||
|
uart[id].base->UARTCR |= UARTCR_UARTEN;
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int hal_uart_pause(enum HAL_UART_ID_T id)
|
||
|
{
|
||
|
ASSERT(id < HAL_UART_ID_QTY, err_invalid_id, id);
|
||
|
if (hal_uart_opened(id)) {
|
||
|
uart[id].base->UARTCR &= ~(UARTCR_TXE | UARTCR_RXE);
|
||
|
return 0;
|
||
|
}
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
int hal_uart_continue(enum HAL_UART_ID_T id)
|
||
|
{
|
||
|
ASSERT(id < HAL_UART_ID_QTY, err_invalid_id, id);
|
||
|
if (hal_uart_opened(id)) {
|
||
|
uart[id].base->UARTCR |= (UARTCR_TXE | UARTCR_RXE);
|
||
|
return 0;
|
||
|
}
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
int hal_uart_readable(enum HAL_UART_ID_T id)
|
||
|
{
|
||
|
ASSERT(id < HAL_UART_ID_QTY, err_invalid_id, id);
|
||
|
return (uart[id].base->UARTFR & UARTFR_RXFE) == 0;
|
||
|
}
|
||
|
|
||
|
int hal_uart_writable(enum HAL_UART_ID_T id)
|
||
|
{
|
||
|
ASSERT(id < HAL_UART_ID_QTY, err_invalid_id, id);
|
||
|
return (uart[id].base->UARTFR & UARTFR_TXFF) == 0;
|
||
|
}
|
||
|
|
||
|
uint8_t hal_uart_getc(enum HAL_UART_ID_T id)
|
||
|
{
|
||
|
uint32_t c;
|
||
|
ASSERT(id < HAL_UART_ID_QTY, err_invalid_id, id);
|
||
|
ASSERT((uart[id].base->UARTDMACR & UARTDMACR_RXDMAE) == 0, "RX-DMA configured on UART %d", id);
|
||
|
c = uart[id].base->UARTDR;
|
||
|
return (c & 0xFF);
|
||
|
}
|
||
|
|
||
|
int hal_uart_putc(enum HAL_UART_ID_T id, uint8_t c)
|
||
|
{
|
||
|
ASSERT(id < HAL_UART_ID_QTY, err_invalid_id, id);
|
||
|
ASSERT((uart[id].base->UARTDMACR & UARTDMACR_TXDMAE) == 0, "TX-DMA configured on UART %d", id);
|
||
|
uart[id].base->UARTDR = c;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
uint8_t hal_uart_blocked_getc(enum HAL_UART_ID_T id)
|
||
|
{
|
||
|
while (hal_uart_readable(id) == 0);
|
||
|
return hal_uart_getc(id);
|
||
|
}
|
||
|
|
||
|
int hal_uart_blocked_putc(enum HAL_UART_ID_T id, uint8_t c)
|
||
|
{
|
||
|
while (hal_uart_writable(id) == 0);
|
||
|
return hal_uart_putc(id, c);
|
||
|
}
|
||
|
|
||
|
union HAL_UART_FLAG_T hal_uart_get_flag(enum HAL_UART_ID_T id)
|
||
|
{
|
||
|
union HAL_UART_FLAG_T flag;
|
||
|
ASSERT(id < HAL_UART_ID_QTY, err_invalid_id, id);
|
||
|
flag.reg = uart[id].base->UARTFR;
|
||
|
return flag;
|
||
|
}
|
||
|
|
||
|
union HAL_UART_STATUS_T hal_uart_get_status(enum HAL_UART_ID_T id)
|
||
|
{
|
||
|
union HAL_UART_STATUS_T status;
|
||
|
ASSERT(id < HAL_UART_ID_QTY, err_invalid_id, id);
|
||
|
status.reg = uart[id].base->UARTRSR;
|
||
|
return status;
|
||
|
}
|
||
|
|
||
|
void hal_uart_clear_status(enum HAL_UART_ID_T id)
|
||
|
{
|
||
|
ASSERT(id < HAL_UART_ID_QTY, err_invalid_id, id);
|
||
|
uart[id].base->UARTECR = 0;
|
||
|
}
|
||
|
|
||
|
void hal_uart_break_set(enum HAL_UART_ID_T id)
|
||
|
{
|
||
|
ASSERT(id < HAL_UART_ID_QTY, err_invalid_id, id);
|
||
|
uart[id].base->UARTLCR_H |= UARTLCR_H_BRK;
|
||
|
}
|
||
|
|
||
|
void hal_uart_break_clear(enum HAL_UART_ID_T id)
|
||
|
{
|
||
|
ASSERT(id < HAL_UART_ID_QTY, err_invalid_id, id);
|
||
|
uart[id].base->UARTLCR_H &= ~UARTLCR_H_BRK;
|
||
|
}
|
||
|
|
||
|
void hal_uart_flush(enum HAL_UART_ID_T id, uint32_t ticks)
|
||
|
{
|
||
|
ASSERT(id < HAL_UART_ID_QTY, err_invalid_id, id);
|
||
|
|
||
|
if (!hal_uart_opened(id)) {
|
||
|
return;
|
||
|
}
|
||
|
if ((uart[id].base->UARTLCR_H & UARTLCR_H_FEN) == 0) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// Disable the UART
|
||
|
uart[id].base->UARTCR &= ~UARTCR_UARTEN;
|
||
|
// Wait for the end of transmission or reception of the current character
|
||
|
hal_sys_timer_delay((ticks > 0) ? ticks : UART_FLUSH_DELAY_DEFAULT);
|
||
|
|
||
|
// Flush FIFO
|
||
|
uart[id].base->UARTLCR_H &= ~UARTLCR_H_FEN;
|
||
|
uart[id].base->UARTLCR_H |= UARTLCR_H_FEN;
|
||
|
|
||
|
// Enable the UART
|
||
|
uart[id].base->UARTCR |= UARTCR_UARTEN;
|
||
|
}
|
||
|
|
||
|
union HAL_UART_IRQ_T hal_uart_get_raw_irq(enum HAL_UART_ID_T id)
|
||
|
{
|
||
|
union HAL_UART_IRQ_T irq;
|
||
|
|
||
|
irq.reg = uart[id].base->UARTRIS;
|
||
|
|
||
|
return irq;
|
||
|
}
|
||
|
|
||
|
void hal_uart_clear_irq(enum HAL_UART_ID_T id, union HAL_UART_IRQ_T irq)
|
||
|
{
|
||
|
uart[id].base->UARTICR = irq.reg;
|
||
|
}
|
||
|
|
||
|
union HAL_UART_IRQ_T hal_uart_irq_get_mask(enum HAL_UART_ID_T id)
|
||
|
{
|
||
|
union HAL_UART_IRQ_T mask;
|
||
|
|
||
|
mask.reg = uart[id].base->UARTIMSC;
|
||
|
|
||
|
return mask;
|
||
|
}
|
||
|
|
||
|
union HAL_UART_IRQ_T hal_uart_irq_set_mask(enum HAL_UART_ID_T id, union HAL_UART_IRQ_T mask)
|
||
|
{
|
||
|
union HAL_UART_IRQ_T old_mask;
|
||
|
|
||
|
ASSERT(id < HAL_UART_ID_QTY, err_invalid_id, id);
|
||
|
if (mask.RT) {
|
||
|
ASSERT(recv_dma_chan[id] == HAL_DMA_CHAN_NONE, err_recv_dma_api, __FUNCTION__);
|
||
|
}
|
||
|
old_mask.reg = uart[id].base->UARTIMSC;
|
||
|
uart[id].base->UARTIMSC = mask.reg;
|
||
|
|
||
|
return old_mask;
|
||
|
}
|
||
|
|
||
|
HAL_UART_IRQ_HANDLER_T hal_uart_irq_set_handler(enum HAL_UART_ID_T id, HAL_UART_IRQ_HANDLER_T handler)
|
||
|
{
|
||
|
HAL_UART_IRQ_HANDLER_T old_handler;
|
||
|
|
||
|
ASSERT(id < HAL_UART_ID_QTY, err_invalid_id, id);
|
||
|
old_handler = irq_handler[id];
|
||
|
irq_handler[id] = handler;
|
||
|
|
||
|
return old_handler;
|
||
|
}
|
||
|
|
||
|
static void dma_mode_uart_irq_handler(enum HAL_UART_ID_T id, union HAL_UART_IRQ_T status)
|
||
|
{
|
||
|
uint32_t xfer = 0;
|
||
|
uint32_t lock;
|
||
|
|
||
|
if (status.RT || status.FE || status.OE || status.PE || status.BE) {
|
||
|
if (recv_dma_mode[id] == UART_RX_DMA_MODE_NORMAL) {
|
||
|
// Restore the traditional RT behaviour
|
||
|
lock = int_lock();
|
||
|
uart[id].base->UARTLCR_H &= ~UARTLCR_H_DMA_RT_EN;
|
||
|
int_unlock(lock);
|
||
|
}
|
||
|
|
||
|
if (rxdma_handler[id]) {
|
||
|
if (recv_dma_chan[id] != HAL_DMA_CHAN_NONE) {
|
||
|
if (recv_dma_mode[id] == UART_RX_DMA_MODE_NORMAL) {
|
||
|
xfer = hal_uart_stop_dma_recv(id);
|
||
|
if (recv_dma_size[id] > xfer) {
|
||
|
xfer = recv_dma_size[id] - xfer;
|
||
|
} else {
|
||
|
xfer = 0;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
rxdma_handler[id](xfer, 0, status);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void hal_uart_irq_set_dma_handler(enum HAL_UART_ID_T id, HAL_UART_IRQ_RXDMA_HANDLER_T rxdma, HAL_UART_IRQ_TXDMA_HANDLER_T txdma)
|
||
|
{
|
||
|
ASSERT(id < HAL_UART_ID_QTY, err_invalid_id, id);
|
||
|
rxdma_handler[id] = rxdma;
|
||
|
txdma_handler[id] = txdma;
|
||
|
irq_handler[id] = dma_mode_uart_irq_handler;
|
||
|
}
|
||
|
|
||
|
static void recv_dma_irq_handler(uint8_t chan, uint32_t remain_tsize, uint32_t error, struct HAL_DMA_DESC_T *lli)
|
||
|
{
|
||
|
enum HAL_UART_ID_T id;
|
||
|
uint32_t xfer;
|
||
|
uint32_t lock;
|
||
|
union HAL_UART_IRQ_T status;
|
||
|
|
||
|
lock = int_lock();
|
||
|
for (id = HAL_UART_ID_0; id < HAL_UART_ID_QTY; id++) {
|
||
|
if (recv_dma_chan[id] == chan) {
|
||
|
if (recv_dma_mode[id] == UART_RX_DMA_MODE_NORMAL) {
|
||
|
recv_dma_chan[id] = HAL_DMA_CHAN_NONE;
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
int_unlock(lock);
|
||
|
|
||
|
if (id == HAL_UART_ID_QTY) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (recv_dma_mode[id] == UART_RX_DMA_MODE_NORMAL) {
|
||
|
// Get remain xfer size
|
||
|
xfer = hal_gpdma_get_sg_remain_size(chan);
|
||
|
|
||
|
hal_gpdma_free_chan(chan);
|
||
|
} else {
|
||
|
xfer = 0;
|
||
|
status.reg = 0;
|
||
|
}
|
||
|
|
||
|
if (rxdma_handler[id]) {
|
||
|
if (recv_dma_mode[id] == UART_RX_DMA_MODE_NORMAL) {
|
||
|
// Already get xfer size
|
||
|
if (recv_dma_size[id] > xfer) {
|
||
|
xfer = recv_dma_size[id] - xfer;
|
||
|
} else {
|
||
|
xfer = 0;
|
||
|
}
|
||
|
} else if (recv_dma_mode[id] == UART_RX_DMA_MODE_PINGPANG) {
|
||
|
xfer = recv_dma_size[id] / 2;
|
||
|
}
|
||
|
rxdma_handler[id](xfer, error, status);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void recv_dma_start_callback(uint8_t chan)
|
||
|
{
|
||
|
enum HAL_UART_ID_T id;
|
||
|
|
||
|
for (id = HAL_UART_ID_0; id < HAL_UART_ID_QTY; id++) {
|
||
|
if (recv_dma_chan[id] == chan) {
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (id == HAL_UART_ID_QTY) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
uart[id].base->UARTIMSC = recv_mask[id].reg;
|
||
|
}
|
||
|
|
||
|
static int fill_buf_list_dma_desc(struct HAL_DMA_DESC_T *desc, uint32_t cnt, struct HAL_DMA_CH_CFG_T *cfg,
|
||
|
const struct HAL_UART_BUF_T *ubuf, uint32_t ucnt, uint32_t step)
|
||
|
{
|
||
|
enum HAL_DMA_RET_T ret;
|
||
|
struct HAL_DMA_DESC_T *last_desc;
|
||
|
int tc_irq;
|
||
|
int i;
|
||
|
int u;
|
||
|
uint32_t remain;
|
||
|
uint32_t dlen;
|
||
|
|
||
|
last_desc = NULL;
|
||
|
|
||
|
u = 0;
|
||
|
remain = ubuf[0].len;
|
||
|
|
||
|
for (i = 0, u = 0; i < cnt - 1; i++) {
|
||
|
if (ubuf[u].loop_hdr && last_desc == NULL) {
|
||
|
last_desc = &desc[i];
|
||
|
}
|
||
|
if (remain <= step) {
|
||
|
dlen = remain;
|
||
|
tc_irq = ubuf[u].irq;
|
||
|
} else {
|
||
|
dlen = step;
|
||
|
tc_irq = 0;
|
||
|
}
|
||
|
cfg->src_tsize = dlen;
|
||
|
ret = hal_gpdma_init_desc(&desc[i], cfg, &desc[i + 1], tc_irq);
|
||
|
if (ret != HAL_DMA_OK) {
|
||
|
return 1;
|
||
|
}
|
||
|
remain -= dlen;
|
||
|
if (remain) {
|
||
|
cfg->dst += dlen;
|
||
|
} else {
|
||
|
u++;
|
||
|
if (u >= ucnt) {
|
||
|
return 2;
|
||
|
}
|
||
|
cfg->dst = (uint32_t)ubuf[u].buf;
|
||
|
remain = ubuf[u].len;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
cfg->src_tsize = remain;
|
||
|
ret = hal_gpdma_init_desc(&desc[i], cfg, last_desc, ubuf[u].irq);
|
||
|
if (ret != HAL_DMA_OK) {
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int fill_dma_desc(struct HAL_DMA_DESC_T *desc, uint32_t cnt, struct HAL_DMA_CH_CFG_T *cfg,
|
||
|
uint32_t len, enum UART_RX_DMA_MODE_T mode, uint32_t step)
|
||
|
{
|
||
|
enum HAL_DMA_RET_T ret;
|
||
|
struct HAL_DMA_DESC_T *last_desc;
|
||
|
int tc_irq;
|
||
|
int i;
|
||
|
|
||
|
for (i = 0; i < cnt - 1; i++) {
|
||
|
cfg->src_tsize = step;
|
||
|
tc_irq = 0;
|
||
|
if (mode == UART_RX_DMA_MODE_PINGPANG) {
|
||
|
tc_irq = (i == cnt / 2 - 1) ? 1 : 0;
|
||
|
} else if (mode == UART_RX_DMA_MODE_STREAM) {
|
||
|
tc_irq = 1;
|
||
|
}
|
||
|
ret = hal_gpdma_init_desc(&desc[i], cfg, &desc[i + 1], tc_irq);
|
||
|
if (ret != HAL_DMA_OK) {
|
||
|
return 1;
|
||
|
}
|
||
|
cfg->dst += step;
|
||
|
}
|
||
|
|
||
|
cfg->src_tsize = len - (step * i);
|
||
|
last_desc = NULL;
|
||
|
if (mode == UART_RX_DMA_MODE_PINGPANG || mode == UART_RX_DMA_MODE_STREAM) {
|
||
|
last_desc = &desc[0];
|
||
|
}
|
||
|
ret = hal_gpdma_init_desc(&desc[i], cfg, last_desc, 1);
|
||
|
if (ret != HAL_DMA_OK) {
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int start_recv_dma_with_mask(enum HAL_UART_ID_T id, const struct HAL_UART_BUF_T *ubuf, uint32_t ucnt,
|
||
|
struct HAL_DMA_DESC_T *desc, uint32_t *desc_cnt,
|
||
|
const union HAL_UART_IRQ_T *mask,
|
||
|
enum UART_RX_DMA_MODE_T mode, uint32_t step)
|
||
|
{
|
||
|
uint8_t *buf;
|
||
|
uint32_t len;
|
||
|
struct HAL_DMA_CH_CFG_T dma_cfg;
|
||
|
enum HAL_DMA_RET_T ret;
|
||
|
uint32_t lock;
|
||
|
uint32_t cnt;
|
||
|
uint32_t i;
|
||
|
enum HAL_DMA_PERIPH_T periph;
|
||
|
|
||
|
ASSERT(id < HAL_UART_ID_QTY, err_invalid_id, id);
|
||
|
ASSERT(uart[id].irq != INVALID_IRQn, "DMA not supported on UART %d", id);
|
||
|
ASSERT((uart[id].base->UARTDMACR & UARTDMACR_RXDMAE), "DMA not configured on UART %d", id);
|
||
|
|
||
|
if (ucnt == 0) {
|
||
|
return -21;
|
||
|
}
|
||
|
if (step > HAL_UART_DMA_TRANSFER_STEP || step == 0) {
|
||
|
return -22;
|
||
|
}
|
||
|
|
||
|
buf = ubuf[0].buf;
|
||
|
len = ubuf[0].len;
|
||
|
|
||
|
if (buf == NULL) {
|
||
|
return -23;
|
||
|
}
|
||
|
if (len == 0) {
|
||
|
return -24;
|
||
|
}
|
||
|
|
||
|
if (0) {
|
||
|
} else if (mode == UART_RX_DMA_MODE_NORMAL) {
|
||
|
cnt = (len + step - 1) / step;
|
||
|
} else if (mode == UART_RX_DMA_MODE_PINGPANG) {
|
||
|
cnt = ((len / 2 + step - 1) / step) * 2;
|
||
|
step = len / cnt;
|
||
|
if (len % cnt != 0) {
|
||
|
return -11;
|
||
|
}
|
||
|
if (step == 0) {
|
||
|
return -12;
|
||
|
}
|
||
|
} else if (mode == UART_RX_DMA_MODE_STREAM) {
|
||
|
cnt = (len + step - 1) / step;
|
||
|
if (cnt == 1) {
|
||
|
// cnt should >= 2
|
||
|
cnt++;
|
||
|
}
|
||
|
step = (len + cnt - 1) / cnt;
|
||
|
if (step == 0) {
|
||
|
return -13;
|
||
|
}
|
||
|
} else if (mode == UART_RX_DMA_MODE_BUF_LIST) {
|
||
|
cnt = 0;
|
||
|
for (i = 0; i < ucnt; i++) {
|
||
|
if (ubuf->buf == NULL) {
|
||
|
return -14;
|
||
|
}
|
||
|
if (ubuf->len == 0) {
|
||
|
return -15;
|
||
|
}
|
||
|
cnt += (ubuf->len + step - 1) / step;
|
||
|
}
|
||
|
} else {
|
||
|
return -10;
|
||
|
}
|
||
|
|
||
|
// Return the required DMA descriptor count
|
||
|
if (desc == NULL && desc_cnt) {
|
||
|
*desc_cnt = (cnt == 1) ? 0 : cnt;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
if (cnt > 1) {
|
||
|
if (desc == NULL || desc_cnt == NULL) {
|
||
|
return -1;
|
||
|
}
|
||
|
if (*desc_cnt < cnt) {
|
||
|
return -2;
|
||
|
}
|
||
|
}
|
||
|
if (desc_cnt) {
|
||
|
*desc_cnt = (cnt == 1) ? 0 : cnt;
|
||
|
}
|
||
|
|
||
|
periph = uart[id].rx_periph;
|
||
|
|
||
|
lock = int_lock();
|
||
|
if (recv_dma_chan[id] != HAL_DMA_CHAN_NONE) {
|
||
|
int_unlock(lock);
|
||
|
return 1;
|
||
|
}
|
||
|
recv_dma_chan[id] = hal_gpdma_get_chan(periph, HAL_DMA_HIGH_PRIO);
|
||
|
if (recv_dma_chan[id] == HAL_DMA_CHAN_NONE) {
|
||
|
int_unlock(lock);
|
||
|
return 2;
|
||
|
}
|
||
|
int_unlock(lock);
|
||
|
|
||
|
recv_dma_mode[id] = mode;
|
||
|
recv_dma_size[id] = len;
|
||
|
|
||
|
memset(&dma_cfg, 0, sizeof(dma_cfg));
|
||
|
dma_cfg.ch = recv_dma_chan[id];
|
||
|
dma_cfg.dst = (uint32_t)buf;
|
||
|
dma_cfg.dst_bsize = HAL_DMA_BSIZE_32;
|
||
|
dma_cfg.dst_periph = 0; // useless
|
||
|
dma_cfg.dst_width = HAL_DMA_WIDTH_BYTE;
|
||
|
dma_cfg.handler = recv_dma_irq_handler;
|
||
|
dma_cfg.src = 0; // useless
|
||
|
dma_cfg.src_bsize = HAL_DMA_BSIZE_8;
|
||
|
dma_cfg.src_periph = periph;
|
||
|
dma_cfg.src_tsize = len;
|
||
|
dma_cfg.src_width = HAL_DMA_WIDTH_BYTE;
|
||
|
dma_cfg.type = HAL_DMA_FLOW_P2M_DMA;
|
||
|
dma_cfg.try_burst = 0;
|
||
|
|
||
|
if (mask) {
|
||
|
recv_mask[id] = *mask;
|
||
|
dma_cfg.start_cb = recv_dma_start_callback;
|
||
|
} else {
|
||
|
union HAL_UART_IRQ_T irq_mask;
|
||
|
|
||
|
irq_mask.reg = uart[id].base->UARTIMSC;
|
||
|
ASSERT(irq_mask.RT == 0, err_recv_dma_api, __FUNCTION__);
|
||
|
}
|
||
|
|
||
|
// Activate DMA RT behaviour
|
||
|
lock = int_lock();
|
||
|
uart[id].base->UARTLCR_H |= UARTLCR_H_DMA_RT_EN;
|
||
|
int_unlock(lock);
|
||
|
|
||
|
if (cnt == 1) {
|
||
|
ret = hal_gpdma_start(&dma_cfg);
|
||
|
} else {
|
||
|
if (mode == UART_RX_DMA_MODE_BUF_LIST) {
|
||
|
ret = fill_buf_list_dma_desc(desc, cnt, &dma_cfg, ubuf, ucnt, step);
|
||
|
} else {
|
||
|
ret = fill_dma_desc(desc, cnt, &dma_cfg, len, mode, step);
|
||
|
}
|
||
|
if (ret) {
|
||
|
goto _err_exit;
|
||
|
}
|
||
|
ret = hal_gpdma_sg_start(desc, &dma_cfg);
|
||
|
}
|
||
|
|
||
|
if (ret != HAL_DMA_OK) {
|
||
|
_err_exit:
|
||
|
// Restore the traditional RT behaviour
|
||
|
lock = int_lock();
|
||
|
uart[id].base->UARTLCR_H &= ~UARTLCR_H_DMA_RT_EN;
|
||
|
int_unlock(lock);
|
||
|
hal_gpdma_free_chan(recv_dma_chan[id]);
|
||
|
recv_dma_chan[id] = HAL_DMA_CHAN_NONE;
|
||
|
return 3;
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
// Safe API to trigger receive timeout IRQ and DMA IRQ
|
||
|
int hal_uart_dma_recv_mask(enum HAL_UART_ID_T id, uint8_t *buf, uint32_t len,
|
||
|
struct HAL_DMA_DESC_T *desc, uint32_t *desc_cnt,
|
||
|
const union HAL_UART_IRQ_T *mask)
|
||
|
{
|
||
|
struct HAL_UART_BUF_T uart_buf;
|
||
|
|
||
|
uart_buf.buf = buf;
|
||
|
uart_buf.len = len;
|
||
|
uart_buf.irq = false;
|
||
|
uart_buf.loop_hdr = false;
|
||
|
return start_recv_dma_with_mask(id, &uart_buf, 1, desc, desc_cnt, mask, UART_RX_DMA_MODE_NORMAL, HAL_UART_DMA_TRANSFER_STEP);
|
||
|
}
|
||
|
|
||
|
// Safe API to trigger receive timeout IRQ and DMA IRQ
|
||
|
int hal_uart_dma_recv_mask_pingpang(enum HAL_UART_ID_T id, uint8_t *buf, uint32_t len,
|
||
|
struct HAL_DMA_DESC_T *desc, uint32_t *desc_cnt,
|
||
|
const union HAL_UART_IRQ_T *mask, uint32_t step)
|
||
|
{
|
||
|
struct HAL_UART_BUF_T uart_buf;
|
||
|
|
||
|
uart_buf.buf = buf;
|
||
|
uart_buf.len = len;
|
||
|
uart_buf.irq = false;
|
||
|
uart_buf.loop_hdr = false;
|
||
|
return start_recv_dma_with_mask(id, &uart_buf, 1, desc, desc_cnt, mask, UART_RX_DMA_MODE_PINGPANG, step);
|
||
|
}
|
||
|
|
||
|
// Safe API to trigger receive timeout IRQ and DMA IRQ
|
||
|
int hal_uart_dma_recv_mask_stream(enum HAL_UART_ID_T id, uint8_t *buf, uint32_t len,
|
||
|
struct HAL_DMA_DESC_T *desc, uint32_t *desc_cnt,
|
||
|
const union HAL_UART_IRQ_T *mask, uint32_t step)
|
||
|
{
|
||
|
struct HAL_UART_BUF_T uart_buf;
|
||
|
|
||
|
uart_buf.buf = buf;
|
||
|
uart_buf.len = len;
|
||
|
uart_buf.irq = false;
|
||
|
uart_buf.loop_hdr = false;
|
||
|
return start_recv_dma_with_mask(id, &uart_buf, 1, desc, desc_cnt, mask, UART_RX_DMA_MODE_STREAM, step);
|
||
|
}
|
||
|
|
||
|
// Safe API to trigger receive timeout IRQ and DMA IRQ
|
||
|
int hal_uart_dma_recv_mask_buf_list(enum HAL_UART_ID_T id, const struct HAL_UART_BUF_T *ubuf, uint32_t ucnt,
|
||
|
struct HAL_DMA_DESC_T *desc, uint32_t *desc_cnt, const union HAL_UART_IRQ_T *mask)
|
||
|
{
|
||
|
return start_recv_dma_with_mask(id, ubuf, ucnt, desc, desc_cnt, mask, UART_RX_DMA_MODE_BUF_LIST, HAL_UART_DMA_TRANSFER_STEP);
|
||
|
}
|
||
|
|
||
|
int hal_uart_dma_recv(enum HAL_UART_ID_T id, uint8_t *buf, uint32_t len,
|
||
|
struct HAL_DMA_DESC_T *desc, uint32_t *desc_cnt)
|
||
|
{
|
||
|
struct HAL_UART_BUF_T uart_buf;
|
||
|
|
||
|
uart_buf.buf = buf;
|
||
|
uart_buf.len = len;
|
||
|
uart_buf.irq = false;
|
||
|
uart_buf.loop_hdr = false;
|
||
|
return start_recv_dma_with_mask(id, &uart_buf, 1, desc, desc_cnt, NULL, UART_RX_DMA_MODE_NORMAL, HAL_UART_DMA_TRANSFER_STEP);
|
||
|
}
|
||
|
|
||
|
int hal_uart_dma_recv_pingpang(enum HAL_UART_ID_T id, uint8_t *buf, uint32_t len,
|
||
|
struct HAL_DMA_DESC_T *desc, uint32_t *desc_cnt)
|
||
|
{
|
||
|
struct HAL_UART_BUF_T uart_buf;
|
||
|
|
||
|
uart_buf.buf = buf;
|
||
|
uart_buf.len = len;
|
||
|
uart_buf.irq = false;
|
||
|
uart_buf.loop_hdr = false;
|
||
|
return start_recv_dma_with_mask(id, &uart_buf, 1, desc, desc_cnt, NULL, UART_RX_DMA_MODE_PINGPANG, HAL_UART_DMA_TRANSFER_STEP_PINGPANG);
|
||
|
}
|
||
|
|
||
|
uint32_t hal_uart_get_dma_recv_addr(enum HAL_UART_ID_T id)
|
||
|
{
|
||
|
uint32_t lock;
|
||
|
uint32_t addr;
|
||
|
int i;
|
||
|
|
||
|
ASSERT(id < HAL_UART_ID_QTY, err_invalid_id, id);
|
||
|
|
||
|
addr = 0;
|
||
|
|
||
|
lock = int_lock();
|
||
|
if (recv_dma_chan[id] != HAL_DMA_CHAN_NONE) {
|
||
|
for (i = 0; i < 2; i++) {
|
||
|
addr = hal_dma_get_cur_dst_addr(recv_dma_chan[id]);
|
||
|
if (addr) {
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
int_unlock(lock);
|
||
|
|
||
|
return addr;
|
||
|
}
|
||
|
|
||
|
uint32_t hal_uart_stop_dma_recv(enum HAL_UART_ID_T id)
|
||
|
{
|
||
|
uint32_t remains;
|
||
|
uint32_t lock;
|
||
|
uint8_t chan;
|
||
|
|
||
|
ASSERT(id < HAL_UART_ID_QTY, err_invalid_id, id);
|
||
|
|
||
|
lock = int_lock();
|
||
|
chan = recv_dma_chan[id];
|
||
|
recv_dma_chan[id] = HAL_DMA_CHAN_NONE;
|
||
|
// Restore the traditional RT behaviour
|
||
|
uart[id].base->UARTLCR_H &= ~UARTLCR_H_DMA_RT_EN;
|
||
|
int_unlock(lock);
|
||
|
|
||
|
if (chan == HAL_DMA_CHAN_NONE) {
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
// Save the data in DMA FIFO
|
||
|
hal_gpdma_stop(chan);
|
||
|
remains = hal_gpdma_get_sg_remain_size(chan);
|
||
|
hal_gpdma_free_chan(chan);
|
||
|
|
||
|
return remains;
|
||
|
}
|
||
|
|
||
|
static void send_dma_irq_handler(uint8_t chan, uint32_t remain_tsize, uint32_t error, struct HAL_DMA_DESC_T *lli)
|
||
|
{
|
||
|
enum HAL_UART_ID_T id;
|
||
|
uint32_t xfer;
|
||
|
uint32_t lock;
|
||
|
|
||
|
lock = int_lock();
|
||
|
for (id = HAL_UART_ID_0; id < HAL_UART_ID_QTY; id++) {
|
||
|
if (send_dma_chan[id] == chan) {
|
||
|
send_dma_chan[id] = HAL_DMA_CHAN_NONE;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
int_unlock(lock);
|
||
|
|
||
|
if (id == HAL_UART_ID_QTY) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// Get remain xfer size
|
||
|
xfer = hal_gpdma_get_sg_remain_size(chan);
|
||
|
|
||
|
hal_gpdma_free_chan(chan);
|
||
|
|
||
|
if (txdma_handler[id]) {
|
||
|
// Get already xfer size
|
||
|
if (send_dma_size[id] > xfer) {
|
||
|
xfer = send_dma_size[id] - xfer;
|
||
|
} else {
|
||
|
xfer = 0;
|
||
|
}
|
||
|
txdma_handler[id](xfer, error);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
int hal_uart_dma_send(enum HAL_UART_ID_T id, const uint8_t *buf, uint32_t len,
|
||
|
struct HAL_DMA_DESC_T *desc, uint32_t *desc_cnt)
|
||
|
{
|
||
|
struct HAL_DMA_CH_CFG_T dma_cfg;
|
||
|
enum HAL_DMA_RET_T ret;
|
||
|
uint32_t lock;
|
||
|
uint32_t cnt;
|
||
|
uint32_t i;
|
||
|
enum HAL_DMA_PERIPH_T periph;
|
||
|
|
||
|
ASSERT(id < HAL_UART_ID_QTY, err_invalid_id, id);
|
||
|
ASSERT(uart[id].irq != INVALID_IRQn, "DMA not supported on UART %d", id);
|
||
|
ASSERT((uart[id].base->UARTDMACR & UARTDMACR_TXDMAE), "DMA not configured on UART %d", id);
|
||
|
|
||
|
cnt = (len + HAL_UART_DMA_TRANSFER_STEP - 1) / HAL_UART_DMA_TRANSFER_STEP;
|
||
|
|
||
|
// Return the required DMA descriptor count
|
||
|
if (desc == NULL && desc_cnt) {
|
||
|
*desc_cnt = (cnt == 1) ? 0 : cnt;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
if (cnt == 0) {
|
||
|
return 0;
|
||
|
}
|
||
|
if (cnt > 1) {
|
||
|
if (desc == NULL || desc_cnt == NULL) {
|
||
|
return -1;
|
||
|
}
|
||
|
if (*desc_cnt < cnt) {
|
||
|
return -2;
|
||
|
}
|
||
|
}
|
||
|
if (desc_cnt) {
|
||
|
*desc_cnt = (cnt == 1) ? 0 : cnt;
|
||
|
}
|
||
|
|
||
|
periph = uart[id].tx_periph;
|
||
|
|
||
|
lock = int_lock();
|
||
|
if (send_dma_chan[id] != HAL_DMA_CHAN_NONE) {
|
||
|
int_unlock(lock);
|
||
|
return 1;
|
||
|
}
|
||
|
send_dma_chan[id] = hal_gpdma_get_chan(periph, HAL_DMA_HIGH_PRIO);
|
||
|
if (send_dma_chan[id] == HAL_DMA_CHAN_NONE) {
|
||
|
int_unlock(lock);
|
||
|
return 2;
|
||
|
}
|
||
|
int_unlock(lock);
|
||
|
|
||
|
send_dma_size[id] = len;
|
||
|
|
||
|
memset(&dma_cfg, 0, sizeof(dma_cfg));
|
||
|
dma_cfg.ch = send_dma_chan[id];
|
||
|
dma_cfg.dst = 0; // useless
|
||
|
dma_cfg.dst_bsize = HAL_DMA_BSIZE_8;
|
||
|
dma_cfg.dst_periph = periph;
|
||
|
dma_cfg.dst_width = HAL_DMA_WIDTH_BYTE;
|
||
|
dma_cfg.handler = send_dma_irq_handler;
|
||
|
dma_cfg.src = (uint32_t)buf;
|
||
|
dma_cfg.src_bsize = HAL_DMA_BSIZE_32;
|
||
|
dma_cfg.src_periph = 0; // useless
|
||
|
dma_cfg.src_tsize = len;
|
||
|
dma_cfg.src_width = HAL_DMA_WIDTH_BYTE;
|
||
|
dma_cfg.type = HAL_DMA_FLOW_M2P_DMA;
|
||
|
dma_cfg.try_burst = 0;
|
||
|
|
||
|
if (cnt == 1) {
|
||
|
ret = hal_gpdma_start(&dma_cfg);
|
||
|
} else {
|
||
|
for (i = 0; i < cnt - 1; i++) {
|
||
|
dma_cfg.src_tsize = HAL_UART_DMA_TRANSFER_STEP;
|
||
|
ret = hal_gpdma_init_desc(&desc[i], &dma_cfg, &desc[i + 1], 0);
|
||
|
if (ret != HAL_DMA_OK) {
|
||
|
goto _err_exit;
|
||
|
}
|
||
|
dma_cfg.src += HAL_UART_DMA_TRANSFER_STEP;
|
||
|
}
|
||
|
dma_cfg.src_tsize = len - (HAL_UART_DMA_TRANSFER_STEP * i);
|
||
|
ret = hal_gpdma_init_desc(&desc[i], &dma_cfg, NULL, 1);
|
||
|
if (ret != HAL_DMA_OK) {
|
||
|
goto _err_exit;
|
||
|
}
|
||
|
ret = hal_gpdma_sg_start(desc, &dma_cfg);
|
||
|
}
|
||
|
|
||
|
if (ret != HAL_DMA_OK) {
|
||
|
_err_exit:
|
||
|
hal_gpdma_free_chan(send_dma_chan[id]);
|
||
|
send_dma_chan[id] = HAL_DMA_CHAN_NONE;
|
||
|
return 3;
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
uint32_t hal_uart_stop_dma_send(enum HAL_UART_ID_T id)
|
||
|
{
|
||
|
uint32_t remains;
|
||
|
uint32_t lock;
|
||
|
uint8_t chan;
|
||
|
|
||
|
ASSERT(id < HAL_UART_ID_QTY, err_invalid_id, id);
|
||
|
|
||
|
lock = int_lock();
|
||
|
chan = send_dma_chan[id];
|
||
|
send_dma_chan[id] = HAL_DMA_CHAN_NONE;
|
||
|
int_unlock(lock);
|
||
|
|
||
|
if (chan == HAL_DMA_CHAN_NONE) {
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
// Not to keep the data in DMA FIFO
|
||
|
hal_gpdma_cancel(chan);
|
||
|
remains = hal_gpdma_get_sg_remain_size(chan);
|
||
|
hal_gpdma_free_chan(chan);
|
||
|
|
||
|
return remains;
|
||
|
}
|
||
|
|
||
|
static void hal_uart_irq_handler(void)
|
||
|
{
|
||
|
enum HAL_UART_ID_T id;
|
||
|
union HAL_UART_IRQ_T state;
|
||
|
|
||
|
for (id = HAL_UART_ID_0; id < HAL_UART_ID_QTY; id++) {
|
||
|
state.reg = uart[id].base->UARTMIS;
|
||
|
|
||
|
if (state.reg) {
|
||
|
uart[id].base->UARTICR = state.reg;
|
||
|
|
||
|
if (irq_handler[id] != NULL) {
|
||
|
irq_handler[id](id, state);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// ========================================================================
|
||
|
// Test function
|
||
|
|
||
|
#include "stdarg.h"
|
||
|
#include "stdio.h"
|
||
|
|
||
|
#if !defined(DEBUG_PORT) || (DEBUG_PORT == 1)
|
||
|
#define UART_PRINTF_ID HAL_UART_ID_0
|
||
|
#else
|
||
|
#define UART_PRINTF_ID HAL_UART_ID_1
|
||
|
#endif
|
||
|
|
||
|
#ifndef TRACE_BAUD_RATE
|
||
|
#define TRACE_BAUD_RATE (921600)
|
||
|
#endif
|
||
|
|
||
|
int hal_uart_printf_init(void)
|
||
|
{
|
||
|
static const struct HAL_UART_CFG_T uart_cfg = {
|
||
|
.parity = HAL_UART_PARITY_NONE,
|
||
|
.stop = HAL_UART_STOP_BITS_1,
|
||
|
.data = HAL_UART_DATA_BITS_8,
|
||
|
.flow = HAL_UART_FLOW_CONTROL_NONE,//HAL_UART_FLOW_CONTROL_RTSCTS,
|
||
|
.tx_level = HAL_UART_FIFO_LEVEL_1_2,
|
||
|
.rx_level = HAL_UART_FIFO_LEVEL_1_4,
|
||
|
.baud = TRACE_BAUD_RATE,
|
||
|
.dma_rx = false,
|
||
|
.dma_tx = false,
|
||
|
.dma_rx_stop_on_err = false,
|
||
|
};
|
||
|
|
||
|
if (UART_PRINTF_ID == HAL_UART_ID_0) {
|
||
|
hal_iomux_set_uart0();
|
||
|
} else {
|
||
|
hal_iomux_set_uart1();
|
||
|
}
|
||
|
|
||
|
return hal_uart_open(UART_PRINTF_ID, &uart_cfg);
|
||
|
}
|
||
|
|
||
|
void hal_uart_printf(const char *fmt, ...)
|
||
|
{
|
||
|
char buf[200];
|
||
|
int ret;
|
||
|
int i;
|
||
|
va_list ap;
|
||
|
|
||
|
va_start(ap, fmt);
|
||
|
ret = vsnprintf(buf, sizeof(buf), fmt, ap);
|
||
|
#ifdef TRACE_CRLF
|
||
|
if (ret + 2 < sizeof(buf)) {
|
||
|
buf[ret++] = '\r';
|
||
|
}
|
||
|
#endif
|
||
|
if (ret + 1 < sizeof(buf)) {
|
||
|
buf[ret++] = '\n';
|
||
|
}
|
||
|
//buf[ret] = 0;
|
||
|
va_end(ap);
|
||
|
|
||
|
if (ret > 0) {
|
||
|
for (i = 0; i < ret; i++) {
|
||
|
hal_uart_blocked_putc(UART_PRINTF_ID, buf[i]);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#endif // CHIP_HAS_UART
|