# console.ha -rw-r--r-- 1.2 KiB View raw
                                                                                
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
// I have prepared a copy of the appropriate file for blog readers to examine
// without disclosing the name of the programming language. I have also included
// a copy of one of the console drivers which makes use of this interface.
// Enjoy!
use io;
use rt::efi;

// The primary active console device.
export let console: *io::stream = null: *io::stream;

// Sets the active console device.
export fn setcons(cons: *io::stream) void = {
	console = cons;
};

export type syscons = struct {
	name: str,
	cons: *io::stream,
};

def MAX_CONS: size = 16;
let consbuf: [MAX_CONS]syscons = [syscons { cons = null: *io::stream, ... }...];

// List of known consoles. Do not edit.
export let consoles: []syscons = [];

@init fn cons_init() void = {
	consoles = consbuf[..0];
};

// Adds a console to the list of system consoles.
export fn register(name: str, cons: *io::stream) void = {
	if (len(consoles) >= MAX_CONS) {
		return;
	};
	static append(consoles, syscons {
		name = name,
		cons = cons,
	});
};

// Automatically select the best console and set it as the system console.
export fn auto() void = {
	// TODO: Check kernel command line and/or DT /chosen for the user's
	// preference
	if (len(consoles) == 0) {
		return;
	};
	setcons(consoles[0].cons);
};
# sifive.ha -rw-r--r-- 2.9 KiB View raw
                                                                                
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
123
124
125
126
use errors;
use fdt;
use fmt;
use io;
use rt;

def UART_TXFIFO_FULL: u32 = 0x80000000;
def UART_RXFIFO_EMPTY: u32 = 0x80000000;
def UART_RXFIFO_DATA: u32 = 0x000000ff;
def UART_TXCTRL_TXEN: u32 = 0x1;
def UART_RXCTRL_RXEN: u32 = 0x1;
def UART_IP_RXWM: u32 = 0x2;

export type uart_sifive = struct {
	txfifo: u32,
	rxfifo: u32,
	txctrl: u32,
	rxctrl: u32,
	ie: u32,
	ip: u32,
	div: u32,
};

export type sifive_console = struct {
	io::stream,
	clock: u64,
	namebuf: [8]u8, // "sifive" + up to 2 digits
	regs: *uart_sifive,
};

fn init_sifive(regs: *void) sifive_console = {
	let cons = sifive_console {
		writer = &sifive_console_write,
		regs = regs: *uart_sifive,
		...
	};
	rt::writeu32(&cons.regs.txctrl, UART_TXCTRL_TXEN);
	rt::writeu32(&cons.regs.rxctrl, UART_RXCTRL_RXEN);
	rt::writeu32(&cons.regs.ie, 0);
	return cons;
};

// Sets the baud rate for a sifive console device.
export fn sifive_set_baud(cons: *io::stream, clock: u64, baud: u64) void = {
	assert(cons.writer == &sifive_console_write);
	let cons = cons: *sifive_console;
	rt::writeu32(&cons.regs.div, min_clk_divisor(clock, baud));
};

fn sifive_console_write(
	cons: *io::stream,
	buf: const []u8,
) (size | io::error) = {
	assert(cons.writer == &sifive_console_write);
	let cons = cons: *sifive_console;
	let z = 0z;
	for (let i = 0z; i < len(buf); i += 1) {
		match (sifive_console_putc(cons, buf[i])) {
		case errors::busy =>
			// XXX: Might be better to bubble this up
			i -= 1;
			continue;
		case let n: size =>
			z += n;
		case let err: io::error =>
			return err;
		};
	};
	return z;
};

fn sifive_console_putc(cons: *sifive_console, c: u8) (size | io::error) = {
	let txstatus = rt::readu32(&cons.regs.txfifo);
	if (txstatus & UART_TXFIFO_FULL > 0) {
		return errors::busy;
	};
	rt::writeu32(&cons.regs.txfifo, c);
	return 1;
};

// Based on the uart driver in the SiFive FSBL
fn min_clk_divisor(in_freq: u64, max_target_hz: u64) u32 = {
	const quot = (in_freq + max_target_hz - 1) / max_target_hz;
	if (quot == 0) {
		// Avoids underflow
		return 0;
	};
	return (quot - 1): u32;
};

def SIFIVE_MAX_CONS: size = 16;
let sifive_consbuf: [SIFIVE_MAX_CONS]sifive_console = [
	sifive_console {
		regs = null: *uart_sifive,
		...
	}...
];
let sifive_consoles: []sifive_console = [];

fn sifive_probe(scan: *fdt::scanner, addr: uintptr) void = {
	if (len(sifive_consoles) >= SIFIVE_MAX_CONS) {
		return;
	};
	static append(sifive_consoles, init_sifive(addr: *void));
	const cons = &sifive_consoles[len(sifive_consoles) - 1];
	static let index: size = 0;
	register(fmt::bsprintf(cons.namebuf, "sifive{}", index), cons);
	index += 1;
};

const sifive_prober: fdt::prober = fdt::prober {
	name = "serial",
	compat = [
		"sifive,fu540-c000-uart0",
		"sifive,fu740-c000-uart",
		"sifive,uart0",
	],
	probe = &sifive_probe,
	next = null,
};

@init fn sifive_init() void = {
	sifive_consoles = sifive_consbuf[..0];
	fdt::registerprobe(&sifive_prober);
};