summaryrefslogtreecommitdiffstats
path: root/uart
diff options
context:
space:
mode:
authorTomasz Kramkowski <tk@the-tk.com>2017-03-26 21:45:42 +0100
committerTomasz Kramkowski <tk@the-tk.com>2017-03-26 21:45:42 +0100
commit03f33481c42954ec118d5dd92703b2b2208f4a3a (patch)
tree61ff51b02413e2a7441bf340f9afd8510af29890 /uart
parent6f2090ae7fca871512258623d2bbd5484716a706 (diff)
downloadfmk-03f33481c42954ec118d5dd92703b2b2208f4a3a.tar.gz
fmk-03f33481c42954ec118d5dd92703b2b2208f4a3a.tar.xz
fmk-03f33481c42954ec118d5dd92703b2b2208f4a3a.zip
uart support
Diffstat (limited to 'uart')
-rw-r--r--uart/uart.c213
-rw-r--r--uart/uart.h4
2 files changed, 217 insertions, 0 deletions
diff --git a/uart/uart.c b/uart/uart.c
index 031cf74..2e5d68d 100644
--- a/uart/uart.c
+++ b/uart/uart.c
@@ -1,5 +1,218 @@
+#include <cm4.h>
+#include <reg/gpio.h>
+#include <reg/port.h>
+#include <reg/sim.h>
+#include <reg/uart.h>
+#include <stddef.h>
+#include <stdarg.h>
+
#include "uart.h"
+#include "../asm.h"
+
+enum {
+ SBR_VAL = 39,
+ BRFA_VAL = 2,
+ SRBSZ = 1024,
+ 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)
+ size = avail;
+
+ for (int 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;
}
diff --git a/uart/uart.h b/uart/uart.h
index 984d704..2733bb7 100644
--- a/uart/uart.h
+++ b/uart/uart.h
@@ -2,5 +2,9 @@
#define UART_UART_H
void uart_setup(void);
+void uart_putchar(int c);
+void uart_printf(const char *fmt, ...);
+void uart_puts(const char *s);
+void uart0_isr(void);
#endif /* UART_UART_H */