#include "bba.h" #include #include #include u32 bba_speed = EXI_SPEED32MHZ; /* nominal name; ~27 MHz in reality */ static int begin(void); static void end(void); u32 bba_gap_us = 16; /* see bba.h -- 0 reproduces the raw failure */ /* Busy-wait, not usleep(): we are holding the EXI lock with CS asserted, so * yielding here is both unnecessary and imprecise at these timescales. */ static void gap(void) { u64 t0; if (!bba_gap_us) return; t0 = gettime(); while (ticks_to_microsecs(diff_ticks(t0, gettime())) < bba_gap_us) ; } /* Real GC<->BBA protocol (docs/exi_protocol_real.md), verified against libogc * and Swiss. Register space is a 4-byte header followed by a data phase that * runs until CS -- there is no length field. * read [0x80, addr>>8, addr&0xFF, 0x00] + N bytes * write [0xC0, addr>>8, addr&0xFF, 0x00] + N bytes * The trailing 0x00 is the device's prefetch window: the address completes a * whole byte before the data phase so the register file can fetch in time. */ int begin_raw(void) { return begin(); } void end_raw(void) { end(); } static int begin(void) { int tries; /* EXI_Lock fails outright (rather than blocking) when another subsystem * holds the channel -- Swiss or a libogc driver may have it briefly. * Retry for ~100 ms before giving up so a transient collision does not * surface as a test failure. */ for (tries = 0; tries < 100; tries++) { if (EXI_Lock(BBA_EXI_CHAN, BBA_EXI_DEV, NULL) > 0) break; usleep(1000); } if (tries == 100) return -1; if (EXI_Select(BBA_EXI_CHAN, BBA_EXI_DEV, bba_speed) <= 0) { EXI_Unlock(BBA_EXI_CHAN); return -2; } return 0; } static void end(void) { EXI_Deselect(BBA_EXI_CHAN); EXI_Unlock(BBA_EXI_CHAN); } static int put_hdr(u16 addr, int write) { u32 h = ((u32)(write ? 0xC0 : 0x80) << 24) | ((u32)addr << 8); if (EXI_Imm(BBA_EXI_CHAN, &h, 4, EXI_WRITE, NULL) <= 0) return -3; if (EXI_Sync(BBA_EXI_CHAN) <= 0) return -4; return 0; } int bba_read(u16 addr, void *buf, u32 len) { int rc; if (len < 1) return -10; if ((rc = begin()) < 0) return rc; if ((rc = put_hdr(addr, 0)) < 0) { end(); return rc; } gap(); memset(buf, 0, len); if (EXI_ImmEx(BBA_EXI_CHAN, buf, len, EXI_READ) <= 0) { end(); return -5; } end(); return 0; } int bba_write(u16 addr, const void *buf, u32 len) { int rc; u8 tmp[64]; if (len < 1 || len > sizeof(tmp)) return -10; memcpy(tmp, buf, len); if ((rc = begin()) < 0) return rc; if ((rc = put_hdr(addr, 1)) < 0) { end(); return rc; } if (EXI_ImmEx(BBA_EXI_CHAN, tmp, len, EXI_WRITE) <= 0) { end(); return -6; } end(); return 0; } int bba_devid(u8 id[4]) { int rc; u16 h = 0x0000; /* command space, reg 0, read */ if ((rc = begin()) < 0) return rc; if (EXI_Imm(BBA_EXI_CHAN, &h, 2, EXI_WRITE, NULL) <= 0) { end(); return -3; } if (EXI_Sync(BBA_EXI_CHAN) <= 0) { end(); return -4; } gap(); memset(id, 0, 4); if (EXI_ImmEx(BBA_EXI_CHAN, id, 4, EXI_READ) <= 0) { end(); return -5; } end(); return 0; } int bba_r8(u16 addr) { u8 v; int rc = bba_read(addr, &v, 1); return rc < 0 ? rc : (int)v; } int bba_w8(u16 addr, u8 val) { return bba_write(addr, &val, 1); } int bba_stream(u16 addr, void *buf, u32 len) { /* Identical to bba_read now: the real protocol always streams until CS, * so there is no separate "DMA" header form. */ return bba_read(addr, buf, len); } int bba_tx_bytes(const u8 *buf, u32 len) { u32 off = 0; /* Stream in 64-byte chunks, ONE EXI transaction each. * * Writing byte by byte with bba_w8() costs a full EXI_Lock / EXI_Select / * header / EXI_Deselect / EXI_Unlock PER BYTE -- about sixty transactions * for one 60-byte ACK. That latency is why ~1/3 of netboot blocks were * retried: the ACK always reached the W5100 (txto=0, TI always set) but * missed the host's timeout. * * ⚠️ BUT THE CHUNK SIZE IS BOUNDED BY tx_bytes, WHICH IS 16 DEEP. * BBARegisterFile silently drops bytes when tx_bytes.w_rdy is low, * because SPI cannot back-pressure. Within one transaction the bytes * arrive back-to-back at EXI speed -- far faster than TXFrameDrain can * start consuming -- so a 64-byte chunk overran the FIFO, the frame went * out truncated, and the host saw no valid ACK at all ("no ACK after 20 * tries"). Byte-by-byte worked only because the per-byte EXI overhead * happened to give the drain time. * * tx_bytes is now 2048 deep -- a whole maximum-size frame -- so the old * 8-byte cap is gone and 64 (bba_write()'s buffer limit) sends a 60-byte * ACK in a SINGLE transaction. The depth is what makes this safe: SPI * cannot back-pressure, so the FIFO must absorb the entire burst. */ while (off < len) { u32 n = len - off; int rc; if (n > 64) n = 64; rc = bba_write(REG_TXDATA, buf + off, n); if (rc < 0) return rc; off += n; } return 0; }