sylveos

Toy Operating System
Log | Files | Refs

mini-spi.zig (6768B)


      1 const std = @import("std");
      2 
      3 const aux = @import("./aux.zig");
      4 const gpio = @import("./gpio.zig");
      5 const mem = @import("../mem.zig");
      6 const register = @import("../register.zig");
      7 
      8 const BASE_ADDRESS_1: usize = aux.BASE_ADDRESS + 0x80;
      9 const BASE_ADDRESS_2: usize = aux.BASE_ADDRESS + 0xC0;
     10 
     11 const AUX_SPI1_CNTL0: register.Register(Control0, .ReadModifyWrite) = .init(BASE_ADDRESS_1 + 0x00);
     12 const AUX_SPI1_CNTL1: register.Register(Control1, .ReadModifyWrite) = .init(BASE_ADDRESS_1 + 0x04);
     13 const AUX_SPI1_STAT: register.Register(Status, .ReadModifyWrite) = .init(BASE_ADDRESS_1 + 0x08);
     14 const AUX_SPI1_PEEK: register.Register(FiFo, .ReadOnly) = .init(BASE_ADDRESS_1 + 0x0C);
     15 const AUX_SPI1_IO: register.Register(FiFo, .ReadModifyWrite) = .init(BASE_ADDRESS_1 + 0x20);
     16 
     17 const AUX_SPI2_CNTL0: register.Register(Control0, .ReadModifyWrite) = .init(BASE_ADDRESS_2 + 0x00);
     18 const AUX_SPI2_CNTL1: register.Register(Control1, .ReadModifyWrite) = .init(BASE_ADDRESS_2 + 0x04);
     19 const AUX_SPI2_STAT: register.Register(Status, .ReadModifyWrite) = .init(BASE_ADDRESS_2 + 0x08);
     20 const AUX_SPI2_PEEK: register.Register(FiFo, .ReadOnly) = .init(BASE_ADDRESS_2 + 0x0C);
     21 const AUX_SPI2_IO: register.Register(FiFo, .ReadModifyWrite) = .init(BASE_ADDRESS_2 + 0x20);
     22 
     23 // Unlike UART, only 1 pair
     24 const SPI1_CE2 = 16;
     25 const SPI1_CE1 = 17;
     26 const SPI1_CE0 = 18;
     27 const SPI1_MISO = 19;
     28 const SPI1_MOSI = 20;
     29 const SPI1_SCLK = 21;
     30 
     31 const SPI2_CE2 = 40;
     32 const SPI2_CE1 = 41;
     33 const SPI2_CE0 = 42;
     34 const SPI2_MISO = 43;
     35 const SPI2_MOSI = 44;
     36 const SPI2_SCLK = 45;
     37 
     38 const Control0 = packed struct(u32) {
     39     pub const DOutHold = enum(u2) {
     40         None = 0b00,
     41         @"1" = 0b01,
     42         @"4" = 0b10,
     43         @"7" = 0b11,
     44     };
     45     pub const ChipSelect = packed struct(u3) {
     46         @"0": bool,
     47         @"1": bool,
     48         @"2": bool,
     49     };
     50     shift_length: u6,
     51     shift_out_ms: bool,
     52     invert_spi_clk: bool,
     53     out_rising: bool,
     54     clear_fifos: bool,
     55     in_rising: bool,
     56     enable: bool,
     57     dout_hold: DOutHold,
     58     variable_width: bool,
     59     variable_cs: bool,
     60     post_input: bool,
     61     chip_select: ChipSelect,
     62     speed: u12,
     63 };
     64 const Control1 = packed struct(u32) {
     65     keep_input: bool,
     66     shift_in_ms: bool,
     67     _reserved_5_2: u4 = 0,
     68     done_irq: bool,
     69     tx_empty_irq: bool,
     70     cs_high_time: u3,
     71     _reserved_31_11: u21 = 0,
     72 };
     73 const Status = packed struct(u32) {
     74     bit_count: u6,
     75     busy: bool,
     76     rx_empty: bool,
     77     rx_full: bool,
     78     tx_empty: bool,
     79     tx_full: bool,
     80     _reserved_15_11: u5 = 0,
     81     rx_fill_level: u4,
     82     tx_fill_level: u4,
     83     _reserved_31_22: u8,
     84 };
     85 const FiFo = packed struct(u32) {
     86     data: u16,
     87     _reserved_16_31: u16 = 0,
     88 };
     89 
     90 pub const SpiDevice = enum {
     91     SPI1,
     92     SPI2,
     93 };
     94 
     95 pub const SpiConfig = struct {
     96     speed: u12,
     97     chip_select: Control0.ChipSelect = .{
     98         .@"0" = false,
     99         .@"1" = false,
    100         .@"2" = false,
    101     },
    102     shift_length: u6 = 8,
    103     out_rising: bool = true,
    104     in_rising: bool = false,
    105     invert_clk: bool = false,
    106     shift_out_ms: bool = true,
    107     shift_in_ms: bool = false,
    108     cs_high_time: u3 = 0,
    109 };
    110 
    111 pub fn init(device: SpiDevice, config: SpiConfig) void {
    112     const cntl0_reg = switch (device) {
    113         .SPI1 => AUX_SPI1_CNTL0,
    114         .SPI2 => AUX_SPI2_CNTL0,
    115     };
    116     const cntl1_reg = switch (device) {
    117         .SPI1 => AUX_SPI1_CNTL1,
    118         .SPI2 => AUX_SPI2_CNTL1,
    119     };
    120 
    121     switch (device) {
    122         .SPI1 => {
    123             gpio.fn_sel(SPI1_MISO, .alt_fn_4) catch {};
    124             gpio.set_pull(SPI1_MISO, .Up) catch {};
    125 
    126             gpio.fn_sel(SPI1_MOSI, .alt_fn_4) catch {};
    127             gpio.set_pull(SPI1_MOSI, .Up) catch {};
    128 
    129             gpio.fn_sel(SPI1_SCLK, .alt_fn_4) catch {};
    130             if (config.invert_clk) {
    131                 gpio.set_pull(SPI1_SCLK, .Up) catch {};
    132             } else {
    133                 gpio.set_pull(SPI1_SCLK, .Down) catch {};
    134             }
    135 
    136             gpio.fn_sel(SPI1_CE0, .alt_fn_4) catch {};
    137             gpio.set_pull(SPI1_CE2, .Up) catch {};
    138 
    139             gpio.fn_sel(SPI1_CE1, .alt_fn_4) catch {};
    140             gpio.set_pull(SPI1_CE2, .Up) catch {};
    141 
    142             gpio.fn_sel(SPI1_CE2, .alt_fn_4) catch {};
    143             gpio.set_pull(SPI1_CE2, .Up) catch {};
    144 
    145             aux.enable_spi_1();
    146         },
    147         .SPI2 => {
    148             gpio.fn_sel(SPI2_MISO, .alt_fn_4) catch {};
    149             gpio.set_pull(SPI2_MISO, .Up) catch {};
    150 
    151             gpio.fn_sel(SPI2_MOSI, .alt_fn_4) catch {};
    152             gpio.set_pull(SPI2_MOSI, .Up) catch {};
    153 
    154             gpio.fn_sel(SPI2_SCLK, .alt_fn_4) catch {};
    155             if (config.invert_clk) {
    156                 gpio.set_pull(SPI2_SCLK, .Up) catch {};
    157             } else {
    158                 gpio.set_pull(SPI2_SCLK, .Down) catch {};
    159             }
    160 
    161             gpio.fn_sel(SPI2_CE0, .alt_fn_4) catch {};
    162             gpio.set_pull(SPI2_CE2, .Up) catch {};
    163 
    164             gpio.fn_sel(SPI2_CE1, .alt_fn_4) catch {};
    165             gpio.set_pull(SPI2_CE1, .Up) catch {};
    166 
    167             gpio.fn_sel(SPI2_CE2, .alt_fn_4) catch {};
    168             gpio.set_pull(SPI2_CE2, .Up) catch {};
    169 
    170             aux.enable_spi_2();
    171         },
    172     }
    173 
    174     cntl0_reg.set(.{
    175         .shift_length = config.shift_length,
    176         .shift_out_ms = config.shift_out_ms,
    177         .invert_spi_clk = config.invert_clk,
    178         .out_rising = config.out_rising,
    179         .clear_fifos = true,
    180         .in_rising = config.in_rising,
    181         .enable = true,
    182         .dout_hold = .None,
    183         .variable_width = false,
    184         .variable_cs = false,
    185         .post_input = false,
    186         .chip_select = config.chip_select,
    187         .speed = config.speed,
    188     });
    189 
    190     cntl1_reg.set(.{
    191         .keep_input = false,
    192         .shift_in_ms = config.shift_in_ms,
    193         .done_irq = false,
    194         .tx_empty_irq = false,
    195         .cs_high_time = config.cs_high_time,
    196     });
    197 }
    198 
    199 pub fn status(device: SpiDevice) Status {
    200     return switch (device) {
    201         .SPI1 => AUX_SPI1_STAT.get(),
    202         .SPI2 => AUX_SPI2_STAT.get(),
    203     };
    204 }
    205 
    206 pub fn write_polled(device: SpiDevice, data: []const u8) void {
    207     const io_reg = switch (device) {
    208         .SPI1 => AUX_SPI1_IO,
    209         .SPI2 => AUX_SPI2_IO,
    210     };
    211 
    212     for (data) |b| {
    213         while (status(device).tx_full) {}
    214         io_reg.set(.{
    215             .data = @as(u16, b),
    216         });
    217     }
    218 }
    219 
    220 pub fn read_polled(device: SpiDevice, data: []u8) void {
    221     const io_reg = switch (device) {
    222         .SPI1 => AUX_SPI1_IO,
    223         .SPI2 => AUX_SPI2_IO,
    224     };
    225 
    226     for (0..data.len) |i| {
    227         while (status(device).rx_empty) {}
    228         data[i] = @truncate(io_reg.get().data);
    229     }
    230 }
    231 
    232 pub fn transfer(device: SpiDevice, src: []const u8, dst: []u8) void {
    233     write_polled(device, src);
    234     flush(device);
    235     read_polled(device, dst);
    236 }
    237 
    238 pub fn flush(device: SpiDevice) void {
    239     while (status(device).busy) {}
    240 }