summaryrefslogtreecommitdiff
path: root/chip
diff options
context:
space:
mode:
Diffstat (limited to 'chip')
-rw-r--r--chip/lm4/uart.c457
1 files changed, 52 insertions, 405 deletions
diff --git a/chip/lm4/uart.c b/chip/lm4/uart.c
index 26373e21e5..7fa01dec7a 100644
--- a/chip/lm4/uart.c
+++ b/chip/lm4/uart.c
@@ -19,50 +19,62 @@
/* Baud rate for UARTs */
#define BAUD_RATE 115200
-/* Buffer sizes; should be power of 2 */
-#define TX_BUF_SIZE 512
-#define RX_BUF_SIZE 128 /* suggest larger than 80 to copy&paste script. */
-
-/* Macros to advance in the circular transmit and receive buffers */
-#define TX_BUF_NEXT(i) (((i) + 1) & (TX_BUF_SIZE - 1))
-#define RX_BUF_NEXT(i) (((i) + 1) & (RX_BUF_SIZE - 1))
-#define RX_BUF_PREV(i) (((i) - 1) & (RX_BUF_SIZE - 1))
-
-/* Transmit and receive buffers */
-static volatile char tx_buf[TX_BUF_SIZE];
-static volatile int tx_buf_head = 0;
-static volatile int tx_buf_tail = 0;
-static volatile char rx_buf[RX_BUF_SIZE];
-static volatile int rx_buf_head = 0;
-static volatile int rx_buf_tail = 0;
-static int last_rx_was_cr = 0;
-
-static int console_mode = 1;
-
-/* TODO: should have an API to set raw mode for the UART. In raw
- * mode, we don't do CRLF translation or echo input. */
-
-
-/* Put a single character into the transmit buffer. Does not enable
- * the transmit interrupt; assumes that happens elsewhere. Returns
- * zero if the character was transmitted, 1 if it was dropped. */
-static int __tx_char(int c)
+void uart_tx_start(void)
{
- int tx_buf_next;
+ /* Re-enable the transmit interrupt, then forcibly trigger the
+ * interrupt. This works around a hardware problem with the
+ * UART where the FIFO only triggers the interrupt when its
+ * threshold is _crossed_, not just met. */
+ LM4_UART_IM(0) |= 0x20;
+ task_trigger_irq(LM4_IRQ_UART0);
+}
- /* Do newline to CRLF translation */
- if (console_mode && c == '\n' && __tx_char('\r'))
- return 1;
+void uart_tx_stop(void)
+{
+ LM4_UART_IM(0) &= ~0x20;
+}
- tx_buf_next = TX_BUF_NEXT(tx_buf_head);
- if (tx_buf_next == tx_buf_tail)
- return 1;
+int uart_tx_stopped(void)
+{
+ return !(LM4_UART_IM(0) & 0x20);
+}
- tx_buf[tx_buf_head] = c;
- tx_buf_head = tx_buf_next;
- return 0;
+void uart_tx_flush(void)
+{
+ /* Wait for transmit FIFO empty */
+ while (!(LM4_UART_FR(0) & 0x80))
+ ;
}
+int uart_tx_ready(void)
+{
+ return !(LM4_UART_FR(0) & 0x20);
+}
+
+int uart_rx_available(void)
+{
+ return !(LM4_UART_FR(0) & 0x10);
+}
+
+void uart_write_char(char c)
+{
+ LM4_UART_DR(0) = c;
+}
+
+int uart_read_char(void)
+{
+ return LM4_UART_DR(0);
+}
+
+void uart_disable_interrupt(void)
+{
+ task_disable_irq(LM4_IRQ_UART0);
+}
+
+void uart_enable_interrupt(void)
+{
+ task_enable_irq(LM4_IRQ_UART0);
+}
/* Interrupt handler for UART0 */
static void uart_0_interrupt(void)
@@ -70,64 +82,9 @@ static void uart_0_interrupt(void)
/* Clear transmit and receive interrupt status */
LM4_UART_ICR(0) = 0x70;
- /* Copy input from buffer until RX fifo empty */
- while (!(LM4_UART_FR(0) & 0x10)) {
- int c = LM4_UART_DR(0);
-
- /* Handle console mode echoing and translation */
- if (console_mode) {
- /* Translate CR and CRLF to LF (newline) */
- if (c == '\r') {
- last_rx_was_cr = 1;
- c = '\n';
- } else if (c == '\n' && last_rx_was_cr) {
- last_rx_was_cr = 0;
- continue;
- } else {
- last_rx_was_cr = 0;
- }
-
- /* Echo characters directly to the transmit FIFO so we
- * don't interfere with the transmit buffer. This
- * means that if a lot of output is happening, input
- * characters won't always be properly echoed. */
- if (console_mode && c == '\n')
- LM4_UART_DR(0) = '\r';
- LM4_UART_DR(0) = c;
-
- /* Handle backspace if we can */
- if (c == '\b') {
- if (rx_buf_head != rx_buf_tail) {
- /* Delete the previous character (and
- * space over it on the output) */
- LM4_UART_DR(0) = ' ';
- LM4_UART_DR(0) = '\b';
- rx_buf_head = RX_BUF_PREV(rx_buf_head);
- }
- continue;
- }
- }
-
- rx_buf[rx_buf_head] = c;
- rx_buf_head = RX_BUF_NEXT(rx_buf_head);
- /* On overflow, discard oldest output */
- if (rx_buf_head == rx_buf_tail)
- rx_buf_tail = RX_BUF_NEXT(rx_buf_tail);
-
- /* Call console callback on newline, if in console mode */
- if (console_mode && c == '\n')
- console_has_input();
- }
-
- /* Copy output from buffer until TX fifo full or output buffer empty */
- while (!(LM4_UART_FR(0) & 0x20) && (tx_buf_head != tx_buf_tail)) {
- LM4_UART_DR(0) = tx_buf[tx_buf_tail];
- tx_buf_tail = TX_BUF_NEXT(tx_buf_tail);
- }
- /* If output buffer is empty, disable transmit interrupt */
- if (tx_buf_tail == tx_buf_head)
- LM4_UART_IM(0) &= ~0x20;
+ /* Read input FIFO until empty, then fill output FIFO */
+ uart_process();
}
DECLARE_IRQ(LM4_IRQ_UART0, uart_0_interrupt, 1);
@@ -227,316 +184,6 @@ int uart_init(void)
}
-void uart_set_console_mode(int enable)
-{
- console_mode = enable;
-}
-
-
-int uart_puts(const char *outstr)
-{
- int was_empty = (tx_buf_head == tx_buf_tail);
-
- /* Put all characters in the output buffer */
- while (*outstr) {
- if (__tx_char(*outstr++) != 0)
- break;
- }
-
- if (was_empty) {
- /* Re-enable the transmit interrupt, then forcibly trigger the
- * interrupt. This works around a hardware problem with the
- * UART where the FIFO only triggers the interrupt when its
- * threshold is _crossed_, not just met. */
- LM4_UART_IM(0) |= 0x20;
- task_trigger_irq(LM4_IRQ_UART0);
- }
-
- /* Successful if we consumed all output */
- return *outstr ? EC_ERROR_OVERFLOW : EC_SUCCESS;
-}
-
-
-int uart_printf(const char *format, ...)
-{
- static const char int_chars[] = "0123456789abcdef";
- static const char error_str[] = "ERROR";
- char intbuf[21]; /* Longest uint64 */
- int dropped_chars = 0;
- int is_left;
- int pad_zero;
- int pad_width;
- int was_empty = (tx_buf_head == tx_buf_tail);
- va_list args;
- char *vstr;
- int vlen;
-
- va_start(args, format);
-
- while (*format && !dropped_chars) {
- int c = *format++;
-
- /* Copy normal characters */
- if (c != '%') {
- dropped_chars |= __tx_char(c);
- continue;
- }
-
- /* Get first format character */
- c = *format++;
-
- /* Send "%" for "%%" input */
- if (c == '%' || c == '\0') {
- dropped_chars |= __tx_char('%');
- continue;
- }
-
- /* Handle %c */
- if (c == 'c') {
- c = va_arg(args, int);
- dropped_chars |= __tx_char(c);
- continue;
- }
-
- /* Handle left-justification ("%-5s") */
- is_left = (c == '-');
- if (is_left)
- c = *format++;
-
- /* Handle padding with 0's */
- pad_zero = (c == '0');
- if (pad_zero)
- c = *format++;
-
- /* Count padding length */
- pad_width = 0;
- while (c >= '0' && c <= '9') {
- pad_width = (10 * pad_width) + c - '0';
- c = *format++;
- }
- if (pad_width > 80) {
- /* Sanity check for width failed */
- format = error_str;
- continue;
- }
-
- if (c == 's') {
- vstr = va_arg(args, char *);
- if (vstr == NULL)
- vstr = "(NULL)";
- } else {
- uint32_t v;
- int is_negative = 0;
- int base = 10;
-
- /* TODO: (crosbug.com/p/7490) handle "%l" prefix for
- * uint64_t */
-
- v = va_arg(args, uint32_t);
-
- switch (c) {
- case 'd':
- if ((int)v < 0) {
- is_negative = 1;
- v = -v;
- }
- break;
- case 'u':
- break;
- case 'x':
- case 'p':
- base = 16;
- break;
- default:
- format = error_str;
- }
- if (format == error_str)
- continue; /* Bad format specifier */
-
- /* Convert integer to string, starting at end of
- * buffer and working backwards. */
- vstr = intbuf + sizeof(intbuf) - 1;
- *(vstr) = '\0';
-
- if (!v)
- *(--vstr) = '0';
-
- while (v) {
- *(--vstr) = int_chars[v % base];
- v /= base;
- }
- if (is_negative)
- *(--vstr) = '-';
- }
-
- /* Copy string (or stringified integer) */
- vlen = strlen(vstr);
- while (vlen < pad_width && !is_left) {
- dropped_chars |= __tx_char(pad_zero ? '0' : ' ');
- vlen++;
- }
- while (*vstr)
- dropped_chars |= __tx_char(*vstr++);
- while (vlen < pad_width && is_left) {
- dropped_chars |= __tx_char(' ');
- vlen++;
- }
- }
- va_end(args);
-
- if (was_empty) {
- /* Re-enable the transmit interrupt, then forcibly trigger the
- * interrupt. This works around a hardware problem with the
- * UART where the FIFO only triggers the interrupt when its
- * threshold is _crossed_, not just met. */
- LM4_UART_IM(0) |= 0x20;
- task_trigger_irq(LM4_IRQ_UART0);
- }
-
- /* Successful if we consumed all output */
- return dropped_chars ? EC_ERROR_OVERFLOW : EC_SUCCESS;
-}
-
-
-void uart_flush_output(void)
-{
- /* Wait for buffer to empty */
- while (tx_buf_head != tx_buf_tail) {
- /* It's possible we're in some other interrupt, and the
- * previous context was doing a printf() or puts() but hadn't
- * enabled the UART interrupt. Check if the interrupt is
- * disabled, and if so, re-enable and trigger it. Note that
- * this check is inside the while loop, so we'll be safe even
- * if the context switches away from us to another partial
- * printf() and back. */
- if (!(LM4_UART_IM(0) & 0x20)) {
- LM4_UART_IM(0) |= 0x20;
- task_trigger_irq(LM4_IRQ_UART0);
- }
- }
-
- /* Wait for transmit FIFO empty */
- while (!(LM4_UART_FR(0) & 0x80)) {}
-}
-
-void uart_emergency_flush(void)
-{
- do {
- /* Copy output from buffer until TX fifo full
- * or output buffer empty
- */
- while (!(LM4_UART_FR(0) & 0x20) &&
- (tx_buf_head != tx_buf_tail)) {
- LM4_UART_DR(0) = tx_buf[tx_buf_tail];
- tx_buf_tail = TX_BUF_NEXT(tx_buf_tail);
- }
- /* Wait for transmit FIFO empty */
- while (!(LM4_UART_FR(0) & 0x80)) {}
- } while (tx_buf_head != tx_buf_tail);
-}
-
-
-void uart_flush_input(void)
-{
- /* Disable interrupts */
- task_disable_irq(LM4_IRQ_UART0);
-
- /* Call interrupt handler to empty the hardware FIFO */
- uart_0_interrupt();
-
- /* Clear the input buffer */
- rx_buf_tail = rx_buf_head;
-
- /* Re-enable interrupts */
- task_enable_irq(LM4_IRQ_UART0);
-}
-
-
-int uart_peek(int c)
-{
- int index = -1;
- int i = 0;
-
- /* Disable interrupts while we pull characters out, because the
- * interrupt handler can also modify the tail pointer. */
- task_disable_irq(LM4_IRQ_UART0);
-
- /* Call interrupt handler to empty the hardware FIFO. The minimum
- * FIFO trigger depth is 1/8 (2 chars), so this is the only way to
- * ensure we've pulled the very last character out of the FIFO. */
- uart_0_interrupt();
-
- for (i = rx_buf_tail; i != rx_buf_head; i = RX_BUF_NEXT(i)) {
- if (rx_buf[i] == c) {
- index = (RX_BUF_SIZE + i - rx_buf_tail) &
- (RX_BUF_SIZE - 1);
- break;
- }
- }
-
- /* Re-enable interrupts */
- task_enable_irq(LM4_IRQ_UART0);
-
- return index;
-}
-
-
-int uart_getc(void)
-{
- int c;
-
- /* Disable interrupts */
- task_disable_irq(LM4_IRQ_UART0);
-
- /* Call interrupt handler to empty the hardware FIFO */
- uart_0_interrupt();
-
- if (rx_buf_tail == rx_buf_head) {
- c = -1; /* No pending input */
- } else {
- c = rx_buf[rx_buf_tail];
- rx_buf_tail = RX_BUF_NEXT(rx_buf_tail);
- }
-
- /* Re-enable interrupts */
- task_enable_irq(LM4_IRQ_UART0);
-
- return c;
-}
-
-
-int uart_gets(char *dest, int size)
-{
- int got = 0;
- int c;
-
- /* Disable interrupts while we pull characters out, because the
- * interrupt handler can also modify the tail pointer. */
- task_disable_irq(LM4_IRQ_UART0);
-
- /* Call interrupt handler to empty the hardware FIFO */
- uart_0_interrupt();
-
- /* Read characters */
- while (got < size - 1 && rx_buf_tail != rx_buf_head) {
- c = rx_buf[rx_buf_tail];
- dest[got++] = c;
- rx_buf_tail = RX_BUF_NEXT(rx_buf_tail);
- if (c == '\n')
- break; /* Stop on newline */
- }
-
- /* Re-enable interrupts */
- task_enable_irq(LM4_IRQ_UART0);
-
- /* Null-terminate */
- dest[got] = '\0';
-
- /* Return the length we got */
- return got;
-}
-
-
/*****************************************************************************/
/* COMx functions */