1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
|
// See LICENSE for license details.
#include <string.h>
#include <stdarg.h>
#include <assert.h>
#include "uart16550.h"
#include "fdt.h"
volatile uint8_t* uart16550;
// some devices require a shifted register index
// (e.g. 32 bit registers instead of 8 bit registers)
static uint32_t uart16550_reg_shift;
static uint32_t uart16550_clock = 1843200; // a "common" base clock
#define UART_REG_QUEUE 0 // rx/tx fifo data
#define UART_REG_DLL 0 // divisor latch (LSB)
#define UART_REG_IER 1 // interrupt enable register
#define UART_REG_DLM 1 // divisor latch (MSB)
#define UART_REG_FCR 2 // fifo control register
#define UART_REG_LCR 3 // line control register
#define UART_REG_MCR 4 // modem control register
#define UART_REG_LSR 5 // line status register
#define UART_REG_MSR 6 // modem status register
#define UART_REG_SCR 7 // scratch register
#define UART_REG_STATUS_RX 0x01
#define UART_REG_STATUS_TX 0x20
// We cannot use the word DEFAULT for a parameter that cannot be overridden due to -Werror
#ifndef UART_DEFAULT_BAUD
#define UART_DEFAULT_BAUD 38400
#endif
void uart16550_putchar(uint8_t ch)
{
while ((uart16550[UART_REG_LSR << uart16550_reg_shift] & UART_REG_STATUS_TX) == 0);
uart16550[UART_REG_QUEUE << uart16550_reg_shift] = ch;
}
int uart16550_getchar()
{
if (uart16550[UART_REG_LSR << uart16550_reg_shift] & UART_REG_STATUS_RX)
return uart16550[UART_REG_QUEUE << uart16550_reg_shift];
return -1;
}
struct uart16550_scan
{
int compat;
uint64_t reg;
uint32_t reg_offset;
uint32_t reg_shift;
uint32_t clock_freq;
uint32_t baud;
};
static void uart16550_open(const struct fdt_scan_node *node, void *extra)
{
struct uart16550_scan *scan = (struct uart16550_scan *)extra;
memset(scan, 0, sizeof(*scan));
scan->baud = UART_DEFAULT_BAUD;
}
static void uart16550_prop(const struct fdt_scan_prop *prop, void *extra)
{
struct uart16550_scan *scan = (struct uart16550_scan *)extra;
// For the purposes of the boot loader, the 16750 is a superset of what 16550a provides
if (!strcmp(prop->name, "compatible") && ((fdt_string_list_index(prop, "ns16550a") != -1) || (fdt_string_list_index(prop, "ns16750") != -1))) {
scan->compat = 1;
} else if (!strcmp(prop->name, "reg")) {
fdt_get_address(prop->node->parent, prop->value, &scan->reg);
} else if (!strcmp(prop->name, "reg-shift")) {
scan->reg_shift = fdt_get_value(prop, 0);
} else if (!strcmp(prop->name, "reg-offset")) {
scan->reg_offset = fdt_get_value(prop, 0);
} else if (!strcmp(prop->name, "current-speed")) {
// This is the property that Linux uses
scan->baud = fdt_get_value(prop, 0);
} else if (!strcmp(prop->name, "clock-frequency")) {
scan->clock_freq = fdt_get_value(prop, 0);
}
}
static void uart16550_done(const struct fdt_scan_node *node, void *extra)
{
uint32_t clock_freq;
struct uart16550_scan *scan = (struct uart16550_scan *)extra;
if (!scan->compat || !scan->reg || uart16550) return;
if (scan->clock_freq != 0)
uart16550_clock = scan->clock_freq;
// if device tree doesn't supply a clock, fallback to default clock of 1843200
// Check for divide by zero
uint32_t divisor = uart16550_clock / (16 * (scan->baud ? scan->baud : UART_DEFAULT_BAUD));
// If the divisor is out of range, don't assert, set the rate back to the default
if (divisor >= 0x10000u)
divisor = uart16550_clock / (16 * UART_DEFAULT_BAUD);
uart16550 = (void*)((uintptr_t)scan->reg + scan->reg_offset);
uart16550_reg_shift = scan->reg_shift;
// http://wiki.osdev.org/Serial_Ports
uart16550[UART_REG_IER << uart16550_reg_shift] = 0x00; // Disable all interrupts
uart16550[UART_REG_LCR << uart16550_reg_shift] = 0x80; // Enable DLAB (set baud rate divisor)
uart16550[UART_REG_DLL << uart16550_reg_shift] = (uint8_t)divisor; // Set divisor (lo byte)
uart16550[UART_REG_DLM << uart16550_reg_shift] = (uint8_t)(divisor >> 8); // (hi byte)
uart16550[UART_REG_LCR << uart16550_reg_shift] = 0x03; // 8 bits, no parity, one stop bit
uart16550[UART_REG_FCR << uart16550_reg_shift] = 0xC7; // Enable FIFO, clear them, with 14-byte threshold
}
void query_uart16550(uintptr_t fdt)
{
struct fdt_cb cb;
struct uart16550_scan scan;
memset(&cb, 0, sizeof(cb));
cb.open = uart16550_open;
cb.prop = uart16550_prop;
cb.done = uart16550_done;
cb.extra = &scan;
fdt_scan(fdt, &cb);
}
|