DragonProbe/bsp/rp2040/m_ftdi/uart.c

144 lines
4.5 KiB
C
Raw Normal View History

2021-09-07 01:19:14 +00:00
// vim: set et:
2021-09-16 01:33:46 +00:00
#include <hardware/dma.h>
#include <hardware/gpio.h>
#include <hardware/pio.h>
#include <hardware/structs/dma.h>
#include "m_ftdi/pinout.h"
#include "m_ftdi/ftdi_hw.h"
2021-09-16 01:33:46 +00:00
#include "ftdi_uart_rx.pio.h"
#include "ftdi_uart_tx.pio.h"
struct uart_state {
uint32_t baudrate;
bool enabled;
};
static struct uart_state state[2] = {
(struct uart_state){ .baudrate = 115200, .enabled = false },
(struct uart_state){ .baudrate = 115200, .enabled = false },
};
#define STATEOF(itf) (state[(itf)->index & 1])
// set up PIO->dma_in_buf DMA:
// * src=pio dst=buf 8bit 256words pacing=pio
// * IRQ: set overrun bit
// * start it
// * SETUP AT MODE ENTER, STOP AT MODE EXIT
//
// set up dma_out_buf->PIO DMA:
// * src=buf dst=pio 8bit <num>words pacing=pio
// * ~~no IRQ I think?~~ IRQ: next ringbuffer part (see below)
// * DO NOT start it on mode enter!
// * STOP AT MODE EXIT
//
// read routine:
// * abort DMA
// * copy data from dma_in_buf (read xfer len? == remaining of 256 bytes)
// * resetup & start DMA:
//
// write routine:
// * if DMA running: set overrun bit, bail out?
// * should use ringbuffer-like structure
// * pointers: dma start, dma end, data end (after dma, contiguous)
// * dma end can be calculated from DMA MMIO, but, race conditions so no
// * use DMA IRQ for next block (and wraparound: dma cannot do wraparound manually)
// * do not start next block if data end == dma start
// * can we set DMA xfer len while in-flight? datasheet p92 2.5.1: nope. sad
// * only bail out when data end == dma start - 1
// * copy data to dma_out_buf
// * set up & start DMA
//
// * what with buffers larger than 256 bytes?
// * just drop for now ig
// * what is the actual FTDI buffer size??
// * device-dependent so aaaa
// * which bits get set on errors?
// * do TCIFLUSH/TCOFLUSH influence these buffers, or only the USB proto
// handling buffers?
2021-09-17 01:25:23 +00:00
2021-09-16 01:33:46 +00:00
void ftdi_if_uart_init(struct ftdi_interface* itf) {
if (STATEOF(itf).enabled) return; // shrug
struct ftdi_hw* hw = ftdihw_itf_to_hw(itf);
2021-09-16 01:33:46 +00:00
if (!ftdihw_dma_ch_init(&hw->rx, hw, &ftdi_uart_rx_program)) return;
if (!ftdihw_dma_ch_init(&hw->tx, hw, &ftdi_uart_tx_program)) {
ftdihw_dma_ch_deinit(&hw->rx, hw);
return;
}
2021-09-16 01:33:46 +00:00
int pin_rx = hw->pinbase + PINOUT_UART_RXD_OFF,
pin_tx = hw->pinbase + PINOUT_UART_TXD_OFF;
ftdi_uart_rx_program_init(hw->pio, hw->rx.piosm, hw->rx.prg_off,
2021-09-16 01:33:46 +00:00
pin_rx, STATEOF(itf).baudrate);
ftdi_uart_tx_program_init(hw->pio, hw->tx.piosm, hw->tx.prg_off,
2021-09-16 01:33:46 +00:00
pin_tx, STATEOF(itf).baudrate);
gpio_set_function(pin_rx, GPIO_FUNC_PIO0 + itf->index);
gpio_set_function(pin_tx, GPIO_FUNC_PIO0 + itf->index);
ftdihw_dma_rx_setup(hw, true);
ftdihw_dma_tx_setup(hw, false);
2021-09-16 01:33:46 +00:00
STATEOF(itf).enabled = true;
}
void ftdi_if_uart_deinit(struct ftdi_interface* itf) {
if (!STATEOF(itf).enabled) return; // shrug
struct ftdi_hw* hw = ftdihw_itf_to_hw(itf);
int pin_rx = hw->pinbase + PINOUT_UART_RXD_OFF,
pin_tx = hw->pinbase + PINOUT_UART_TXD_OFF;
2021-09-16 01:33:46 +00:00
ftdihw_dma_rx_flush(hw);
ftdihw_dma_tx_flush(hw);
2021-09-16 01:33:46 +00:00
ftdihw_dma_ch_deinit(&hw->rx, hw);
ftdihw_dma_ch_deinit(&hw->tx, hw);
2021-09-16 01:33:46 +00:00
gpio_set_function(pin_rx, GPIO_FUNC_NULL);
gpio_set_function(pin_tx, GPIO_FUNC_NULL);
gpio_set_pulls(pin_rx, false, false);
gpio_set_pulls(pin_tx, false, false);
STATEOF(itf).enabled = false;
}
void ftdi_if_uart_set_baudrate(struct ftdi_interface* itf, uint32_t baudrate) {
if (!STATEOF(itf).enabled) return;
struct ftdi_hw* hw = ftdihw_itf_to_hw(itf);
int pin_rx = ftdihw_itf_to_base(itf) + PINOUT_UART_RXD_OFF,
pin_tx = ftdihw_itf_to_base(itf) + PINOUT_UART_TXD_OFF;
2021-09-16 01:33:46 +00:00
ftdi_uart_rx_program_init(hw->pio, hw->rx.piosm, hw->rx.prg_off, pin_rx, baudrate);
ftdi_uart_tx_program_init(hw->pio, hw->tx.piosm, hw->tx.prg_off, pin_tx, baudrate);
2021-09-16 01:33:46 +00:00
STATEOF(itf).baudrate = baudrate;
}
2021-09-07 01:19:14 +00:00
void ftdi_if_set_flowctrl(struct ftdi_interface* itf, enum ftdi_flowctrl flow) {
2021-09-16 01:33:46 +00:00
(void)itf; (void)flow; // TODO: bluh
2021-09-07 01:19:14 +00:00
}
void ftdi_if_set_lineprop(struct ftdi_interface* itf, enum ftdi_sio_lineprop lineprop) {
2021-09-16 01:33:46 +00:00
(void)itf; (void)lineprop; // TODO: break, stop, parity, #bits
2021-09-07 01:19:14 +00:00
}
void ftdi_if_uart_write(struct ftdi_interface* itf, const uint8_t* data, size_t datasize) {
struct ftdi_hw* hw = ftdihw_itf_to_hw(itf);
ftdihw_dma_write(hw, data, datasize);
2021-09-07 01:19:14 +00:00
}
size_t ftdi_if_uart_read(struct ftdi_interface* itf, uint8_t* data, size_t maxsize) {
struct ftdi_hw* hw = ftdihw_itf_to_hw(itf);
2021-09-16 01:33:46 +00:00
return ftdihw_dma_read(hw, data, maxsize);
2021-09-07 01:19:14 +00:00
}