at91sam3x8e: serial refactor
This commit is contained in:
parent
316b4c2c0c
commit
1c835a9738
5 changed files with 65 additions and 38 deletions
|
@ -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 (arch_iface->tx_next != NULL)
|
||||
return -EBUSY;
|
||||
|
||||
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 (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…
Reference in a new issue