1// See LICENSE for license details.
2
3#include <string.h>
4#include "uart16750.h"
5#include "fdt.h"
6
7volatile uint32_t* uart16750;
8
9#define UART_REG_QUEUE     0
10#define UART_REG_LINESTAT  5
11#define UART_REG_STATUS_RX 0x01
12#define UART_REG_STATUS_TX 0x20
13
14void uart16750_putchar(uint8_t ch)
15{
16  while ((uart16750[UART_REG_LINESTAT] & UART_REG_STATUS_TX) == 0);
17  uart16750[UART_REG_QUEUE] = ch;
18}
19
20int uart16750_getchar()
21{
22  if (uart16750[UART_REG_LINESTAT] & UART_REG_STATUS_RX)
23    return uart16750[UART_REG_QUEUE];
24  return -1;
25}
26
27struct uart16750_scan
28{
29  int compat;
30  uint64_t reg;
31  uint32_t freq;
32  uint32_t baud;
33};
34
35static void uart16750_open(const struct fdt_scan_node *node, void *extra)
36{
37  struct uart16750_scan *scan = (struct uart16750_scan *)extra;
38  memset(scan, 0, sizeof(*scan));
39}
40
41static void uart16750_prop(const struct fdt_scan_prop *prop, void *extra)
42{
43  struct uart16750_scan *scan = (struct uart16750_scan *)extra;
44  if (!strcmp(prop->name, "compatible") && !strcmp((const char*)prop->value, "ns16750")) {
45    scan->compat = 1;
46  } else if (!strcmp(prop->name, "reg")) {
47    fdt_get_address(prop->node->parent, prop->value, &scan->reg);
48  } else if (!strcmp(prop->name, "clock-frequency")) {
49    fdt_get_value(prop->value, &scan->freq);
50  } else if (!strcmp(prop->name, "current-speed")) {
51    fdt_get_value(prop->value, &scan->baud);
52  }
53}
54
55static void uart16750_done(const struct fdt_scan_node *node, void *extra)
56{
57  struct uart16750_scan *scan = (struct uart16750_scan *)extra;
58  if (!scan->compat || !scan->reg || uart16750) return;
59
60  uint32_t divisor = scan->freq / (scan->baud << 4);
61  uart16750 = (void*)(uintptr_t)scan->reg;
62  // http://wiki.osdev.org/Serial_Ports
63  uart16750[1] = 0x00;    // Disable all interrupts
64  uart16750[3] = 0x80;    // Enable DLAB (set baud rate divisor)
65  uart16750[0] = divisor & 0xFF;
66  uart16750[1] = (divisor >> 8) & 0xFF; // (hi byte)
67  uart16750[3] = 0x03;    // 8 bits, no parity, one stop bit
68  uart16750[2] = 0xC7;    // Enable FIFO, clear them, with 14-byte threshold
69}
70
71void query_uart16750(uintptr_t fdt)
72{
73  struct fdt_cb cb;
74  struct uart16750_scan scan;
75
76  memset(&cb, 0, sizeof(cb));
77  cb.open = uart16750_open;
78  cb.prop = uart16750_prop;
79  cb.done = uart16750_done;
80  cb.extra = &scan;
81
82  fdt_scan(fdt, &cb);
83}
84