sylveos

Toy Operating System
Log | Files | Refs

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:
Api/devices/spi.zig | 216+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mpi/root.zig | 1+
Dpi/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 };