/* Copyright 2018 Canaan Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * @file hardware_uart.c * @brief add from Canaan k210 SDK * https://canaan-creative.com/developer * @version 1.0 * @author AIIT XUOS Lab * @date 2021-04-25 */ #include #include #include "plic.h" #include "sysctl.h" #include "hardware_uart.h" #include "utils.h" #include "atomic.h" #define __UART_BRATE_CONST 16 volatile UartT* const uart[3] = { (volatile UartT*)UART1_BASE_ADDR, (volatile UartT*)UART2_BASE_ADDR, (volatile UartT*)UART3_BASE_ADDR }; #define UART_INTERRUPT_SEND 0x02U #define UART_INTERRUPT_RECEIVE 0x04U #define UART_INTERRUPT_CHARACTER_TIMEOUT 0x0CU typedef struct UartInterruptInstance { plic_irq_callback_t callback; void *ctx; } UartInterruptInstanceT; typedef struct UartInstance { UartInterruptInstanceT UartReceiveInstance; UartInterruptInstanceT UartSendInstance; uint32_t UartNum; } UartInstancePointer; UartInstancePointer GUartInstance[3]; typedef struct UartDmaInstance { uint8_t *buffer; size_t BufLen; uint32_t *MallocBuffer; UartInterruptModeT IntMode; dmac_channel_number_t dmac_channel; UartDeviceNumberT UartNum; UartInterruptInstanceT UartIntInstance; } UartDmaInstanceT; UartDmaInstanceT uart_send_dma_instance[3]; UartDmaInstanceT UartRecvDmaInstance[3]; typedef struct UartInstanceDma { UartDeviceNumberT UartNum; UartInterruptModeT TransferMode; dmac_channel_number_t dmac_channel; plic_instance_t UartIntInstance; spinlock_t lock; } UartInstanceDmaT; static UartInstanceDmaT GUartSendInstanceDma[3]; static UartInstanceDmaT GUartRecvInstanceDma[3]; volatile int GWriteCount = 0; static int UartIrqCallback(void *param) { UartInstancePointer *uart_instance = (UartInstancePointer *)param; uint32_t v_channel = uart_instance->UartNum; uint8_t VIntStatus = uart[v_channel]->IIR & 0xF; if(VIntStatus == UART_INTERRUPT_SEND && GWriteCount != 0) { if(uart_instance->UartSendInstance.callback != NULL) uart_instance->UartSendInstance.callback(uart_instance->UartSendInstance.ctx); } else if(VIntStatus == UART_INTERRUPT_RECEIVE || VIntStatus == UART_INTERRUPT_CHARACTER_TIMEOUT) { if(uart_instance->UartReceiveInstance.callback != NULL) uart_instance->UartReceiveInstance.callback(uart_instance->UartReceiveInstance.ctx); } return 0; } static int UartapbPutc(UartDeviceNumberT channel, char c) { while (uart[channel]->LSR & (1u << 5)) continue; uart[channel]->THR = c; return 0; } int UartapbGetc(UartDeviceNumberT channel) { while (!(uart[channel]->LSR & 1)) continue; return (char)(uart[channel]->RBR & 0xff); } static int UartDmaCallback(void *ctx) { UartDmaInstanceT *VUartDmaInstance = (UartDmaInstanceT *)ctx; dmac_channel_number_t dmac_channel = VUartDmaInstance->dmac_channel; dmac_irq_unregister(dmac_channel); if(VUartDmaInstance->IntMode == UART_RECEIVE) { size_t VBufLen = VUartDmaInstance->BufLen; uint8_t *VBuffer = VUartDmaInstance->buffer; uint32_t *VRecvBuffer = VUartDmaInstance->MallocBuffer; for(size_t i = 0; i < VBufLen; i++) { VBuffer[i] = VRecvBuffer[i]; } } free(VUartDmaInstance->MallocBuffer); if(VUartDmaInstance->UartIntInstance.callback) VUartDmaInstance->UartIntInstance.callback(VUartDmaInstance->UartIntInstance.ctx); return 0; } int UartReceiveData(UartDeviceNumberT channel, char *buffer, size_t BufLen) { size_t i = 0; for(i = 0;i < BufLen; i++) { if(uart[channel]->LSR & 1) buffer[i] = (char)(uart[channel]->RBR & 0xff); else break; } return i; } void UartReceiveDataDma(UartDeviceNumberT uart_channel, dmac_channel_number_t dmac_channel, uint8_t *buffer, size_t BufLen) { uint32_t *VRecvBuf = malloc(BufLen * sizeof(uint32_t)); configASSERT(VRecvBuf!=NULL); sysctl_dma_select((sysctl_dma_channel_t)dmac_channel, SYSCTL_DMA_SELECT_UART1_RX_REQ + uart_channel * 2); dmac_set_single_mode(dmac_channel, (void *)(&uart[uart_channel]->RBR), VRecvBuf, DMAC_ADDR_NOCHANGE, DMAC_ADDR_INCREMENT, DMAC_MSIZE_1, DMAC_TRANS_WIDTH_32, BufLen); dmac_wait_done(dmac_channel); for(uint32_t i = 0; i < BufLen; i++) { buffer[i] = (uint8_t)(VRecvBuf[i] & 0xff); } free(VRecvBuf); } void UartReceiveDataDmaIrq(UartDeviceNumberT uart_channel, dmac_channel_number_t dmac_channel, uint8_t *buffer, size_t BufLen, plic_irq_callback_t uart_callback, void *ctx, uint32_t priority) { uint32_t *VRecvBuf = malloc(BufLen * sizeof(uint32_t)); configASSERT(VRecvBuf!=NULL); UartRecvDmaInstance[uart_channel].dmac_channel = dmac_channel; UartRecvDmaInstance[uart_channel].UartNum = uart_channel; UartRecvDmaInstance[uart_channel].MallocBuffer = VRecvBuf; UartRecvDmaInstance[uart_channel].buffer = buffer; UartRecvDmaInstance[uart_channel].BufLen = BufLen; UartRecvDmaInstance[uart_channel].IntMode = UART_RECEIVE; UartRecvDmaInstance[uart_channel].UartIntInstance.callback = uart_callback; UartRecvDmaInstance[uart_channel].UartIntInstance.ctx = ctx; dmac_irq_register(dmac_channel, UartDmaCallback, &UartRecvDmaInstance[uart_channel], priority); sysctl_dma_select((sysctl_dma_channel_t)dmac_channel, SYSCTL_DMA_SELECT_UART1_RX_REQ + uart_channel * 2); dmac_set_single_mode(dmac_channel, (void *)(&uart[uart_channel]->RBR), VRecvBuf, DMAC_ADDR_NOCHANGE, DMAC_ADDR_INCREMENT, DMAC_MSIZE_1, DMAC_TRANS_WIDTH_32, BufLen); } int UartSendData(UartDeviceNumberT channel, const char *buffer, size_t BufLen) { GWriteCount = 0; while (GWriteCount < BufLen) { UartapbPutc(channel, *buffer++); GWriteCount++; } return GWriteCount; } void UartSendDataDma(UartDeviceNumberT uart_channel, dmac_channel_number_t dmac_channel, const uint8_t *buffer, size_t BufLen) { uint32_t *VSendBuf = malloc(BufLen * sizeof(uint32_t)); configASSERT(VSendBuf!=NULL); for(uint32_t i = 0; i < BufLen; i++) VSendBuf[i] = buffer[i]; sysctl_dma_select((sysctl_dma_channel_t)dmac_channel, SYSCTL_DMA_SELECT_UART1_TX_REQ + uart_channel * 2); dmac_set_single_mode(dmac_channel, VSendBuf, (void *)(&uart[uart_channel]->THR), DMAC_ADDR_INCREMENT, DMAC_ADDR_NOCHANGE, DMAC_MSIZE_1, DMAC_TRANS_WIDTH_32, BufLen); dmac_wait_done(dmac_channel); free((void *)VSendBuf); } void UartSendDataDmaIrq(UartDeviceNumberT uart_channel, dmac_channel_number_t dmac_channel, const uint8_t *buffer, size_t BufLen, plic_irq_callback_t uart_callback, void *ctx, uint32_t priority) { uint32_t *VSendBuf = malloc(BufLen * sizeof(uint32_t)); configASSERT(VSendBuf!=NULL); uart_send_dma_instance[uart_channel] = (UartDmaInstanceT) { .dmac_channel = dmac_channel, .UartNum = uart_channel, .MallocBuffer = VSendBuf, .buffer = (uint8_t *)buffer, .BufLen = BufLen, .IntMode = UART_SEND, .UartIntInstance.callback = uart_callback, .UartIntInstance.ctx = ctx, }; for(uint32_t i = 0; i < BufLen; i++) VSendBuf[i] = buffer[i]; dmac_irq_register(dmac_channel, UartDmaCallback, &uart_send_dma_instance[uart_channel], priority); sysctl_dma_select((sysctl_dma_channel_t)dmac_channel, SYSCTL_DMA_SELECT_UART1_TX_REQ + uart_channel * 2); dmac_set_single_mode(dmac_channel, VSendBuf, (void *)(&uart[uart_channel]->THR), DMAC_ADDR_INCREMENT, DMAC_ADDR_NOCHANGE, DMAC_MSIZE_1, DMAC_TRANS_WIDTH_32, BufLen); } void uart_configure(UartDeviceNumberT channel, uint32_t BaudRate, UartBitwidthPointer DataWidth, UartStopbitT stopbit, UartParityT parity) { configASSERT(DataWidth >= 5 && DataWidth <= 8); if (DataWidth == 5) { configASSERT(stopbit != UART_STOP_2); } else { configASSERT(stopbit != UART_STOP_1_5); } uint32_t stopbit_val = stopbit == UART_STOP_1 ? 0 : 1; uint32_t ParityVal; switch (parity) { case UART_PARITY_NONE: ParityVal = 0; break; case UART_PARITY_ODD: ParityVal = 1; break; case UART_PARITY_EVEN: ParityVal = 3; break; default: configASSERT(!"Invalid parity"); break; } uint32_t freq = SysctlClockGetFreq(SYSCTL_CLOCK_APB0); uint32_t divisor = freq / BaudRate; uint8_t dlh = divisor >> 12; uint8_t dll = (divisor - (dlh << 12)) / __UART_BRATE_CONST; uint8_t dlf = divisor - (dlh << 12) - dll * __UART_BRATE_CONST; /* Set UART registers */ uart[channel]->TCR &= ~(1u); uart[channel]->TCR &= ~(1u << 3); uart[channel]->TCR &= ~(1u << 4); uart[channel]->TCR |= (1u << 2); uart[channel]->TCR &= ~(1u << 1); uart[channel]->DE_EN &= ~(1u); uart[channel]->LCR |= 1u << 7; uart[channel]->DLH = dlh; uart[channel]->DLL = dll; uart[channel]->DLF = dlf; uart[channel]->LCR = 0; uart[channel]->LCR = (DataWidth - 5) | (stopbit_val << 2) | (ParityVal << 3); uart[channel]->LCR &= ~(1u << 7); uart[channel]->MCR &= ~3; uart[channel]->IER |= 0x80; /* THRE */ uart[channel]->FCR = UART_RECEIVE_FIFO_1 << 6 | UART_SEND_FIFO_8 << 4 | 0x1 << 3 | 0x1; } void __attribute__((weak, alias("uart_configure"))) uart_config(UartDeviceNumberT channel, uint32_t BaudRate, UartBitwidthPointer DataWidth, UartStopbitT stopbit, UartParityT parity); void UartInit(UartDeviceNumberT channel) { sysctl_clock_enable(SYSCTL_CLOCK_UART1 + channel); } void UartSetSendTrigger(UartDeviceNumberT channel, uart_send_trigger_t trigger) { uart[channel]->STET = trigger; } void uart_set_receive_trigger(UartDeviceNumberT channel, uart_receive_trigger_t trigger) { uart[channel]->SRT = trigger; } void uart_irq_register(UartDeviceNumberT channel, UartInterruptModeT interrupt_mode, plic_irq_callback_t uart_callback, void *ctx, uint32_t priority) { if(interrupt_mode == UART_SEND) { uart[channel]->IER |= 0x2; GUartInstance[channel].UartSendInstance.callback = uart_callback; GUartInstance[channel].UartSendInstance.ctx = ctx; } else if(interrupt_mode == UART_RECEIVE) { uart[channel]->IER |= 0x1; GUartInstance[channel].UartReceiveInstance.callback = uart_callback; GUartInstance[channel].UartReceiveInstance.ctx = ctx; } GUartInstance[channel].UartNum = channel; plic_set_priority(IRQN_UART1_INTERRUPT + channel, priority); plic_irq_register(IRQN_UART1_INTERRUPT + channel, UartIrqCallback, &GUartInstance[channel]); plic_irq_enable(IRQN_UART1_INTERRUPT + channel); } void uart_irq_unregister(UartDeviceNumberT channel, UartInterruptModeT interrupt_mode) { if(interrupt_mode == UART_SEND) { uart[channel]->IER &= ~(0x2); GUartInstance[channel].UartSendInstance.callback = NULL; GUartInstance[channel].UartSendInstance.ctx = NULL; } else if(interrupt_mode == UART_RECEIVE) { uart[channel]->IER &= ~(0x1); GUartInstance[channel].UartReceiveInstance.callback = NULL; GUartInstance[channel].UartReceiveInstance.ctx = NULL; } if(uart[channel]->IER == 0) { plic_irq_unregister(IRQN_UART1_INTERRUPT + channel); } } int uart_dma_irq(void *ctx) { UartInstanceDmaT *v_instance = (UartInstanceDmaT *)ctx; dmac_irq_unregister(v_instance->dmac_channel); if(v_instance->TransferMode == UART_SEND) { while(!(uart[v_instance->UartNum]->LSR & (1u << 6))); } spinlock_unlock(&v_instance->lock); if(v_instance->UartIntInstance.callback) { v_instance->UartIntInstance.callback(v_instance->UartIntInstance.ctx); } return 0; } void uart_handle_data_dma(UartDeviceNumberT uart_channel ,uart_data_t data, plic_interrupt_t *cb) { configASSERT(uart_channel < UART_DEVICE_MAX); if(data.TransferMode == UART_SEND) { configASSERT(data.tx_buf && data.tx_len && data.tx_channel < DMAC_CHANNEL_MAX); spinlock_lock(&GUartSendInstanceDma[uart_channel].lock); if(cb) { GUartSendInstanceDma[uart_channel].UartIntInstance.callback = cb->callback; GUartSendInstanceDma[uart_channel].UartIntInstance.ctx = cb->ctx; GUartSendInstanceDma[uart_channel].dmac_channel = data.tx_channel; GUartSendInstanceDma[uart_channel].TransferMode = UART_SEND; dmac_irq_register(data.tx_channel, uart_dma_irq, &GUartSendInstanceDma[uart_channel], cb->priority); } sysctl_dma_select((sysctl_dma_channel_t)data.tx_channel, SYSCTL_DMA_SELECT_UART1_TX_REQ + uart_channel * 2); dmac_set_single_mode(data.tx_channel, data.tx_buf, (void *)(&uart[uart_channel]->THR), DMAC_ADDR_INCREMENT, DMAC_ADDR_NOCHANGE, DMAC_MSIZE_1, DMAC_TRANS_WIDTH_32, data.tx_len); if(!cb) { dmac_wait_done(data.tx_channel); while(!(uart[uart_channel]->LSR & (1u << 6))); spinlock_unlock(&GUartSendInstanceDma[uart_channel].lock); } } else { configASSERT(data.rx_buf && data.rx_len && data.rx_channel < DMAC_CHANNEL_MAX); spinlock_lock(&GUartRecvInstanceDma[uart_channel].lock); if(cb) { GUartRecvInstanceDma[uart_channel].UartIntInstance.callback = cb->callback; GUartRecvInstanceDma[uart_channel].UartIntInstance.ctx = cb->ctx; GUartRecvInstanceDma[uart_channel].dmac_channel = data.rx_channel; GUartRecvInstanceDma[uart_channel].TransferMode = UART_RECEIVE; dmac_irq_register(data.rx_channel, uart_dma_irq, &GUartRecvInstanceDma[uart_channel], cb->priority); } sysctl_dma_select((sysctl_dma_channel_t)data.rx_channel, SYSCTL_DMA_SELECT_UART1_RX_REQ + uart_channel * 2); dmac_set_single_mode(data.rx_channel, (void *)(&uart[uart_channel]->RBR), data.rx_buf, DMAC_ADDR_NOCHANGE, DMAC_ADDR_INCREMENT, DMAC_MSIZE_1, DMAC_TRANS_WIDTH_32, data.rx_len); if(!cb) { dmac_wait_done(data.rx_channel); spinlock_unlock(&GUartRecvInstanceDma[uart_channel].lock); } } }