From 890a38bfa08e44d0f2a850b0803215e4219a732e Mon Sep 17 00:00:00 2001 From: Ryan Malloy Date: Thu, 12 Feb 2026 07:17:47 -0700 Subject: [PATCH] Fix BCM4500 I2C address and add hardware diagnostic commands MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Correct BCM4500 I2C address from 0x10 (8-bit wire) to 0x08 (7-bit) since fx2lib shifts internally. Add i2c_combined_read() with repeated START for proper BCM4500 register access. Add I2C bus scan (0xB4), raw read (0xB5), and indirect protocol diagnostic (0xB6) commands. Single-transaction indirect reads/writes for BCM4500 register protocol. Verified on hardware: BCM4500 ACKs at 0x08, BOOT_8PSK returns config 0x03. Register reads still return zeros — BCM4500 needs DSP firmware loaded via LOAD_BCM4500 (0x88) before registers become functional. --- firmware/skywalker1.c | 222 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 192 insertions(+), 30 deletions(-) diff --git a/firmware/skywalker1.c b/firmware/skywalker1.c index 9188cfa..fe9d0d2 100644 --- a/firmware/skywalker1.c +++ b/firmware/skywalker1.c @@ -18,8 +18,8 @@ #define SYNCDELAY SYNCDELAY4 -/* BCM4500 I2C address (7-bit) */ -#define BCM4500_ADDR 0x10 +/* BCM4500 I2C address (7-bit); 8-bit wire address is 0x10/0x11 */ +#define BCM4500_ADDR 0x08 /* BCM4500 indirect register protocol registers */ #define BCM_REG_PAGE 0xA6 @@ -81,6 +81,68 @@ static __xdata BYTE i2c_rd[8]; /* ---------- BCM4500 I2C helpers ---------- */ +/* + * Combined I2C write-read with repeated START (no STOP between + * write and read phases). Many I2C devices including the BCM4500 + * require this pattern instead of separate write+stop/read+stop. + * + * Sequence: START → addr+W → reg → RESTART → addr+R → data → STOP + */ +static BOOL i2c_combined_read(BYTE addr, BYTE reg, BYTE len, BYTE *buf) { + BYTE i; + BYTE tmp; + + /* START + write address */ + I2CS |= bmSTART; + I2DAT = addr << 1; + while (!(I2CS & bmDONE)) + ; + if (!(I2CS & bmACK)) + goto fail; + + /* Write register address */ + I2DAT = reg; + while (!(I2CS & bmDONE)) + ; + if (!(I2CS & bmACK)) + goto fail; + + /* REPEATED START + read address */ + I2CS |= bmSTART; + I2DAT = (addr << 1) | 1; + while (!(I2CS & bmDONE)) + ; + if (!(I2CS & bmACK)) + goto fail; + + /* For single byte, set LASTRD before dummy read */ + if (len == 1) + I2CS |= bmLASTRD; + + /* Dummy read to trigger first clock burst */ + tmp = I2DAT; + + for (i = 0; i < len; i++) { + while (!(I2CS & bmDONE)) + ; + if (i == len - 2) + I2CS |= bmLASTRD; + if (i == len - 1) + I2CS |= bmSTOP; + buf[i] = I2DAT; + } + + while (I2CS & bmSTOP) + ; + return TRUE; + +fail: + I2CS |= bmSTOP; + while (I2CS & bmSTOP) + ; + return FALSE; +} + /* * Write one byte to a BCM4500 direct I2C register (subaddr). * This writes to the I2C register directly, not through the @@ -92,49 +154,42 @@ static BOOL bcm_direct_write(BYTE reg, BYTE val) { } /* - * Read one byte from a BCM4500 direct I2C register. + * Read one byte from a BCM4500 direct I2C register using + * combined write-read with repeated START. */ static BOOL bcm_direct_read(BYTE reg, BYTE *val) { - BYTE r = reg; - if (!i2c_write(BCM4500_ADDR, 1, &r, 0, NULL)) - return FALSE; - if (!i2c_read(BCM4500_ADDR, 1, val)) - return FALSE; - return TRUE; + return i2c_combined_read(BCM4500_ADDR, reg, 1, val); } /* * Write a value to a BCM4500 indirect register. - * Protocol: - * 1. Write page/register to 0xA6 - * 2. Write data to 0xA7 - * 3. Write 0x03 to 0xA8 (execute write) + * Single multi-byte I2C write to 0xA6 with auto-increment: + * [0xA6] = page, [0xA7] = data, [0xA8] = 0x03 (write cmd) */ static BOOL bcm_indirect_write(BYTE reg, BYTE val) { - if (!bcm_direct_write(BCM_REG_PAGE, reg)) - return FALSE; - if (!bcm_direct_write(BCM_REG_DATA, val)) - return FALSE; - if (!bcm_direct_write(BCM_REG_CMD, BCM_CMD_WRITE)) - return FALSE; - return TRUE; + BYTE start_reg = BCM_REG_PAGE; + i2c_rd[0] = reg; + i2c_rd[1] = val; + i2c_rd[2] = BCM_CMD_WRITE; + return i2c_write(BCM4500_ADDR, 1, &start_reg, 3, i2c_rd); } /* * Read a value from a BCM4500 indirect register. - * Protocol: - * 1. Write page/register to 0xA6 - * 2. Write 0x01 to 0xA8 (execute read) - * 3. Read data from 0xA7 + * Protocol: single multi-byte I2C write to 0xA6 with auto-increment: + * [0xA6] = page, [0xA7] = 0x00, [0xA8] = 0x01 (read cmd) + * Then read the result from 0xA7. */ static BOOL bcm_indirect_read(BYTE reg, BYTE *val) { - if (!bcm_direct_write(BCM_REG_PAGE, reg)) + BYTE start_reg = BCM_REG_PAGE; + /* page, placeholder data, read command — written to A6,A7,A8 in one shot */ + i2c_rd[0] = reg; + i2c_rd[1] = 0x00; + i2c_rd[2] = BCM_CMD_READ; + if (!i2c_write(BCM4500_ADDR, 1, &start_reg, 3, i2c_rd)) return FALSE; - if (!bcm_direct_write(BCM_REG_CMD, BCM_CMD_READ)) - return FALSE; - if (!bcm_direct_read(BCM_REG_DATA, val)) - return FALSE; - return TRUE; + delay(1); + return i2c_combined_read(BCM4500_ADDR, BCM_REG_DATA, 1, val); } /* @@ -735,6 +790,113 @@ BOOL handle_vendorcommand(BYTE cmd) { do_blind_scan(); return TRUE; + /* 0xB4: I2C_BUS_SCAN -- probe all 7-bit addresses, return 16-byte bitmap */ + case 0xB4: { + BYTE a, byte_idx, bit; + /* 128 addresses / 8 = 16 bytes bitmap */ + for (byte_idx = 0; byte_idx < 16; byte_idx++) + EP0BUF[byte_idx] = 0; + + for (a = 1; a < 0x78; a++) { + /* Try START + address + write, see if ACK comes back */ + I2CS |= bmSTART; + I2DAT = a << 1; /* write direction */ + while (!(I2CS & bmDONE)) + ; + if (I2CS & bmACK) { + /* Device responded at this address */ + byte_idx = a >> 3; + bit = a & 0x07; + EP0BUF[byte_idx] |= (1 << bit); + } + I2CS |= bmSTOP; + while (I2CS & bmSTOP) + ; + } + EP0BCH = 0; + EP0BCL = 16; + return TRUE; + } + + /* 0xB5: I2C_RAW_READ -- read N bytes from any I2C address + * wValue = 7-bit I2C address, wIndex = register, wLength = bytes to read + * Uses combined write-read with repeated START */ + case 0xB5: { + BYTE i2c_addr_b5 = (BYTE)wval; + BYTE i2c_reg_b5 = (BYTE)SETUP_INDEX(); + BYTE i2c_len_b5 = (BYTE)SETUP_LENGTH(); + BYTE ok; + + if (i2c_len_b5 > 64) i2c_len_b5 = 64; + + ok = i2c_combined_read(i2c_addr_b5, i2c_reg_b5, i2c_len_b5, EP0BUF); + if (!ok) { + BYTE fi; + for (fi = 0; fi < i2c_len_b5; fi++) + EP0BUF[fi] = 0xFF; + } + EP0BCH = 0; + EP0BCL = i2c_len_b5; + return TRUE; + } + + /* 0xB6: I2C_DIAG -- step-by-step indirect register read diagnostic + * wValue = page/register to read + * Returns 8 bytes: [write_A6_ok, readback_A6, write_A8_ok, readback_A8, + * readback_A7, direct_read_A6, direct_read_A7, direct_read_A8] */ + case 0xB6: { + BYTE target_reg = (BYTE)wval; + BYTE diag[8]; + BYTE rb; + + /* Step 1: Write target register to page select (0xA6) */ + diag[0] = bcm_direct_write(BCM_REG_PAGE, target_reg) ? 0x01 : 0x00; + + /* Step 2: Read back 0xA6 to verify write */ + rb = 0xEE; + i2c_combined_read(BCM4500_ADDR, BCM_REG_PAGE, 1, &rb); + diag[1] = rb; + + /* Step 3: Write read command (0x01) to 0xA8 */ + diag[2] = bcm_direct_write(BCM_REG_CMD, BCM_CMD_READ) ? 0x01 : 0x00; + + /* Step 4: Read back 0xA8 to check command status */ + rb = 0xEE; + i2c_combined_read(BCM4500_ADDR, BCM_REG_CMD, 1, &rb); + diag[3] = rb; + + /* Step 5: Small delay for command execution */ + delay(2); + + /* Step 6: Read 0xA7 (data register) — this is the result */ + rb = 0xEE; + i2c_combined_read(BCM4500_ADDR, BCM_REG_DATA, 1, &rb); + diag[4] = rb; + + /* Step 7: Read back all three control regs for final state */ + rb = 0xEE; + i2c_combined_read(BCM4500_ADDR, BCM_REG_PAGE, 1, &rb); + diag[5] = rb; + rb = 0xEE; + i2c_combined_read(BCM4500_ADDR, BCM_REG_DATA, 1, &rb); + diag[6] = rb; + rb = 0xEE; + i2c_combined_read(BCM4500_ADDR, BCM_REG_CMD, 1, &rb); + diag[7] = rb; + + EP0BUF[0] = diag[0]; + EP0BUF[1] = diag[1]; + EP0BUF[2] = diag[2]; + EP0BUF[3] = diag[3]; + EP0BUF[4] = diag[4]; + EP0BUF[5] = diag[5]; + EP0BUF[6] = diag[6]; + EP0BUF[7] = diag[7]; + EP0BCH = 0; + EP0BCL = 8; + return TRUE; + } + default: return FALSE; }