DragonProbe/bsp/rp2040/m_default/dap_uart.c

291 lines
10 KiB
C
Raw Normal View History

// vim: set et:
#include "DAP_config.h"
#include "DAP.h"
#include <hardware/dma.h>
#include <hardware/irq.h>
#include <hardware/uart.h>
#include <pico/stdlib.h>
#include "Driver_USART.h"
#include "m_default/bsp-feature.h"
#include "m_default/pinout.h"
#include "m_default/cdc.h"
bool cdc_uart_dap_override = false;
static int rxdmach = -1, txdmach = -1;
static ARM_USART_SignalEvent_t irq_callback = NULL;
static void dap_uart_dma_isr() {
uint32_t ints = dma_hw->ints0;
uint32_t events = 0;
if (ints & (1u << rxdmach)) {
dma_hw->ints0 = 1u << rxdmach;
events |= ARM_USART_EVENT_RECEIVE_COMPLETE;
}
if (ints & (1u << txdmach)) {
dma_hw->ints0 = 1u << txdmach;
events |= ARM_USART_EVENT_SEND_COMPLETE;
}
if (events && irq_callback) irq_callback(events);
}
static void dap_uart_err_isr() {
uint32_t mis = uart_get_hw(PINOUT_UART_INTERFACE)->mis;
uint32_t events = 0;
if (mis & UART_UARTMIS_OEMIS_BITS) {
uart_get_hw(PINOUT_UART_INTERFACE)->icr = UART_UARTICR_OEIC_BITS;
events |= ARM_USART_EVENT_RX_OVERFLOW;
}
if (mis & UART_UARTMIS_PEMIS_BITS) {
uart_get_hw(PINOUT_UART_INTERFACE)->icr = UART_UARTICR_PEIC_BITS;
events |= ARM_USART_EVENT_RX_PARITY_ERROR;
}
if (mis & UART_UARTMIS_FEMIS_BITS) {
uart_get_hw(PINOUT_UART_INTERFACE)->icr = UART_UARTICR_FEIC_BITS;
events |= ARM_USART_EVENT_RX_FRAMING_ERROR;
}
if (events && irq_callback) irq_callback(events);
}
static int32_t dap_uart_initialize(ARM_USART_SignalEvent_t cb) {
if (cdc_uart_dap_override) return ARM_DRIVER_ERROR;
cdc_uart_dap_override = true;
irq_callback = cb;
// TODO: do anything?
// cdc_uart.c probably has already inited (otherwise stuff is broken),
// so we don't really have to do anything else here
// TODO: cb needs to be called on:
// * send() complete
// * receive() complete
// * rx overflow
// * rx framing error
// * rx parity error
//
// USB timeout is one second. for a good implementation, we'd want to use
// DMA to do the UART transfers in the background, but, laziness. so we do
// it in a blocking way in send/receive, and then immediately afterwards do
// the callback (instead of requiring irq stuff). gettxcount and getrxcount
// then always return the last num values
// ... except that doesn't work for receive FIFOs... welp
rxdmach = dma_claim_unused_channel(false);
if (rxdmach == -1) return ARM_DRIVER_ERROR;
txdmach = dma_claim_unused_channel(false);
if (txdmach == -1) {
dma_channel_unclaim(rxdmach);
rxdmach = -1;
return ARM_DRIVER_ERROR;
}
// make DMAing to/from UART possible
hw_set_bits(&uart_get_hw(PINOUT_UART_INTERFACE)->dmacr,
UART_UARTDMACR_TXDMAE_BITS | UART_UARTDMACR_RXDMAE_BITS
);
// set error interrupt bits CMSIS-DAP wants
hw_set_bits(&uart_get_hw(PINOUT_UART_INTERFACE)->imsc,
UART_UARTIMSC_OEIM_BITS | UART_UARTIMSC_PEIM_BITS
| UART_UARTIMSC_FEIM_BITS
);
// start disabled
hw_clear_bits(&uart_get_hw(PINOUT_UART_INTERFACE)->cr,
UART_UARTCR_RXE_BITS | UART_UARTCR_TXE_BITS
);
irq_set_enabled(DMA_IRQ_0, false);
irq_set_enabled(UART1_IRQ, false);
irq_add_shared_handler(DMA_IRQ_0, dap_uart_dma_isr,
PICO_SHARED_IRQ_HANDLER_DEFAULT_ORDER_PRIORITY);
irq_add_shared_handler(UART1_IRQ, dap_uart_err_isr,
PICO_SHARED_IRQ_HANDLER_DEFAULT_ORDER_PRIORITY);
irq_set_enabled(UART1_IRQ, true);
irq_set_enabled(DMA_IRQ_0, true);
return ARM_DRIVER_OK;
}
static int32_t dap_uart_uninitialize(void) {
// Control() may have disabled some stuff, so were going to reenable it now
hw_set_bits(&uart_get_hw(PINOUT_UART_INTERFACE)->cr,
UART_UARTCR_RXE_BITS | UART_UARTCR_TXE_BITS
);
hw_clear_bits(&uart_get_hw(PINOUT_UART_INTERFACE)->imsc,
UART_UARTIMSC_OEIM_BITS | UART_UARTIMSC_PEIM_BITS
| UART_UARTIMSC_FEIM_BITS
);
irq_set_enabled(UART1_IRQ, false);
irq_set_enabled(DMA_IRQ_0, false);
irq_remove_handler(UART1_IRQ, dap_uart_err_isr);
irq_remove_handler(DMA_IRQ_0, dap_uart_dma_isr);
dma_channel_abort(rxdmach);
dma_channel_abort(txdmach);
dma_channel_unclaim(rxdmach);
dma_channel_unclaim(txdmach);
hw_clear_bits(&uart_get_hw(PINOUT_UART_INTERFACE)->dmacr,
UART_UARTDMACR_TXDMAE_BITS | UART_UARTDMACR_RXDMAE_BITS
);
cdc_uart_dap_override = false;
return ARM_DRIVER_OK;
}
static int32_t dap_uart_powercontrol(ARM_POWER_STATE state) {
(void)state; // let's ignore that
return ARM_DRIVER_OK;
}
// DAP/Firmware/Source/UART.c doesn't use these, so let's not implement them
static ARM_DRIVER_VERSION dap_uart_getversion(void) {
panic("dap_uart: GetVersion: not impl");
__builtin_unreachable();
}
static ARM_USART_CAPABILITIES dap_uart_getcapabilities(void) {
panic("dap_uart: GetCapabilities: not impl");
__builtin_unreachable();
}
static int32_t dap_uart_transfer(const void* out, void* in, uint32_t n) {
(void)out; (void)in; (void)n;
panic("dap_uart: Transfer: not impl");
__builtin_unreachable();
}
static ARM_USART_STATUS dap_uart_getstatus(void) {
panic("dap_uart: GetStatus: not impl");
__builtin_unreachable();
}
static int32_t dap_uart_setmodemcontrol(ARM_USART_MODEM_CONTROL mctl) {
(void)mctl;
panic("dap_uart: SetModemControl: not impl");
__builtin_unreachable();
}
static ARM_USART_MODEM_STATUS dap_uart_getmodemstatus(void) {
panic("dap_uart: GetModemStatus: not impl");
__builtin_unreachable();
}
// actual useful stuff now
int32_t dap_uart_send(const void* data, uint32_t num) {
dma_channel_config dcfg = dma_channel_get_default_config(txdmach);
channel_config_set_read_increment(&dcfg, true);
channel_config_set_write_increment(&dcfg, false);
channel_config_set_dreq(&dcfg, DREQ_UART1_TX);
channel_config_set_transfer_data_size(&dcfg, DMA_SIZE_8);
dma_irqn_set_channel_enabled(0, txdmach, true);
dma_channel_configure(txdmach, &dcfg,
&uart_get_hw(PINOUT_UART_INTERFACE)->dr, data, num, true);
return ARM_DRIVER_OK;
}
int32_t dap_uart_receive(void* data, uint32_t num) {
dma_channel_config dcfg = dma_channel_get_default_config(rxdmach);
channel_config_set_read_increment(&dcfg, false);
channel_config_set_write_increment(&dcfg, true);
channel_config_set_dreq(&dcfg, DREQ_UART1_RX);
channel_config_set_transfer_data_size(&dcfg, DMA_SIZE_8);
dma_irqn_set_channel_enabled(0, rxdmach, true);
dma_channel_configure(rxdmach, &dcfg, data,
&uart_get_hw(PINOUT_UART_INTERFACE)->dr, num, true);
return ARM_DRIVER_OK;
}
uint32_t dap_uart_gettxcount(void) { // gets number of bytes transmitted of send() call
return dma_channel_hw_addr(txdmach)->transfer_count;
}
uint32_t dap_uart_getrxcount(void) { // gets number of bytes received of receive() call
return dma_channel_hw_addr(rxdmach)->transfer_count;
}
int32_t dap_uart_control(uint32_t control, uint32_t arg) {
// ARM_USART_CONTROL_RX 1/0
// ARM_USART_CONTROL_TX 1/0
// ARM_USART_ABORT_RECEIVE x
// ARM_USART_ABORT_SEND x
// control ARM_USART_MODE_ASYNCHRONOUS, ARM_USART_FLOW_CONTROL_NONE (ignore this) baudrate!
// control:
// Bit 3..0: Data bits: 5 = 5 Data bits, 6 = 6 Data bits, 7 = 7 Data bits, 0 = 8 Data bits
// Bit 5..4: Parity: 0 = None, 1 = Even, 2 = Odd
// Bit 7..6: Stop bits: 0 = 1 Stop bit, 1 = 2 Stop bits
if (control == ARM_USART_CONTROL_RX) {
if (arg)
hw_set_bits(&uart_get_hw(PINOUT_UART_INTERFACE)->cr, UART_UARTCR_RXE_BITS);
else
hw_clear_bits(&uart_get_hw(PINOUT_UART_INTERFACE)->cr, UART_UARTCR_RXE_BITS);
} else if (control == ARM_USART_CONTROL_TX) {
if (arg)
hw_set_bits(&uart_get_hw(PINOUT_UART_INTERFACE)->cr, UART_UARTCR_TXE_BITS);
else
hw_clear_bits(&uart_get_hw(PINOUT_UART_INTERFACE)->cr, UART_UARTCR_TXE_BITS);
} else if (control == ARM_USART_ABORT_SEND) {
dma_channel_abort(txdmach);
} else if (control == ARM_USART_ABORT_RECEIVE) {
dma_channel_abort(rxdmach);
} else if (control & ARM_USART_MODE_ASYNCHRONOUS) {
// for now, don't set some stuff as it can sometimes be a bit borked
// with the rp2040 uart(?): parity, stop, data bits
uint32_t crv = uart_get_hw(PINOUT_UART_INTERFACE)->cr
& (UART_UARTCR_TXE_BITS | UART_UARTCR_RXE_BITS | UART_UARTCR_UARTEN_BITS);
// disable interrupts for a moment so that flushing the FIFOs won't
// suddenly cause spurious interrupts
hw_clear_bits(&uart_get_hw(PINOUT_UART_INTERFACE)->imsc,
UART_UARTIMSC_OEIM_BITS | UART_UARTIMSC_PEIM_BITS
| UART_UARTIMSC_FEIM_BITS
);
// disable momentarily
hw_clear_bits(&uart_get_hw(PINOUT_UART_INTERFACE)->cr, crv);
hw_clear_bits(&uart_get_hw(PINOUT_UART_INTERFACE)->lcr_h, UART_UARTLCR_H_FEN_BITS); // clears the FIFO(?)
uart_set_baudrate(PINOUT_UART_INTERFACE, arg);
// reenable
hw_set_bits(&uart_get_hw(PINOUT_UART_INTERFACE)->lcr_h, UART_UARTLCR_H_FEN_BITS);
hw_set_bits(&uart_get_hw(PINOUT_UART_INTERFACE)->cr, crv);
hw_set_bits(&uart_get_hw(PINOUT_UART_INTERFACE)->imsc,
UART_UARTIMSC_OEIM_BITS | UART_UARTIMSC_PEIM_BITS
| UART_UARTIMSC_FEIM_BITS
);
}
return ARM_DRIVER_OK;
}
extern ARM_DRIVER_USART Driver_USART_DAPFAKE;
ARM_DRIVER_USART Driver_USART_DAPFAKE = {
.GetVersion = dap_uart_getversion, // nop
.GetCapabilities = dap_uart_getcapabilities, // nop
.Initialize = dap_uart_initialize,
.Uninitialize = dap_uart_uninitialize,
.PowerControl = dap_uart_powercontrol,
.Send = dap_uart_send,
.Receive = dap_uart_receive,
.Transfer = dap_uart_transfer, // nop
.GetTxCount = dap_uart_gettxcount,
.GetRxCount = dap_uart_getrxcount,
.Control = dap_uart_control,
.GetStatus = dap_uart_getstatus, // nop
.SetModemControl = dap_uart_setmodemcontrol, // nop
.GetModemStatus = dap_uart_getmodemstatus // nop
};
// USB-CDC stuff
uint8_t USB_COM_PORT_Activate(uint32_t _) { (void)_; return 0; }