sylveos

Toy Operating System
Log | Files | Refs

commit 98fbf007c9bca4890db28e0c8ad734a00ff05694
parent c2118fc9db32521c05274210cc7d19c86e463c48
Author: Sylvia Ivory <git@sivory.net>
Date:   Thu,  5 Mar 2026 14:45:39 -0800

Add mini SPI

Diffstat:
Api/devices/aux.zig | 57+++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Api/devices/mini-spi.zig | 233+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 290 insertions(+), 0 deletions(-)

diff --git a/pi/devices/aux.zig b/pi/devices/aux.zig @@ -0,0 +1,57 @@ +const std = @import("std"); + +const mem = @import("../mem.zig"); +const register = @import("../register.zig"); + +pub const BASE_ADDRESS: usize = mem.BASE_ADDRESS + 0x0021_5000; +const AUX_IRQ: register.Register(Aux, .ReadOnly) = .init(BASE_ADDRESS + 0x00); +const AUX_ENABLES: register.Register(Aux, .ReadModifyWrite) = .init(BASE_ADDRESS + 0x04); + +const Aux = packed struct(u32) { + mini_uart: bool, + spi_1: bool, + spi_2: bool, + _reserved_31_3: u29, +}; + +pub fn enable_mini_uart() void { + var aux = AUX_ENABLES.get(); + aux.mini_uart = true; + AUX_ENABLES.set(aux); +} +pub fn disable_mini_uart() void { + var aux = AUX_ENABLES.get(); + aux.mini_uart = false; + AUX_ENABLES.set(aux); +} +pub fn has_irq_mini_uart() bool { + return AUX_IRQ.get().mini_uart; +} + +pub fn enable_spi_1() void { + var aux = AUX_ENABLES.get(); + aux.spi_1 = true; + AUX_ENABLES.set(aux); +} +pub fn disable_spi_1() void { + var aux = AUX_ENABLES.get(); + aux.spi_1 = false; + AUX_ENABLES.set(aux); +} +pub fn has_irq_spi_1() bool { + return AUX_IRQ.get().spi_1; +} + +pub fn enable_spi_2() void { + var aux = AUX_ENABLES.get(); + aux.spi_2 = true; + AUX_ENABLES.set(aux); +} +pub fn disable_spi_2() void { + var aux = AUX_ENABLES.get(); + aux.spi_1 = false; + AUX_ENABLES.set(aux); +} +pub fn has_irq_spi_2() bool { + return AUX_IRQ.get().spi_2; +} diff --git a/pi/devices/mini-spi.zig b/pi/devices/mini-spi.zig @@ -0,0 +1,233 @@ +const std = @import("std"); + +const aux = @import("./aux.zig"); +const gpio = @import("./gpio.zig"); +const mem = @import("../mem.zig"); +const register = @import("../register.zig"); + +const BASE_ADDRESS_1: usize = aux.BASE_ADDRESS + 0x80; +const BASE_ADDRESS_2: usize = aux.BASE_ADDRESS + 0xC0; + +const AUX_SPI1_CNTL0: register.Register(Control0, .ReadModifyWrite) = .init(BASE_ADDRESS_1 + 0x00); +const AUX_SPI1_CNTL1: register.Register(Control1, .ReadModifyWrite) = .init(BASE_ADDRESS_1 + 0x04); +const AUX_SPI1_STAT: register.Register(Status, .ReadModifyWrite) = .init(BASE_ADDRESS_1 + 0x08); +const AUX_SPI1_PEEK: register.Register(FiFo, .ReadOnly) = .init(BASE_ADDRESS_1 + 0x0C); +const AUX_SPI1_IO: register.Register(FiFo, .ReadModifyWrite) = .init(BASE_ADDRESS_1 + 0x20); + +const AUX_SPI2_CNTL0: register.Register(Control0, .ReadModifyWrite) = .init(BASE_ADDRESS_2 + 0x00); +const AUX_SPI2_CNTL1: register.Register(Control1, .ReadModifyWrite) = .init(BASE_ADDRESS_2 + 0x04); +const AUX_SPI2_STAT: register.Register(Status, .ReadModifyWrite) = .init(BASE_ADDRESS_2 + 0x08); +const AUX_SPI2_PEEK: register.Register(FiFo, .ReadOnly) = .init(BASE_ADDRESS_2 + 0x0C); +const AUX_SPI2_IO: register.Register(FiFo, .ReadModifyWrite) = .init(BASE_ADDRESS_2 + 0x20); + +// Unlike UART, only 1 pair +const SPI1_CE2 = 16; +const SPI1_CE1 = 17; +const SPI1_CE0 = 18; +const SPI1_MISO = 19; +const SPI1_MOSI = 20; +const SPI1_SCLK = 21; + +const SPI2_CE2 = 40; +const SPI2_CE1 = 41; +const SPI2_CE0 = 42; +const SPI2_MISO = 43; +const SPI2_MOSI = 44; +const SPI2_SCLK = 45; + +const Control0 = packed struct(u32) { + pub const DOutHold = enum(u2) { + None = 0b00, + @"1" = 0b01, + @"4" = 0b10, + @"7" = 0b11, + }; + pub const ChipSelect = packed struct(u3) { + @"0": bool, + @"1": bool, + @"2": bool, + }; + shift_length: u6, + shift_out_ms: bool, + invert_spi_clk: bool, + out_rising: bool, + clear_fifos: bool, + in_rising: bool, + enable: bool, + dout_hold: DOutHold, + variable_width: bool, + variable_cs: bool, + post_input: bool, + chip_select: ChipSelect, + speed: u12, +}; +const Control1 = packed struct(u32) { + keep_input: bool, + shift_in_ms: bool, + _reserved_5_2: u4 = 0, + done_irq: bool, + tx_empty_irq: bool, + cs_high_time: u3, + _reserved_31_11: u11 = 0, +}; +const Status = packed struct(u32) { + bit_count: u6, + busy: bool, + rx_empty: bool, + rx_full: bool, + tx_empty: bool, + tx_full: bool, + _reserved_15_5: u16 = 0, + rx_fill_level: u4, + tx_fill_level: u4, +}; +const FiFo = packed struct(u32) { + data: u16, + _reserved_16_31: u16 = 0, +}; + +pub const SpiDevice = enum { + SPI1, + SPI2, +}; + +pub const SpiConfig = struct { + speed: u12, + chip_select: Control0.ChipSelect = .{ + .@"0" = false, + .@"1" = false, + .@"2" = false, + }, + shift_length: u6 = 8, + out_rising: bool = true, + in_rising: bool = false, + invert_clk: bool = false, + shift_out_ms: bool = true, + shift_in_ms: bool = false, + cs_high_time: u3 = 0, +}; + +pub fn init(device: SpiDevice, config: SpiConfig) void { + const cntl0_reg = switch (device) { + .SPI1 => AUX_SPI1_CNTL0, + .SPI2 => AUX_SPI2_CNTL0, + }; + const cntl1_reg = switch (device) { + .SPI1 => AUX_SPI1_CNTL1, + .SPI2 => AUX_SPI2_CNTL1, + }; + + switch (device) { + .SPI1 => { + gpio.fn_sel(SPI1_MISO, .alt_fn_4) catch {}; + gpio.set_pull(SPI1_MISO, .Up) catch {}; + + gpio.fn_sel(SPI1_MOSI, .alt_fn_4) catch {}; + gpio.set_pull(SPI1_MOSI, .Up) catch {}; + + gpio.fn_sel(SPI1_SCLK, .alt_fn_4) catch {}; + if (config.invert_clk) { + gpio.set_pull(SPI1_SCLK, .Up) catch {}; + } else { + gpio.set_pull(SPI1_SCLK, .Down) catch {}; + } + + gpio.fn_sel(SPI1_CE0, .alt_fn_4) catch {}; + gpio.set_pull(SPI1_CE2, .Up) catch {}; + + gpio.fn_sel(SPI1_CE1, .alt_fn_4) catch {}; + gpio.set_pull(SPI1_CE2, .Up) catch {}; + + gpio.fn_sel(SPI1_CE2, .alt_fn_4) catch {}; + gpio.set_pull(SPI1_CE2, .Up) catch {}; + + aux.enable_spi_1(); + }, + .SPI2 => { + gpio.fn_sel(SPI2_MISO, .alt_fn_4) catch {}; + gpio.set_pull(SPI2_MISO, .Up) catch {}; + + gpio.fn_sel(SPI2_MOSI, .alt_fn_4) catch {}; + gpio.set_pull(SPI2_MOSI, .Up) catch {}; + + gpio.fn_sel(SPI2_SCLK, .alt_fn_4) catch {}; + if (config.invert_clk) { + gpio.set_pull(SPI2_SCLK, .Up) catch {}; + } else { + gpio.set_pull(SPI2_SCLK, .Down) catch {}; + } + + gpio.fn_sel(SPI2_CE0, .alt_fn_4) catch {}; + gpio.set_pull(SPI2_CE2, .Up) catch {}; + + gpio.fn_sel(SPI2_CE1, .alt_fn_4) catch {}; + gpio.set_pull(SPI2_CE1, .Up) catch {}; + + gpio.fn_sel(SPI2_CE2, .alt_fn_4) catch {}; + gpio.set_pull(SPI2_CE2, .Up) catch {}; + + aux.enable_spi_2(); + }, + } + + cntl0_reg.set(.{ + .shift_length = config.shift_length, + .shift_out_ms = config.shift_out_ms, + .invert_spi_clk = config.invert_clk, + .out_rising = config.out_rising, + .clear_fifos = true, + .in_rising = config.in_rising, + .enable = true, + .dout_hold = .None, + .variable_width = false, + .variable_cs = false, + .post_input = false, + .chip_select = config.chip_select, + .speed = config.speed, + }); + + cntl1_reg.set(.{ + .keep_input = false, + .shift_in_ms = config.shift_in_ms, + .done_irq = false, + .tx_empty_irq = false, + .cs_high_time = config.cs_high_time, + }); +} + +pub fn status(device: SpiDevice) Status { + switch (device) { + .SPI1 => AUX_SPI1_STAT.get(), + .SPI2 => AUX_SPI2_STAT.get(), + } +} + +pub fn write_polled(device: SpiDevice, data: []const u8) void { + const io_reg = switch (device) { + .SPI1 => AUX_SPI1_IO, + .SPI2 => AUX_SPI2_IO, + }; + + for (data) |b| { + while (status(device).tx_full) {} + io_reg.set(.{ + .data = @as(u16, b), + }); + } +} + +pub fn read_polled(device: SpiDevice, data: []u8) void { + const io_reg = switch (device) { + .SPI1 => AUX_SPI1_IO, + .SPI2 => AUX_SPI2_IO, + }; + + for (0..data.len) |i| { + while (status(device).rx_empty) {} + data[i] = @truncate(io_reg.get().data); + } +} + +pub fn flush(device: SpiDevice) void { + while (status(device).busy) {} +}