IndustrialDeviceController/Software/MT3620_IDC_RTApp/lib/UART.c (356 lines of code) (raw):

/* Copyright (c) Codethink Ltd. All rights reserved. Licensed under the MIT License. */ #include "UART.h" #include "mt3620/uart.h" #include "mt3620/dma.h" #include "NVIC.h" #include <stddef.h> #include <stdbool.h> // Disable DMA as it doesn't currently work #undef UART_ALLOW_DMA // Configure these variables as needed. #define TX_BUFFER_SIZE 256 #define RX_BUFFER_SIZE 32 #if (TX_BUFFER_SIZE > 65536) #error "TX buffer size must be less than or equal 65536" #endif #if (RX_BUFFER_SIZE > 65536) #error "RX buffer size must be less than or equal 65536" #endif #if (TX_BUFFER_SIZE & (TX_BUFFER_SIZE - 1)) != 0 #error "TX buffer size must be a power of two" #endif #if (RX_BUFFER_SIZE & (RX_BUFFER_SIZE - 1)) != 0 #error "RX buffer size must be a power of two" #endif // TODO: Reduce sysram usage by providing a more limited set of buffers? static __attribute__((section(".sysram"))) uint8_t UART_BuffRX[MT3620_UART_COUNT][RX_BUFFER_SIZE] = { 0 }; static __attribute__((section(".sysram"))) uint8_t UART_BuffTX[MT3620_UART_COUNT][TX_BUFFER_SIZE] = { 0 }; struct UART { bool open; unsigned id; bool dma; uint32_t txRemain, txRead, txWrite; uint32_t rxRemain, rxRead, rxWrite; void (*rxCallback)(void); }; static UART context[MT3620_UART_COUNT] = {0}; static inline unsigned UART_UnitToID(Platform_Unit unit) { if ((unit < MT3620_UNIT_UART_DEBUG) || (unit > MT3620_UNIT_ISU5)) { return MT3620_UART_COUNT; } return (unit - MT3620_UNIT_UART_DEBUG); } UART *UART_Open(Platform_Unit unit, unsigned baud, UART_Parity parity, unsigned stopBits, void (*rxCallback)(void)) { unsigned id = UART_UnitToID(unit); if (id >= MT3620_UART_COUNT) { return NULL; } if (context[id].open) { return NULL; } if (baud > MT3620_UART_MAX_SPEED) { return NULL; } if (parity > UART_PARITY_STICK_ONE) { return NULL; } if ((stopBits < 1) || (stopBits > 2)) { return NULL; } unsigned divs = ((MT3620_UART_CLOCK * 10) + (baud / 2)) / baud; unsigned dl = (divs + 2559) / 2560; divs /= dl; unsigned fract = mt3620_uart_fract_lut[divs % 10]; unsigned count = (divs / 10); // LCR (enable DLL, DLM) mt3620_uart_lcr_t lcr = { .mask = mt3620_uart[id]->lcr }; lcr.wls = 3; lcr.stb = true; lcr.pen = true; lcr.eps = true; lcr.sp = true; lcr.sb = false; lcr.dlab = true; mt3620_uart[id]->lcr = lcr.mask; // EFR (enable enhancement features) mt3620_uart_efr_t efr = { .mask = mt3620_uart[id]->efr }; efr.sw_flow_cont = 0; efr.enable_e = true; efr.auto_rts = 0; efr.auto_cts = 0; mt3620_uart[id]->efr = efr.mask; MT3620_UART_FIELD_WRITE(id, highspeed, speed, 3); MT3620_UART_FIELD_WRITE(id, dlm, dlm, (dl >> 8)); // Divisor Latch (MS) MT3620_UART_FIELD_WRITE(id, dll, dll, (dl & 0xFF)); // Divisor Latch (LS) MT3620_UART_FIELD_WRITE(id, sample_count, sample_count, (count - 1)); MT3620_UART_FIELD_WRITE(id, sample_point, sample_point, ((count / 2) - 2)); MT3620_UART_FIELD_WRITE(id, fracdiv_m, fracdiv_m, (fract >> 8)); MT3620_UART_FIELD_WRITE(id, fracdiv_l, fracdiv_l, (fract & 0xFF)); // LCR (8-bit word length) lcr.wls = 3; lcr.stb = (stopBits > 1); lcr.pen = (parity != UART_PARITY_NONE); lcr.eps = (parity & 1); lcr.sp = (parity >= UART_PARITY_STICK_ONE); lcr.sb = false; lcr.dlab = false; mt3620_uart[id]->lcr = lcr.mask; // FCR is write-only so we don't read an initial value. mt3620_uart_fcr_t fcr = { .mask = 0x00000000 }; fcr.rftl = 2; // 12 element RX FIFO trigger fcr.tftl = 1; // 4 element TX FIFO trigger fcr.clrt = true; // Clear Transmit FIFO fcr.clrr = true; // Clear Receive FIFO fcr.fifoe = true; // FIFO Enable mt3620_uart[id]->fcr = fcr.mask; bool dma = false; #ifdef UART_ALLOW_DMA // Debug UART doesn't seem to have DMA support. if (unit != MT3620_UNIT_UART_DEBUG) { dma = true; } #endif MT3620_UART_FIELD_WRITE(id, vfifo_en, vfifo_en, dma); if (dma) { mt3620_dma_global->ch_en_set = (1U << MT3620_UART_DMA_TX(id)); MT3620_DMA_FIELD_WRITE(MT3620_UART_DMA_TX(id), start, str, false); volatile mt3620_dma_t * const tx_dma = &mt3620_dma[MT3620_UART_DMA_TX(id)]; tx_dma->fixaddr = (void *)&mt3620_uart[id]->thr; tx_dma->pgmaddr = (void *)UART_BuffTX[id]; tx_dma->ffsize = TX_BUFFER_SIZE; tx_dma->count = 0; mt3620_dma_con_t dma_con_tx = { .mask = tx_dma->con }; dma_con_tx.dir = 0; dma_con_tx.iten = false; dma_con_tx.toen = false; dma_con_tx.dreq = false;//true; dma_con_tx.size = 0; tx_dma->con = dma_con_tx.mask; tx_dma->swptr = tx_dma->hwptr; mt3620_dma_global->ch_en_set = (1U << MT3620_UART_DMA_RX(id)); MT3620_DMA_FIELD_WRITE(MT3620_UART_DMA_RX(id), start, str, false); volatile mt3620_dma_t * const rx_dma = &mt3620_dma[MT3620_UART_DMA_RX(id)]; rx_dma->fixaddr = (void *)&mt3620_uart[id]->thr; rx_dma->pgmaddr = (void *)UART_BuffRX[id]; rx_dma->ffsize = RX_BUFFER_SIZE; rx_dma->count = 0; mt3620_dma_con_t dma_con_rx = { .mask = rx_dma->con }; dma_con_rx.dir = 1; dma_con_rx.iten = false; dma_con_rx.toen = false; dma_con_rx.dreq = true; dma_con_rx.size = 0; rx_dma->con = dma_con_rx.mask; rx_dma->swptr = rx_dma->hwptr; mt3620_uart_extend_add_t extend_add = { .mask = mt3620_uart[id]->extend_add }; extend_add.rx_dma_hsk_en = true; extend_add.tx_dma_hsk_en = true; extend_add.tx_auto_trans = true; mt3620_uart[id]->extend_add = extend_add.mask; } // If an RX callback was supplied then enable the Receive Buffer Full Interrupt. if (rxCallback) { // Enable Receiver Buffer Full Interrupt MT3620_UART_FIELD_WRITE(id, ier, erbfi, true); } NVIC_EnableIRQ(MT3620_UART_INTERRUPT(id), UART_PRIORITY); if (dma) { // Only start RX DMA as TX DMA will be unused until first transmissiom. MT3620_DMA_FIELD_WRITE(MT3620_UART_DMA_RX(id), start, str, true); } context[id].id = id; context[id].open = true; context[id].dma = dma; context[id].txRemain = TX_BUFFER_SIZE; context[id].txRead = 0; context[id].txWrite = 0; context[id].rxRemain = RX_BUFFER_SIZE; context[id].rxRead = 0; context[id].rxWrite = 0; context[id].rxCallback = rxCallback; return &context[id]; } void UART_Close(UART *handle) { if (!handle || !handle->open) { return; } MT3620_DMA_FIELD_WRITE(MT3620_UART_DMA_TX(handle->id), start, str, false); MT3620_DMA_FIELD_WRITE(MT3620_UART_DMA_RX(handle->id), start, str, false); mt3620_dma_global->ch_en_clr = (1U << MT3620_UART_DMA_TX(handle->id)); mt3620_dma_global->ch_en_clr = (1U << MT3620_UART_DMA_RX(handle->id)); mt3620_uart[handle->id]->ier = 0x00000000; NVIC_DisableIRQ(MT3620_UART_INTERRUPT(handle->id)); handle->open = false; } int32_t UART_Write(UART *handle, const void *data, uintptr_t size) { if (!handle) { return ERROR_PARAMETER; } if (!handle->open) { return ERROR_HANDLE_CLOSED; } if (!data || (size == 0)) { return ERROR_PARAMETER; } if (handle->dma) { volatile mt3620_dma_t * const tx_dma = &mt3620_dma[MT3620_UART_DMA_TX(handle->id)]; while (size > 0) { // We can't send any bytes until TX fifo is not full. while (MT3620_DMA_FIELD_READ(MT3620_UART_DMA_TX(handle->id), ffsta, full)); MT3620_DMA_FIELD_WRITE(MT3620_UART_DMA_TX(handle->id), start, str, false); uintptr_t remain = (tx_dma->ffsize - tx_dma->ffcnt); uintptr_t chunk = (remain >= size ? size : remain); uintptr_t i; for (i = 0; i < chunk; i++) { UART_BuffTX[handle->id][tx_dma->swptr++] = ((const uint8_t *)data)[i]; #if (TX_BUFFER_SIZE < 65536) // When the buffer isn't exactly 16-bits we need to handle the wrap bit. if ((tx_dma->swptr & 0xFFFF) > TX_BUFFER_SIZE) { tx_dma->swptr &= 0xFFFF0000; tx_dma->swptr ^= 0x00010000; } #endif } MT3620_DMA_FIELD_WRITE(MT3620_UART_DMA_TX(handle->id), start, str, true); data = (const void *)((uintptr_t)data + chunk); size -= chunk; } } else { // If nothing is queued in hardware, queue that first. if ((handle->txRemain == TX_BUFFER_SIZE) && MT3620_UART_FIELD_READ(handle->id, lsr, thre)) { uint32_t offset = MT3620_UART_FIELD_READ(handle->id, tx_offset, tx_offset); uint32_t remain = MT3620_UART_TX_FIFO_DEPTH - offset; uint32_t chunk = (remain > size ? size : remain); uint32_t i; for (i = 0; i < chunk; i++) { mt3620_uart[handle->id]->thr = ((const uint8_t *)data)[i]; } data = (const void *)((uintptr_t)data + chunk); size -= chunk; } // Queue remaining bytes in buffer to be handled by interrupt. while (size > 0) { while (handle->txRemain == 0) { __asm__("wfi"); } uint32_t chunk = (handle->txRemain >= size ? size : handle->txRemain); // We can't use memcpy here because the buffer wraps. uint32_t i; for (i = 0; i < chunk; i++) { UART_BuffTX[handle->id][handle->txWrite++] = ((const uint8_t *)data)[i]; handle->txWrite %= TX_BUFFER_SIZE; } handle->txRemain -= chunk; // Enable interrupt so queued data is processed. MT3620_UART_FIELD_WRITE(handle->id, ier, etbei, true); data = (const void *)((uintptr_t)data + chunk); size -= chunk; } } return ERROR_NONE; } inline bool UART_IsWriteComplete(UART *handle) { return MT3620_UART_FIELD_READ(handle->id, lsr, temt); } int32_t UART_Read(UART *handle, void *data, uintptr_t size) { if (!handle) { return ERROR_PARAMETER; } if (!handle->open) { return ERROR_HANDLE_CLOSED; } if (!data || (size == 0)) { return ERROR_PARAMETER; } if (handle->dma) { volatile mt3620_dma_t * const rx_dma = &mt3620_dma[MT3620_UART_DMA_RX(handle->id)]; while (size > 0) { // We can't receive any bytes while RX fifo is empty. while (MT3620_DMA_FIELD_READ(MT3620_UART_DMA_RX(handle->id), ffsta, empty)); MT3620_DMA_FIELD_WRITE(MT3620_UART_DMA_RX(handle->id), start, str, false); uintptr_t avail = rx_dma->ffcnt; uintptr_t chunk = (avail >= size ? size : avail); uintptr_t i; for (i = 0; i < chunk; i++) { ((uint8_t *)data)[i] = UART_BuffRX[handle->id][rx_dma->swptr++]; #if (RX_BUFFER_SIZE < 65536) // When the buffer isn't exactly 16-bits we need to handle the wrap bit. if ((rx_dma->swptr & 0xFFFF) > RX_BUFFER_SIZE) { rx_dma->swptr &= 0xFFFF0000; rx_dma->swptr ^= 0x00010000; } #endif } MT3620_DMA_FIELD_WRITE(MT3620_UART_DMA_RX(handle->id), start, str, true); data = (void *)((uintptr_t)data + chunk); size -= chunk; } } else { while (size > 0) { uintptr_t avail = RX_BUFFER_SIZE - handle->rxRemain; uintptr_t chunk = (avail > size ? size : avail); uintptr_t i; for (i = 0; i < chunk; i++) { ((uint8_t *)data)[i] = UART_BuffRX[handle->id][handle->rxRead++]; handle->rxRead %= RX_BUFFER_SIZE; } handle->rxRemain += chunk; data = (void *)((uintptr_t)data + chunk); size -= chunk; if ((size > 0) && handle->rxCallback) { __asm__("wfi"); } } } return ERROR_NONE; } uintptr_t UART_ReadAvailable(UART *handle) { if (!handle || !handle->open) { return 0; } if (handle->dma) { return mt3620_dma[MT3620_UART_DMA_RX(handle->id)].ffcnt; } else { return (RX_BUFFER_SIZE - handle->rxRemain); } } static void UART_HandleIRQ(Platform_Unit unit) { unsigned id = UART_UnitToID(unit); if (id >= MT3620_UART_COUNT) { return; } UART *handle = &context[id]; if (!handle->open) { return; } mt3620_uart_iir_id_e iirId; do { // Interrupt Identification Register iirId = MT3620_UART_FIELD_READ(handle->id, iir, iir_id); switch (iirId) { case MT3620_UART_IIR_ID_NO_INTERRUPT_PENDING: // The TX FIFO can accept more data. break; case MT3620_UART_IIR_ID_TX_HOLDING_REGISTER_EMPTY: { uint32_t offset = MT3620_UART_FIELD_READ(id, tx_offset, tx_offset); uint32_t remain = MT3620_UART_TX_FIFO_DEPTH - offset; uint32_t i; for (i = 0; (i < remain) && (handle->txRemain < TX_BUFFER_SIZE); i++, handle->txRemain++) { mt3620_uart[id]->thr = UART_BuffTX[id][handle->txRead++]; handle->txRead %= TX_BUFFER_SIZE; } // If sent all enqueued data then disable TX interrupt. if (handle->txRemain == TX_BUFFER_SIZE) { // Interrupt Enable Register MT3620_UART_FIELD_WRITE(handle->id, ier, etbei, false); } } break; // Read from the FIFO if it has passed its trigger level, or if a timeout // has occurred, meaning there is unread data still in the FIFO. case MT3620_UART_IIR_ID_RX_DATA_TIMEOUT: case MT3620_UART_IIR_ID_RX_DATA_RECEIVED: if (!handle->dma) { for (; (handle->rxRemain > 0) && MT3620_UART_FIELD_READ(handle->id, lsr, dr); handle->rxRemain--) { UART_BuffRX[id][handle->rxWrite++] = MT3620_UART_FIELD_READ(handle->id, rbr, rbr); handle->rxWrite %= RX_BUFFER_SIZE; } } if (handle->rxCallback) { handle->rxCallback(); } break; default: // Do nothing. break; } // switch (iirId) { } while (iirId != MT3620_UART_IIR_ID_NO_INTERRUPT_PENDING); } void uart_irq_b(void) { UART_HandleIRQ(MT3620_UNIT_UART_DEBUG); } void isu_g0_uart_irq_b(void) { UART_HandleIRQ(MT3620_UNIT_ISU0 ); } void isu_g1_uart_irq_b(void) { UART_HandleIRQ(MT3620_UNIT_ISU1 ); } void isu_g2_uart_irq_b(void) { UART_HandleIRQ(MT3620_UNIT_ISU2 ); } void isu_g3_uart_irq_b(void) { UART_HandleIRQ(MT3620_UNIT_ISU3 ); } void isu_g4_uart_irq_b(void) { UART_HandleIRQ(MT3620_UNIT_ISU4 ); } void isu_g5_uart_irq_b(void) { UART_HandleIRQ(MT3620_UNIT_ISU5 ); }