commit bc6e627ad26ad98a63c5686c1402177859859b0e
parent 40ad921dbc21702f28dc56a85920c615333e0aa8
Author: Sylvia Ivory <git@sivory.net>
Date: Tue, 3 Mar 2026 13:27:14 -0800
Add SPI0 to pi exports
Diffstat:
| A | pi/devices/spi.zig | | | 216 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| M | pi/root.zig | | | 1 | + |
| D | pi/spi.zig | | | 216 | ------------------------------------------------------------------------------- |
3 files changed, 217 insertions(+), 216 deletions(-)
diff --git a/pi/devices/spi.zig b/pi/devices/spi.zig
@@ -0,0 +1,216 @@
+const std = @import("std");
+
+const mem = @import("../mem.zig");
+const register = @import("../register.zig");
+
+const BASE_ADDRESS: usize = mem.BASE_ADDRESS + 0x0020_4000;
+const CS: register.Register(MasterControlStatus, .ReadModifyWrite) = .init(BASE_ADDRESS + 0x00);
+const FIFO: register.Register(FiFo, .ReadModifyWrite) = .init(BASE_ADDRESS + 0x04);
+const CLK: register.Register(Clk, .ReadModifyWrite) = .init(BASE_ADDRESS + 0x08);
+const DATA_LENGTH: register.Register(DataLength, .ReadModifyWrite) = .init(BASE_ADDRESS + 0x0C);
+const LTOH: register.Register(Ltoh, .ReadModifyWrite) = .init(BASE_ADDRESS + 0x10);
+const DC: register.Register(Dc, .ReadModifyWrite) = .init(BASE_ADDRESS + 0x14);
+
+const MasterControlStatus = packed struct(u32) {
+ pub const ChipSelect = enum(u2) {
+ @"0" = 0b00,
+ @"1" = 0b01,
+ @"2" = 0b10,
+ };
+
+ pub const ClockPhase = enum(u1) {
+ Middle = 0,
+ Beginning = 1,
+ };
+
+ pub const ClockPolarity = enum(u1) {
+ Low = 0,
+ High = 1,
+ };
+
+ pub const FiFoClear = packed struct(u2) {
+ clear_tx: bool,
+ clear_rx: bool,
+ };
+
+ pub const ChipSelectPolarity = enum(u1) {
+ SelectActiveLow = 0,
+ SelectActiveHigh = 1,
+ };
+
+ pub const Ren = enum(u1) {
+ Write = 0,
+ Read = 1,
+ };
+
+ pub const Len = enum(u1) {
+ SPIMaster = 0,
+ LoSSIMasster = 1,
+ };
+
+ pub const TransferProgress = enum(u1) {
+ InProgress = 0,
+ Complete = 1,
+ };
+
+ chip_select: ChipSelect = .@"0",
+ clock_phase: ClockPhase = .Middle,
+ clock_polarity: ClockPolarity = .Low,
+ clear_fifo_clear: FiFoClear = .{ .clear_tx = false, .clear_rx = false },
+ // 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,
+ interrupt_on_rx: bool = false,
+ automatically_deassert_chip_select: bool = false,
+ ren: Ren = .Write,
+ len_lossi_enable: Len = .SPIMaster,
+ _unused_14: u1 = 0,
+ _unused_15: u1 = 0,
+ transfer_progress: TransferProgress = .InProgress,
+ rxd_contains_data: bool = false,
+ txd_has_space: bool = true,
+ rxr_is_full: bool = false,
+ rxf_is_full: bool = false,
+ 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,
+};
+
+const FiFo = packed union {
+ bytes: [@sizeOf(u32) / @sizeOf(u8)]u8,
+ long: u32,
+};
+
+const Clk = packed struct(u32) {
+ clock_divider: u16 = 0,
+ _reserved_31_16: u16 = 0,
+};
+
+const DataLength = packed struct(u32) {
+ length: u32,
+ _reserved_31_16: u32,
+};
+
+const Ltoh = packed struct(u32) {
+ toh: u4,
+ _reserved_31_4: u28,
+};
+
+const Dc = packed struct(u32) {
+ dma_write_request_threshold: u8,
+ dma_write_panic_threshold: u8,
+ dma_read_request_threshold: u8,
+ dma_read_panic_threshold: u8,
+};
+
+// Polled:
+// 1. Set CS(MasterControlStatus), CPOL(chip_select_polarity), CPHA(clock_phase)
+// as required and set TA(transfer_active) = 1
+// 2. Poll TXD(txd_has_space) writing bytes to SPI_FIFO(FiFo), RXD(rxd_contains_data)
+// 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 };
diff --git a/pi/root.zig b/pi/root.zig
@@ -19,6 +19,7 @@ pub const devices = struct {
pub const sw_uart = @import("./devices/sw-uart.zig");
pub const timer = @import("./devices/timer.zig");
pub const mailbox = @import("./devices/mailbox.zig");
+ pub const spi = @import("./devices/spi.zig");
};
pub inline fn cycle_counter_init() void {
diff --git a/pi/spi.zig b/pi/spi.zig
@@ -1,216 +0,0 @@
-const std = @import("std");
-
-const mem = @import("./mem.zig");
-const register = @import("./register.zig");
-
-const BASE_ADDRESS: usize = mem.BASE_ADDRESS + 0x0020_4000;
-const CS: register.Register(MasterControlStatus, .ReadModifyWrite) = .init(BASE_ADDRESS + 0x00);
-const FIFO: register.Register(FiFo, .ReadModifyWrite) = .init(BASE_ADDRESS + 0x04);
-const CLK: register.Register(Clk, .ReadModifyWrite) = .init(BASE_ADDRESS + 0x08);
-const DATA_LENGTH: register.Register(DataLength, .ReadModifyWrite) = .init(BASE_ADDRESS + 0x0C);
-const LTOH: register.Register(Ltoh, .ReadModifyWrite) = .init(BASE_ADDRESS + 0x10);
-const DC: register.Register(Dc, .ReadModifyWrite) = .init(BASE_ADDRESS + 0x14);
-
-const MasterControlStatus = packed struct(u32) {
- pub const ChipSelect = enum(u2) {
- @"0" = 0b00,
- @"1" = 0b01,
- @"2" = 0b10,
- };
-
- pub const ClockPhase = enum(u1) {
- Middle = 0,
- Beginning = 1,
- };
-
- pub const ClockPolarity = enum(u1) {
- Low = 0,
- High = 1,
- };
-
- pub const FiFoClear = packed struct(u2) {
- clear_tx: bool,
- clear_rx: bool,
- };
-
- pub const ChipSelectPolarity = enum(u1) {
- SelectActiveLow = 0,
- SelectActiveHigh = 1,
- };
-
- pub const Ren = enum(u1) {
- Write = 0,
- Read = 1,
- };
-
- pub const Len = enum(u1) {
- SPIMaster = 0,
- LoSSIMasster = 1,
- };
-
- pub const TransferProgress = enum(u1) {
- InProgress = 0,
- Complete = 1,
- };
-
- chip_select: ChipSelect = .@"0",
- clock_phase: ClockPhase = .Middle,
- clock_polarity: ClockPolarity = .Low,
- clear_fifo_clear: FiFoClear = .{ .clear_tx = false, .clear_rx = false },
- // 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,
- interrupt_on_rx: bool = false,
- automatically_deassert_chip_select: bool = false,
- ren: Ren = .Write,
- len_lossi_enable: Len = .SPIMaster,
- _unused_14: u1 = 0,
- _unused_15: u1 = 0,
- transfer_progress: TransferProgress = .InProgress,
- rxd_contains_data: bool = false,
- txd_has_space: bool = true,
- rxr_is_full: bool = false,
- rxf_is_full: bool = false,
- 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,
-};
-
-const FiFo = packed union {
- bytes: [@sizeOf(u32) / @sizeOf(u8)]u8,
- long: u32,
-};
-
-const Clk = packed struct(u32) {
- clock_divider: u16 = 0,
- _reserved_31_16: u16 = 0,
-};
-
-const DataLength = packed struct(u32) {
- length: u32,
- _reserved_31_16: u32,
-};
-
-const Ltoh = packed struct(u32) {
- toh: u4,
- _reserved_31_4: u28,
-};
-
-const Dc = packed struct(u32) {
- dma_write_request_threshold: u8,
- dma_write_panic_threshold: u8,
- dma_read_request_threshold: u8,
- dma_read_panic_threshold: u8,
-};
-
-// Polled:
-// 1. Set CS(MasterControlStatus), CPOL(chip_select_polarity), CPHA(clock_phase)
-// as required and set TA(transfer_active) = 1
-// 2. Poll TXD(txd_has_space) writing bytes to SPI_FIFO(FiFo), RXD(rxd_contains_data)
-// 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 };