1// Copyright © 2020-2021 Sören Tempel2//3// This program is free software: you can redistribute it and/or modify4// it under the terms of the GNU Affero General Public License as5// published by the Free Software Foundation, either version 3 of the6// License, or (at your option) any later version.7//8// This program is distributed in the hope that it will be useful, but9// WITHOUT ANY WARRANTY; without even the implied warranty of10// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU11// Affero General Public License for more details.12//13// You should have received a copy of the GNU Affero General Public14// License along with this program. If not, see <https://www.gnu.org/licenses/>.1516const gpio = @import("gpio.zig");17const plic = @import("plic.zig");1819// TODO: Extract this value using the PRCI.20const CLK_FREQ = 16 * 1000 * 1000; // 16 MHZ2122pub const ConfFlags = struct {23 tx: bool,24 rx: bool,25 cnt: u3 = 0,26 baud: u32 = 115200,27};2829fn ctrlCount(watermark: u3) u32 {30 return (@as(u32, watermark) & 0x07) << 16;31}3233pub const Uart = struct {34 base_addr: usize,35 rx_pin: gpio.Pin,36 tx_pin: gpio.Pin,37 irq: plic.Irq,3839 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 };4849 pub const ie = struct {50 txwm: bool,51 rxwm: bool,52 };5354 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/rxdata58 pub const IE_TXWM: u32 = 1 << 0;59 pub const IE_RXWM: u32 = 1 << 1;6061 fn writeWord(self: Uart, reg: Reg, value: u32) void {62 const ptr = @intToPtr(*volatile u32, self.base_addr + @enumToInt(reg));63 ptr.* = value;64 }6566 fn readWord(self: Uart, reg: Reg) u32 {67 const ptr = @intToPtr(*volatile u32, self.base_addr + @enumToInt(reg));68 return ptr.*;69 }7071 pub fn confTx(self: Uart, watermark: u3) void {72 self.writeWord(Reg.txctrl, TXCTRL_ENABLE | ctrlCount(watermark));73 }7475 pub fn confRx(self: Uart, watermark: u3) void {76 self.writeWord(Reg.rxctrl, RXCTRL_ENABLE | ctrlCount(watermark));77 }7879 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 }8687 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;9394 self.writeWord(Reg.ie, r);95 }9697 pub fn writeByte(self: Uart, value: u8) void {98 self.writeWord(Reg.txfifo, value);99 }100101 fn drainInput(self: Uart) void {102 // Read until self.readByte() returns null103 while (self.readByte()) |_| {}104 }105106 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 }112113 pub fn isTxFull(self: Uart) bool {114 const txdata = self.readWord(Reg.txfifo);115 return (txdata & EMPTY_MASK) != 0;116 }117118 pub fn init(self: Uart, ugpio: gpio.Gpio, conf: ConfFlags) void {119 // Enable the UART at the given baud rate120 self.writeWord(Reg.div, CLK_FREQ / conf.baud);121122 if (conf.tx)123 self.confTx(conf.cnt);124 if (conf.rx)125 self.confRx(conf.cnt);126127 if (conf.tx)128 ugpio.setIOFCtrl(self.tx_pin, 0);129 if (conf.rx)130 ugpio.setIOFCtrl(self.rx_pin, 0);131132 if (conf.rx)133 self.drainInput();134 }135};