sylveos

Toy Operating System
Log | Files | Refs

spi.zig (6028B)


      1 const std = @import("std");
      2 
      3 const mem = @import("../mem.zig");
      4 const gpio = @import("./gpio.zig");
      5 const register = @import("../register.zig");
      6 
      7 const BASE_ADDRESS: usize = mem.BASE_ADDRESS + 0x0020_4000;
      8 const CS: register.Register(MasterControlStatus, .ReadModifyWrite) = .init(BASE_ADDRESS + 0x00);
      9 const FIFO: register.Register(u32, .ReadModifyWrite) = .init(BASE_ADDRESS + 0x04);
     10 const CLK: register.Register(Clk, .ReadModifyWrite) = .init(BASE_ADDRESS + 0x08);
     11 const DATA_LENGTH: register.Register(DataLength, .ReadModifyWrite) = .init(BASE_ADDRESS + 0x0C);
     12 const LTOH: register.Register(Ltoh, .ReadModifyWrite) = .init(BASE_ADDRESS + 0x10);
     13 const DC: register.Register(Dc, .ReadModifyWrite) = .init(BASE_ADDRESS + 0x14);
     14 
     15 const MasterControlStatus = packed struct(u32) {
     16     pub const ChipSelect = enum(u2) {
     17         @"0" = 0b00,
     18         @"1" = 0b01,
     19         @"2" = 0b10,
     20     };
     21 
     22     pub const ClockPhase = enum(u1) {
     23         Middle = 0,
     24         Beginning = 1,
     25     };
     26 
     27     pub const ClockPolarity = enum(u1) {
     28         Low = 0,
     29         High = 1,
     30     };
     31 
     32     pub const FiFoClear = packed struct(u2) {
     33         clear_tx: bool,
     34         clear_rx: bool,
     35     };
     36 
     37     pub const ChipSelectPolarity = enum(u1) {
     38         ActiveLow = 0,
     39         ActiveHigh = 1,
     40     };
     41 
     42     pub const Ren = enum(u1) {
     43         Write = 0,
     44         Read = 1,
     45     };
     46 
     47     pub const Len = enum(u1) {
     48         SPIMaster = 0,
     49         LoSSIMaster = 1,
     50     };
     51 
     52     chip_select: ChipSelect = .@"0",
     53     clock_phase: ClockPhase = .Middle,
     54     clock_polarity: ClockPolarity = .Low,
     55     clear_fifo: FiFoClear = .{ .clear_tx = false, .clear_rx = false },
     56     // elinux bcm2835 errata:
     57     // "There is a CSPOL bit described here, wereas there are also CSPOL[0,1,2]
     58     // described on page 153. How do these combine???"
     59     // Linux sets both this and chip_select_N together
     60     chip_select_polarity: ChipSelectPolarity = .ActiveLow,
     61     transfer_active: bool = false,
     62     dma_enable: bool = false,
     63     interrupt_on_done: bool = false,
     64     interrupt_on_rx: bool = false,
     65     automatically_deassert_chip_select: bool = false,
     66     ren: Ren = .Write,
     67     len_lossi_enable: Len = .SPIMaster,
     68     _unused_14: u1 = 0,
     69     _unused_15: u1 = 0,
     70     done: bool = false,
     71     rxd_contains_data: bool = false,
     72     txd_has_space: bool = true,
     73     rxr_is_full: bool = false,
     74     rxf_is_full: bool = false,
     75     chip_select_0: ChipSelectPolarity = .ActiveLow,
     76     chip_select_1: ChipSelectPolarity = .ActiveLow,
     77     chip_select_2: ChipSelectPolarity = .ActiveLow,
     78     dma_enable_lossi: bool = false,
     79     lossi_write_u32: bool = false,
     80     _reserved_31_26: u6 = 0,
     81 };
     82 
     83 const Clk = packed struct(u32) {
     84     clock_divider: u16 = 0,
     85     _reserved_31_16: u16 = 0,
     86 };
     87 
     88 const DataLength = packed struct(u32) {
     89     length: u16,
     90     _reserved_31_16: u16,
     91 };
     92 
     93 const Ltoh = packed struct(u32) {
     94     toh: u4,
     95     _reserved_31_4: u28,
     96 };
     97 
     98 const Dc = packed struct(u32) {
     99     dma_write_request_threshold: u8,
    100     dma_write_panic_threshold: u8,
    101     dma_read_request_threshold: u8,
    102     dma_read_panic_threshold: u8,
    103 };
    104 
    105 // Polled:
    106 // 1. Set CS(MasterControlStatus), CPOL(chip_select_polarity), CPHA(clock_phase)
    107 //    as required and set TA(transfer_active) = 1
    108 // 2. Poll TXD(txd_has_space) writing bytes to SPI_FIFO(FiFo), RXD(rxd_contains_data)
    109 //    reading bytes from SPI_FIFO(FiFo) until all data is written
    110 // 3. Poll DONE(transfer_progress) until it goes to 1
    111 // 4. Set TA(transfer_active) = 0
    112 pub const SpiConfig = struct {
    113     chip_select: MasterControlStatus.ChipSelect,
    114     chip_select_polarity: MasterControlStatus.ChipSelectPolarity = .ActiveLow,
    115     clock_polarity: MasterControlStatus.ClockPolarity = .Low,
    116     clock_phase: MasterControlStatus.ClockPhase = .Middle,
    117     pins: enum(u8) {
    118         Lower = 7,
    119         Upper = 35,
    120     },
    121 };
    122 
    123 pub fn init(config: *const SpiConfig, clock: u16) void {
    124     const ce1 = @intFromEnum(config.pins);
    125     gpio.fn_sel(ce1, .alt_fn_0) catch {};
    126     gpio.set_pull(ce1, .Off) catch {};
    127 
    128     gpio.fn_sel(ce1 + 1, .alt_fn_0) catch {};
    129     gpio.set_pull(ce1 + 1, .Off) catch {};
    130 
    131     gpio.fn_sel(ce1 + 2, .alt_fn_0) catch {};
    132     gpio.set_pull(ce1 + 2, .Off) catch {};
    133 
    134     gpio.fn_sel(ce1 + 3, .alt_fn_0) catch {};
    135     gpio.set_pull(ce1 + 3, .Off) catch {};
    136 
    137     gpio.fn_sel(ce1 + 4, .alt_fn_0) catch {};
    138     gpio.set_pull(ce1 + 4, .Off) catch {};
    139 
    140     // Set Clock
    141     CLK.set(.{
    142         .clock_divider = clock,
    143     });
    144 }
    145 
    146 pub fn get_base_config(config: *const SpiConfig) MasterControlStatus {
    147     var cs: MasterControlStatus = .{};
    148 
    149     cs.chip_select = config.chip_select;
    150     cs.chip_select_polarity = config.chip_select_polarity;
    151     cs.clock_polarity = config.clock_polarity;
    152     cs.clock_phase = config.clock_phase;
    153 
    154     switch (config.chip_select) {
    155         .@"0" => cs.chip_select_0 = config.chip_select_polarity,
    156         .@"1" => cs.chip_select_1 = config.chip_select_polarity,
    157         .@"2" => cs.chip_select_2 = config.chip_select_polarity,
    158     }
    159 
    160     return cs;
    161 }
    162 
    163 fn clear_fifo(config: *const SpiConfig) void {
    164     var base = get_base_config(config);
    165     base.clear_fifo = .{ .clear_rx = true, .clear_tx = true };
    166     CS.set(base);
    167 }
    168 
    169 fn start_transfer(config: *const SpiConfig) void {
    170     CS.set(get_base_config(config));
    171     clear_fifo(config);
    172     var base = get_base_config(config);
    173     base.transfer_active = true;
    174     CS.set(base);
    175 }
    176 
    177 fn end_transfer(config: *const SpiConfig) void {
    178     var base = get_base_config(config);
    179     base.transfer_active = false;
    180     CS.set(base);
    181 }
    182 
    183 pub fn transfer(config: *const SpiConfig, src: []const u8, dst: []u8) void {
    184     start_transfer(config);
    185 
    186     for (0..@max(src.len, dst.len)) |i| {
    187         while (!CS.get().txd_has_space) {}
    188 
    189         if (i < src.len) {
    190             FIFO.set(@as(u32, src[i]));
    191         } else {
    192             FIFO.set(0x00);
    193         }
    194 
    195         while (!CS.get().rxd_contains_data) {}
    196 
    197         if (i < dst.len) {
    198             dst[i] = @truncate(FIFO.get());
    199         } else {
    200             _ = FIFO.get();
    201         }
    202     }
    203 
    204     while (!CS.get().done) {}
    205 
    206     end_transfer(config);
    207 }