|
1 | 1 | //! See [rp2040 docs](https://datasheets.raspberrypi.com/rp2040/rp2040-datasheet.pdf), page 136.
|
2 | 2 | const rom = @import("rom.zig");
|
3 | 3 |
|
| 4 | +const microzig = @import("microzig"); |
| 5 | +const peripherals = microzig.chip.peripherals; |
| 6 | + |
| 7 | +const IO_QSPI = peripherals.IO_QSPI; |
| 8 | +const XIP_SSI = peripherals.XIP_SSI; |
| 9 | + |
4 | 10 | pub const Command = enum(u8) {
|
5 | 11 | block_erase = 0xd8,
|
6 | 12 | ruid_cmd = 0x4b,
|
@@ -108,3 +114,86 @@ export fn _range_program(offset: u32, data: [*]const u8, len: usize) linksection
|
108 | 114 |
|
109 | 115 | boot2.flash_enable_xip();
|
110 | 116 | }
|
| 117 | + |
| 118 | +/// Force the chip select using IO overrides, in case RAM-resident IRQs |
| 119 | +/// are still running, and the FIFO bottoms out |
| 120 | +pub inline fn force_cs(high: bool) void { |
| 121 | + @call(.never_inline, _force_cs, .{high}); |
| 122 | +} |
| 123 | + |
| 124 | +fn _force_cs(high: bool) linksection(".time_critical") void { |
| 125 | + const value = v: { |
| 126 | + var value: u32 = 0x2; |
| 127 | + if (high) { |
| 128 | + value = 0x3; |
| 129 | + } |
| 130 | + break :v value << 8; |
| 131 | + }; |
| 132 | + |
| 133 | + const IO_QSPI_GPIO_QSPI_SS_CTRL: *volatile u32 = @ptrFromInt(@intFromPtr(IO_QSPI) + 0x0C); |
| 134 | + IO_QSPI_GPIO_QSPI_SS_CTRL.* = (IO_QSPI_GPIO_QSPI_SS_CTRL.* ^ value) & 0x300; |
| 135 | +} |
| 136 | + |
| 137 | +/// Execute a command on the flash chip |
| 138 | +/// |
| 139 | +/// Configures flash for serial mode operation, sends a command, receives response |
| 140 | +/// and then configures flash back to XIP mode |
| 141 | +pub inline fn cmd(tx_buf: []const u8, rx_buf: []u8) void { |
| 142 | + @call(.never_inline, _cmd, .{ tx_buf, rx_buf }); |
| 143 | +} |
| 144 | + |
| 145 | +fn _cmd(tx_buf: []const u8, rx_buf: []u8) linksection(".time_critical") void { |
| 146 | + boot2.flash_init(); |
| 147 | + asm volatile ("" ::: "memory"); // memory barrier |
| 148 | + rom.connect_internal_flash()(); |
| 149 | + rom.flash_exit_xip()(); |
| 150 | + force_cs(false); |
| 151 | + |
| 152 | + // can't use peripherals, because its functions are not in ram |
| 153 | + const XIP_SSI_SR: *volatile u32 = @ptrFromInt(@intFromPtr(XIP_SSI) + 0x28); |
| 154 | + const XIP_SSI_DR0: *volatile u8 = @ptrFromInt(@intFromPtr(XIP_SSI) + 0x60); |
| 155 | + |
| 156 | + const len = tx_buf.len; |
| 157 | + var tx_remaining = len; |
| 158 | + var rx_remaining = len; |
| 159 | + const fifo_depth = 16 - 2; |
| 160 | + while (tx_remaining > 0 or rx_remaining > 0) { |
| 161 | + const can_put = XIP_SSI_SR.* & 0x2 != 0; // TFNF |
| 162 | + const can_get = XIP_SSI_SR.* & 0x8 != 0; // RFNE |
| 163 | + |
| 164 | + if (can_put and tx_remaining > 0 and rx_remaining < tx_remaining + fifo_depth) { |
| 165 | + XIP_SSI_DR0.* = tx_buf[len - tx_remaining]; |
| 166 | + tx_remaining -= 1; |
| 167 | + } |
| 168 | + if (can_get and rx_remaining > 0) { |
| 169 | + rx_buf[len - rx_remaining] = XIP_SSI_DR0.*; |
| 170 | + rx_remaining -= 1; |
| 171 | + } |
| 172 | + } |
| 173 | + |
| 174 | + force_cs(true); |
| 175 | + rom.flash_flush_cache()(); |
| 176 | + boot2.flash_enable_xip(); |
| 177 | +} |
| 178 | + |
| 179 | +const id_dummy_len = 4; |
| 180 | +const id_data_len = 8; |
| 181 | +const id_total_len = 1 + id_dummy_len + id_data_len; |
| 182 | +var id_buf: ?[id_data_len]u8 = null; |
| 183 | + |
| 184 | +/// Read the flash chip's ID which is unique to each RP2040 |
| 185 | +pub fn id() [id_data_len]u8 { |
| 186 | + if (id_buf) |b| { |
| 187 | + return b; |
| 188 | + } |
| 189 | + |
| 190 | + var tx_buf: [id_total_len]u8 = undefined; |
| 191 | + var rx_buf: [id_total_len]u8 = undefined; |
| 192 | + tx_buf[0] = @intFromEnum(Command.ruid_cmd); |
| 193 | + cmd(&tx_buf, &rx_buf); |
| 194 | + |
| 195 | + id_buf = undefined; |
| 196 | + @memcpy(&id_buf.?, rx_buf[1 + id_dummy_len ..]); |
| 197 | + |
| 198 | + return id_buf.?; |
| 199 | +} |
0 commit comments