commit c4a1d1ddeedb1078b6b1a9401d5f11dd289c5878
parent de71629ce95ed08bd9c6d124e22db286fd3be1e6
Author: Sylvia Ivory <git@sivory.net>
Date: Tue, 3 Mar 2026 13:22:59 -0800
Add polled I/O
Diffstat:
| M | pi/spi.zig | | | 110 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---- |
1 file changed, 105 insertions(+), 5 deletions(-)
diff --git a/pi/spi.zig b/pi/spi.zig
@@ -34,7 +34,7 @@ const MasterControlStatus = packed struct(u32) {
};
pub const ChipSelectPolarity = enum(u1) {
- SelectActoveLow = 0,
+ SelectActiveLow = 0,
SelectActiveHigh = 1,
};
@@ -57,7 +57,11 @@ const MasterControlStatus = packed struct(u32) {
clock_phase: ClockPhase = .Middle,
clock_polarity: ClockPolarity = .Low,
clear_fifo_clear: FiFoClear = .{ .clear_tx = false, .clear_rx = false },
- chip_select_polarity: ChipSelectPolarity = .SelectActoveLow,
+ // elinux bcm2835 errata:
+ // "There is a CSPOL bit described here, wereas there are also CSPOL[0,1,2]
+ // described on page 153. How do these combine???"
+ // Linux sets both this and chip_select_N together
+ chip_select_polarity: ChipSelectPolarity = .SelectActiveLow,
transfer_active: bool = false,
dma_enable: bool = false,
interrupt_on_done: bool = false,
@@ -72,9 +76,9 @@ const MasterControlStatus = packed struct(u32) {
txd_has_space: bool = true,
rxr_is_full: bool = false,
rxf_is_full: bool = false,
- chip_select_0: ChipSelectPolarity = .SelectActoveLow,
- chip_select_1: ChipSelectPolarity = .SelectActoveLow,
- chip_select_2: ChipSelectPolarity = .SelectActoveLow,
+ chip_select_0: ChipSelectPolarity = .SelectActiveLow,
+ chip_select_1: ChipSelectPolarity = .SelectActiveLow,
+ chip_select_2: ChipSelectPolarity = .SelectActiveLow,
dma_enable_lossi: bool = false,
lossi_write_u32: bool = false,
_reserved_31_26: u6,
@@ -114,3 +118,99 @@ const Dc = packed struct(u32) {
// reading bytes from SPI_FIFO(FiFo) until all data is written
// 3. Poll DONE(transfer_progress) until it goes to 1
// 4. Set TA(transfer_active) = 0
+pub const SPIConfig = struct {
+ chip_select: MasterControlStatus.ChipSelect,
+ chip_select_polarity: MasterControlStatus.ChipSelectPolarity,
+ clock_polarity: MasterControlStatus.ClockPolarity,
+ clock_phase: MasterControlStatus.ClockPhase,
+};
+
+pub fn set_config(config: SPIConfig) void {
+ const cs: MasterControlStatus = .{
+ .chip_select = config.chip_select,
+ .chip_select_polarity = config.chip_select_polarity,
+ .clock_polarity = config.clock_polarity,
+ .clock_phase = config.clock_phase,
+ };
+
+ switch (config.chip_select) {
+ .@"0" => cs.chip_select_0 = config.chip_select,
+ .@"1" => cs.chip_select_1 = config.chip_select,
+ .@"2" => cs.chip_select_2 = config.chip_select,
+ }
+
+ CS.set(cs);
+}
+
+pub fn write_polled(bytes: []const u8) void {
+ {
+ var cs = CS.get();
+ cs.transfer_active = true;
+ CS.set(cs);
+ }
+
+ for (bytes) |byte| {
+ while (!CS.get().txd_has_space) {}
+ FIFO.set(@as(u32, byte));
+ }
+
+ // We need to flush before clearing TA
+ flush();
+
+ {
+ var cs = CS.get();
+ cs.transfer_active = false;
+ CS.set(cs);
+ }
+}
+
+pub fn read_polled(dst: []u8) void {
+ for (0..dst.len) |i| {
+ while (!CS.get().rxd_contains_data) {}
+ dst[i] = @truncate(FIFO.get());
+ }
+}
+
+pub fn flush() void {
+ while (CS.get().transfer_progress == .InProgress) {}
+}
+
+fn writer_drain(io_w: *std.Io.Writer, data: []const []const u8, splat: usize) !usize {
+ // We don't care about getting the "parent"
+ _ = io_w;
+ _ = splat;
+
+ write_polled(data[0]);
+
+ return data[0].len;
+}
+
+fn writer_flush(io_w: *std.Io.Writer) !void {
+ // We don't care about getting the "parent"
+ _ = io_w;
+
+ flush();
+}
+
+const writer_vtable: std.Io.Writer.VTable = .{ .drain = writer_drain, .flush = writer_flush };
+pub var writer = std.Io.Writer{ .buffer = undefined, .vtable = &writer_vtable };
+
+fn stream(io_r: *std.Io.Reader, io_w: *std.Io.Writer, limit: std.Io.Limit) !usize {
+ // We don't care about getting the "parent"
+ _ = io_r;
+
+ if (limit.toInt()) |max| {
+ for (0..max) |_| {
+ var buffer: [1]u8 = .{0};
+ read_polled(&buffer);
+ try io_w.writeByte(buffer[0]);
+ }
+
+ return max;
+ } else {
+ return std.Io.Reader.StreamError.ReadFailed;
+ }
+}
+
+const reader_vtable: std.Io.Reader.VTable = .{ .stream = stream };
+pub var reader = std.Io.Reader{ .buffer = undefined, .seek = 0, .end = 0, .vtable = &reader_vtable };