at91sam3x8e: serial refactor

io-wait
Felix Kopp 3 years ago
parent 316b4c2c0c
commit 1c835a9738
No known key found for this signature in database
GPG Key ID: C478BA0A85F75728

@ -38,7 +38,8 @@ ARDIX_SOURCES += \
ARDIX_ASM_SOURCES += \
$(ARDIX_ARCH_PWD)/irq_pend_sv.S \
$(ARDIX_ARCH_PWD)/irq_svc.S
$(ARDIX_ARCH_PWD)/irq_svc.S \
$(ARDIX_ARCH_PWD)/syscall.S
CFLAGS += \
-DARCH_AT91SAM3X8E

@ -3,6 +3,8 @@
#include <ardix/atomic.h>
#include <ardix/io.h>
#include <ardix/list.h>
#include <ardix/malloc.h>
#include <ardix/ringbuf.h>
#include <ardix/serial.h>
#include <ardix/string.h>
@ -16,13 +18,14 @@
#include <stddef.h>
struct arch_serial_interface arch_serial_default_interface = {
.tx_current = NULL,
.tx_next = NULL,
.interface = {
.tx = NULL,
.rx = NULL,
.id = 0,
.baud = 0,
},
.hw_txrdy = false,
};
struct serial_interface *serial_default_interface = &arch_serial_default_interface.interface;
@ -33,8 +36,6 @@ int arch_serial_init(struct serial_interface *interface)
if (interface->baud <= 0 || interface->id != 0)
return -1;
memset(&arch_iface->txbuf[0], 0, CONFIG_ARCH_SERIAL_BUFSZ);
/* enable peripheral clock for UART (which has peripheral id 8) */
REG_PMC_PCER0 |= REG_PMC_PCER0_PID(8);
@ -57,7 +58,7 @@ int arch_serial_init(struct serial_interface *interface)
/* choose the events we want an interrupt on */
REG_UART_IDR = 0xFFFFFFFF; /* make sure all interrupts are disabled first */
REG_UART_IER = REG_UART_IER_RXRDY_MASK
| REG_UART_IER_TXBUFE_MASK
| REG_UART_IER_ENDTX_MASK
| REG_UART_IER_OVRE_MASK
| REG_UART_IER_FRAME_MASK;
@ -85,24 +86,37 @@ void arch_serial_exit(struct serial_interface *interface)
interface->id = -1;
}
void io_serial_buf_update(struct serial_interface *interface)
ssize_t arch_serial_write(struct serial_interface *interface, const void *buf, size_t len)
{
uint16_t len;
struct arch_serial_buffer *arch_buf = NULL;
struct arch_serial_interface *arch_iface = to_arch_serial_interface(interface);
if (arch_iface->hw_txrdy) {
atomic_enter();
len = (uint16_t)ringbuf_read(&arch_iface->txbuf[0], interface->tx,
CONFIG_ARCH_SERIAL_BUFSZ);
atomic_leave();
if (len) {
arch_iface->hw_txrdy = false;
REG_UART_IER = REG_UART_IER_TXBUFE_MASK;
REG_UART_PDC_TPR = (uint32_t)&arch_iface->txbuf[0];
REG_UART_PDC_TCR = len;
}
if (arch_iface->tx_next != NULL)
return -EBUSY;
if (len >= (1 << 16)) /* DMA uses 16-bit counters */
len = 0xffff;
arch_buf = malloc(sizeof(*arch_buf) + len);
if (arch_buf == NULL)
return -ENOMEM;
memcpy(&arch_buf->data[0], buf, len);
arch_buf->len = (uint16_t)len;
if (arch_iface->tx_current == NULL) {
arch_iface->tx_current = arch_buf;
REG_UART_PDC_TPR = (uint32_t)&arch_buf->data[0];
REG_UART_PDC_TCR = arch_buf->len;
/* we weren't transmitting, so the interrupt was masked */
REG_UART_IER = REG_UART_IER_ENDTX_MASK;
} else {
arch_iface->tx_next = arch_buf;
REG_UART_PDC_TNPR = (uint32_t)&arch_buf->data[0];
REG_UART_PDC_TNCR = arch_buf->len;
}
return (ssize_t)len;
}
void irq_uart(void)
@ -116,15 +130,16 @@ void irq_uart(void)
ringbuf_write(arch_serial_default_interface.interface.rx, &tmp, sizeof(tmp));
}
/* TX buffer has been sent */
if (state & REG_UART_SR_TXBUFE_MASK) {
/*
* this is picked up by the I/O thread, which will copy the next
* chunk of data from the ring buffer to the hardware buffer and
* resume transmission
*/
arch_serial_default_interface.hw_txrdy = true;
REG_UART_IDR = REG_UART_IDR_TXBUFE_MASK;
/* REG_UART_PDC_TCR has reached zero */
if (state & REG_UART_SR_ENDTX_MASK) {
free(arch_serial_default_interface.tx_current);
/* DMA automatically does this to the actual hardware registers */
arch_serial_default_interface.tx_current = arch_serial_default_interface.tx_next;
arch_serial_default_interface.tx_next = NULL;
if (arch_serial_default_interface.tx_current == NULL)
REG_UART_IDR = REG_UART_IDR_ENDTX_MASK;
}
/* check for error conditions */

@ -13,20 +13,19 @@
#define CONFIG_ARCH_SERIAL_BUFSZ 32
#endif /* CONFIG_ARCH_SERIAL_BUFSZ */
struct arch_serial_buffer {
uint16_t len;
uint8_t data[];
};
/** Architecture-specific extension of `struct serial_interface` */
struct arch_serial_interface {
struct serial_interface interface;
/** should always match REG_UART_PDC_TPR */
struct arch_serial_buffer *tx_current;
/** should always match REG_UART_PDC_TNPR */
struct arch_serial_buffer *tx_next;
/*
* two hardware buffers; one is for being written to while the other one can be read from
* by the hardware w/out interfering with each other. `arch_serial_hwbuf_rotate()` is
* responsible for writing this to the respective hardware register and swapping them out.
* The platform's buffer length registers only allow 16-byte numbers, so we can save some
* memory by not using `size_t`
*/
uint8_t txbuf[CONFIG_ARCH_SERIAL_BUFSZ];
/** hardware has finished sending the current buffer and ready for a swap */
bool hw_txrdy;
struct serial_interface interface;
};
/**

@ -10,6 +10,17 @@
int arch_serial_init(struct serial_interface *interface);
void arch_serial_exit(struct serial_interface *interface);
/**
* Copy `buf` to a hardware buffer in the TX queue.
* The transmission is performed asynchronously.
*
* @param interface: serial interface to enqueue the buffer for
* @param buf: raw buffer data
* @param len: length of `buf`
* @returns actual amount of bytes enqueued, or a negative error code on failure
*/
ssize_t arch_serial_write(struct serial_interface *interface, const void *buf, size_t len);
#include ARCH_INCLUDE(serial.h)
/*

@ -5,6 +5,7 @@
#include <ardix/types.h>
#include <ardix/ringbuf.h>
#include <toolchain.h>
#ifndef CONFIG_SERIAL_BAUD

Loading…
Cancel
Save