summaryrefslogtreecommitdiffstats
path: root/uart.c
diff options
context:
space:
mode:
authorTomasz Kramkowski <tk@the-tk.com>2017-05-18 19:31:00 +0100
committerTomasz Kramkowski <tk@the-tk.com>2017-05-18 19:31:00 +0100
commit98f075af24dc60a5179511db7b84dbb62d8d80a1 (patch)
tree56f62b18f928b27086b07535576d7ae2d0b3c058 /uart.c
parentfee379e0042b8232ca1b106fd02134ce4451953e (diff)
downloadfmk-98f075af24dc60a5179511db7b84dbb62d8d80a1.tar.gz
fmk-98f075af24dc60a5179511db7b84dbb62d8d80a1.tar.xz
fmk-98f075af24dc60a5179511db7b84dbb62d8d80a1.zip
move uart implementation out of subdirectory
Diffstat (limited to 'uart.c')
-rw-r--r--uart.c238
1 files changed, 238 insertions, 0 deletions
diff --git a/uart.c b/uart.c
new file mode 100644
index 0000000..3ead880
--- /dev/null
+++ b/uart.c
@@ -0,0 +1,238 @@
+/*
+ * uart/uart.c -- hardware UART interface
+ *
+ * Copyright (C) 2016-2017 Tomasz Kramkowski <tk@the-tk.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#include <asm.h>
+#include <cm4.h>
+#include <reg/gpio.h>
+#include <reg/port.h>
+#include <reg/sim.h>
+#include <reg/uart.h>
+#include <stdarg.h>
+#include <stddef.h>
+
+#include "uart.h"
+
+enum {
+ SBR_VAL = 39,
+ BRFA_VAL = 2,
+ SRBSZ = 2048,
+ FIFOSZ = 8,
+};
+
+static struct {
+ unsigned char buf[SRBSZ];
+ int start, end;
+} send_rb = { .start = 0, .end = 0 };
+
+static inline size_t rb_store(void *_data, size_t size)
+{
+ unsigned char *data = _data;
+ size_t avail;
+ int start, end;
+
+ start = send_rb.start;
+ end = send_rb.end;
+ avail = (start + sizeof send_rb.buf - end) % sizeof send_rb.buf - 1;
+
+ if (size > avail) {
+ GPIOC_PSOR = BV(5);
+ size = avail;
+ }
+
+ for (size_t i = 0; i < size; i++)
+ send_rb.buf[(end + i) % sizeof send_rb.buf] = data[i];
+ send_rb.end = (end + size) % sizeof send_rb.buf;
+
+ /* Enable Transmission Interrupt */
+ CLI();
+ SET_BIT(UART0_C2, C2_TIE);
+ STI();
+
+ return size;
+}
+
+void uart_setup(void)
+{
+ /* Clock gating for UART0 */
+ SET_BIT(SIM_SCGC4, SCGC4_UART0);
+ /* Disable TX and RX */
+ SET_MASKED(UART0_C2, BV(C2_TE) | BV(C2_RE), 0);
+ /* Default settings */
+ UART0_C1 = 0;
+ /* SBR and BRFA for baud rate */
+ SET_MASKED(UART0_BDH, BDH_SBR_M, ((SBR_VAL & 0x1f00) >> 8) << BDH_SBR);
+ SET_MASKED(UART0_BDL, BDL_SBR_M, (SBR_VAL & 0xff) << BDL_SBR);
+ SET_MASKED(UART0_C4, C4_BRFA_M, BRFA_VAL << C4_BRFA);
+ /* flush and enable TX FIFO */
+ UART0_CFIFO |= BV(CFIFO_TXFLUSH);
+ UART0_PFIFO |= BV(PFIFO_TXFE);
+ /* set watermark at 1 entry */
+ SET_MASKED(UART0_TWFIFO, TWFIFO_TXWATER_M, 1 << TWFIFO_TXWATER);
+ /* NVIC enable interrupt */
+ ISR_CLRPEND(45);
+ INTPRI(45) = 0xf0;
+ ISR_SETENA(45);
+ /* enable correct ALT MUX on TX pin */
+ PORTB_PCR(17) = 3 << PCR_MUX;
+ /* enable TX */
+ SET_MASKED(UART0_C2, BV(C2_TE), BV(C2_TE));
+}
+
+void uart_putchar(int c)
+{
+ static unsigned char buf[128];
+ static size_t size = 0;
+
+ if (c == '\n')
+ uart_putchar('\r');
+
+ buf[size++] = c;
+
+ if (size >= sizeof buf || c == '\n') {
+ rb_store(buf, size);
+ size = 0;
+ }
+}
+
+void uart_puts(const char *s)
+{
+ for (size_t i = 0; s[i]; i++)
+ uart_putchar(s[i]);
+ uart_putchar('\n');
+}
+
+static inline void putul(unsigned long n, int base)
+{
+ static const char *digits = "0123456789abcdef";
+ int i;
+ char tmp[64];
+
+ if (base < 2 || base > 16)
+ return;
+
+ if (n == 0) {
+ uart_putchar('0');
+ return;
+ }
+ for (i = 0; n; i++) {
+ int res = n % base;
+ n = n / base;
+ tmp[i] = digits[res];
+ }
+
+ for (i -= 1; i >= 0; i--)
+ uart_putchar(tmp[i]);
+}
+
+static inline void putl(long n)
+{
+ int i;
+ char tmp[64];
+
+ if (n < 0) {
+ uart_putchar('-');
+ n *= -1;
+ }
+
+ if (n == 0) {
+ uart_putchar('0');
+ return;
+ }
+
+ for (i = 0; n; i++) {
+ int res = n % 10;
+ n = n / 10;
+ tmp[i] = '0' + res;
+ }
+
+ for (i -= 1; i >= 0; i--)
+ uart_putchar(tmp[i]);
+}
+
+void uart_printf(const char *fmt, ...)
+{
+ va_list ap;
+ const char *p;
+ int ival;
+ unsigned uival;
+ long lival;
+ unsigned long ulival;
+
+ va_start(ap, fmt);
+ for (p = fmt; *p; p++) {
+ if (*p != '%') {
+ uart_putchar(*p);
+ continue;
+ }
+ switch (*++p) {
+ case 'd': case 'i':
+ ival = va_arg(ap, int);
+ putl(ival);
+ break;
+ case 'o': case 'u': case 'x':
+ uival = va_arg(ap, unsigned);
+ switch (*p) {
+ case 'o': putul(uival, 8); break;
+ case 'u': putul(uival, 10); break;
+ case 'x': putul(uival, 16); break;
+ }
+ break;
+ case 'l':
+ switch (*++p) {
+ case 'd': case 'i':
+ lival = va_arg(ap, long);
+ putl(lival);
+ break;
+ case 'o': case 'u': case 'x':
+ ulival = va_arg(ap, unsigned long);
+ switch (*p) {
+ case 'o': putul(ulival, 8); break;
+ case 'u': putul(ulival, 10); break;
+ case 'x': putul(ulival, 16); break;
+ }
+ break;
+ }
+ }
+ }
+ va_end(ap);
+}
+
+void uart0_isr(void)
+{
+ int avail, start, end, tosend;
+
+ if (!GET_BIT(UART0_S1, S1_TDRE))
+ return;
+
+ avail = FIFOSZ - UART0_TCFIFO;
+ CLI();
+ start = send_rb.start;
+ end = send_rb.end;
+ tosend = (end + sizeof send_rb.buf - start) % sizeof send_rb.buf;
+ if (tosend == 0)
+ UNSET_BIT(UART0_C2, C2_TIE);
+ STI();
+
+ if (tosend > avail)
+ tosend = avail;
+
+ for (int i = 0; i < tosend; i++)
+ UART0_D = send_rb.buf[(start + i) % sizeof send_rb.buf];
+
+ send_rb.start = (start + tosend) % sizeof send_rb.buf;
+}