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 }