1// Copyright © 2020-2021 Sören Tempel
2//
3// This program is free software: you can redistribute it and/or modify
4// it under the terms of the GNU Affero General Public License as
5// published by the Free Software Foundation, either version 3 of the
6// License, or (at your option) any later version.
7//
8// This program is distributed in the hope that it will be useful, but
9// WITHOUT ANY WARRANTY; without even the implied warranty of
10// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11// Affero General Public License for more details.
12//
13// You should have received a copy of the GNU Affero General Public
14// License along with this program. If not, see <https://www.gnu.org/licenses/>.
15
16const gpio = @import("gpio.zig");
17const plic = @import("plic.zig");
18
19// TODO: Extract this value using the PRCI.
20const CLK_FREQ = 16 * 1000 * 1000; // 16 MHZ
21
22pub const ConfFlags = struct {
23 tx: bool,
24 rx: bool,
25 cnt: u3 = 0,
26 baud: u32 = 115200,
27};
28
29fn ctrlCount(watermark: u3) u32 {
30 return (@as(u32, watermark) & 0x07) << 16;
31}
32
33pub const Uart = struct {
34 base_addr: usize,
35 rx_pin: gpio.Pin,
36 tx_pin: gpio.Pin,
37 irq: plic.Irq,
38
39 const Reg = enum(usize) {
40 txfifo = 0x00,
41 rxfifo = 0x04,
42 txctrl = 0x08,
43 rxctrl = 0x0c,
44 ie = 0x10,
45 ip = 0x14,
46 div = 0x18,
47 };
48
49 pub const ie = struct {
50 txwm: bool,
51 rxwm: bool,
52 };
53
54 pub const FIFO_DEPTH: usize = 8;
55 pub const TXCTRL_ENABLE: u32 = 1;
56 pub const RXCTRL_ENABLE: u32 = 1;
57 pub const EMPTY_MASK: u32 = 1 << 31; // for txdata/rxdata
58 pub const IE_TXWM: u32 = 1 << 0;
59 pub const IE_RXWM: u32 = 1 << 1;
60
61 fn writeWord(self: Uart, reg: Reg, value: u32) void {
62 const ptr = @intToPtr(*volatile u32, self.base_addr + @enumToInt(reg));
63 ptr.* = value;
64 }
65
66 fn readWord(self: Uart, reg: Reg) u32 {
67 const ptr = @intToPtr(*volatile u32, self.base_addr + @enumToInt(reg));
68 return ptr.*;
69 }
70
71 pub fn confTx(self: Uart, watermark: u3) void {
72 self.writeWord(Reg.txctrl, TXCTRL_ENABLE | ctrlCount(watermark));
73 }
74
75 pub fn confRx(self: Uart, watermark: u3) void {
76 self.writeWord(Reg.rxctrl, RXCTRL_ENABLE | ctrlCount(watermark));
77 }
78
79 pub fn readIp(self: Uart) ie {
80 const r = self.readWord(Reg.ip);
81 return .{
82 .txwm = (r & IE_TXWM) != 0,
83 .rxwm = (r & IE_RXWM) != 0,
84 };
85 }
86
87 pub fn writeIe(self: Uart, txwm: bool, rxwm: bool) void {
88 var r: u32 = 0;
89 if (txwm)
90 r |= IE_TXWM;
91 if (rxwm)
92 r |= IE_RXWM;
93
94 self.writeWord(Reg.ie, r);
95 }
96
97 pub fn writeByte(self: Uart, value: u8) void {
98 self.writeWord(Reg.txfifo, value);
99 }
100
101 fn drainInput(self: Uart) void {
102 // Read until self.readByte() returns null
103 while (self.readByte()) |_| {}
104 }
105
106 pub fn readByte(self: Uart) ?u8 {
107 const rxdata = self.readWord(Reg.rxfifo);
108 if ((rxdata & EMPTY_MASK) != 0)
109 return null;
110 return @truncate(u8, rxdata);
111 }
112
113 pub fn isTxFull(self: Uart) bool {
114 const txdata = self.readWord(Reg.txfifo);
115 return (txdata & EMPTY_MASK) != 0;
116 }
117
118 pub fn init(self: Uart, ugpio: gpio.Gpio, conf: ConfFlags) void {
119 // Enable the UART at the given baud rate
120 self.writeWord(Reg.div, CLK_FREQ / conf.baud);
121
122 if (conf.tx)
123 self.confTx(conf.cnt);
124 if (conf.rx)
125 self.confRx(conf.cnt);
126
127 if (conf.tx)
128 ugpio.setIOFCtrl(self.tx_pin, 0);
129 if (conf.rx)
130 ugpio.setIOFCtrl(self.rx_pin, 0);
131
132 if (conf.rx)
133 self.drainInput();
134 }
135};