Fix BCM4500 full boot: strip init block length prefixes, handle gateway poll
Two root causes prevented BCM4500 init block writes from completing: 1. Init block data arrays included length prefix bytes from the stock firmware's XDATA format (17-byte blocks at code:0x0B4F). The stock firmware reads byte 0 as length and writes bytes 1..N to A7. Blocks 0 and 1 had the length prefix (0x06, 0x07) as the first data byte, corrupting the DSP's indirect register FIFO. 2. The BCM3440 gateway's A8 register does not clear bit 0 after indirect write commands (0x03), even though the BCM4500 processes them successfully (confirmed via direct address 0x08 where A8 transitions from 0x03 → 0x02). bcm_poll_ready() now treats gateway timeout as success with a settling delay. Boot now completes reliably in ~0.96s through all stages: GPIO → power → reset → PLL/DSP load → init blocks 0,1,2 → 0xFF.
This commit is contained in:
parent
7f1e0cf0d7
commit
29df688f28
@ -22,14 +22,32 @@
|
|||||||
|
|
||||||
#define SYNCDELAY SYNCDELAY4
|
#define SYNCDELAY SYNCDELAY4
|
||||||
|
|
||||||
/* BCM4500 I2C address (7-bit); 8-bit wire address is 0x10/0x11 */
|
/* I2C device addresses (7-bit)
|
||||||
#define BCM4500_ADDR 0x08
|
*
|
||||||
|
* BCM4500 register access goes THROUGH the BCM3440 tuner's I2C gateway.
|
||||||
|
* The BCM3440 at 0x10 (wire 0x20/0x21) transparently forwards register
|
||||||
|
* reads/writes in the 0xA0+ range to the BCM4500 demodulator.
|
||||||
|
*
|
||||||
|
* The BCM4500's own I2C address (0x08, wire 0x10/0x11) only exposes a
|
||||||
|
* single status byte via simple reads -- it does NOT support register-
|
||||||
|
* addressed reads at that address. Stock firmware v2.06 disassembly
|
||||||
|
* confirms: FUN_CODE_0DDD, FUN_CODE_10F2, and all internal register
|
||||||
|
* access use device address 0x10 (BCM3440), never 0x08 directly. */
|
||||||
|
#define BCM4500_ADDR 0x10 /* BCM4500 via BCM3440 tuner gateway */
|
||||||
|
#define BCM4500_DIRECT 0x08 /* BCM4500 direct (status byte only, no reg addressing) */
|
||||||
|
#define EEPROM_ADDR 0x51 /* Calibration EEPROM; 16-bit addressed, AT24C-series */
|
||||||
|
|
||||||
/* BCM4500 indirect register protocol registers */
|
/* BCM4500 indirect register protocol registers */
|
||||||
#define BCM_REG_PAGE 0xA6
|
#define BCM_REG_PAGE 0xA6
|
||||||
#define BCM_REG_DATA 0xA7
|
#define BCM_REG_DATA 0xA7
|
||||||
#define BCM_REG_CMD 0xA8
|
#define BCM_REG_CMD 0xA8
|
||||||
|
|
||||||
|
/* BCM4500 PLL/config direct registers (written from EEPROM during boot) */
|
||||||
|
#define BCM_REG_CFG_MODE 0xA0 /* 0x01=enter PLL config, 0x00=exit */
|
||||||
|
#define BCM_REG_PLL_A9 0xA9
|
||||||
|
#define BCM_REG_PLL_AA 0xAA
|
||||||
|
#define BCM_REG_PLL_AB 0xAB
|
||||||
|
|
||||||
/* BCM4500 status registers */
|
/* BCM4500 status registers */
|
||||||
#define BCM_REG_STATUS 0xA2
|
#define BCM_REG_STATUS 0xA2
|
||||||
#define BCM_REG_LOCK 0xA4
|
#define BCM_REG_LOCK 0xA4
|
||||||
@ -66,6 +84,8 @@
|
|||||||
#define GET_LAST_ERROR 0xBC
|
#define GET_LAST_ERROR 0xBC
|
||||||
#define GET_STREAM_DIAG 0xBD
|
#define GET_STREAM_DIAG 0xBD
|
||||||
#define GET_HOTPLUG_STATUS 0xBE
|
#define GET_HOTPLUG_STATUS 0xBE
|
||||||
|
#define GET_PLL_DIAG 0xBF
|
||||||
|
#define EEPROM_READ 0xC0
|
||||||
|
|
||||||
/* error codes (set by I2C helpers, read via 0xBC) */
|
/* error codes (set by I2C helpers, read via 0xBC) */
|
||||||
#define ERR_OK 0x00
|
#define ERR_OK 0x00
|
||||||
@ -110,8 +130,8 @@ static volatile BYTE boot_stage;
|
|||||||
/* ISR flag */
|
/* ISR flag */
|
||||||
volatile __bit got_sud;
|
volatile __bit got_sud;
|
||||||
|
|
||||||
/* I2C scratch buffers in xdata */
|
/* I2C scratch buffers in xdata (24 bytes: fits 20-byte EEPROM blocks) */
|
||||||
static __xdata BYTE i2c_buf[16];
|
static __xdata BYTE i2c_buf[24];
|
||||||
static __xdata BYTE i2c_rd[8];
|
static __xdata BYTE i2c_rd[8];
|
||||||
|
|
||||||
/* TUNE_MONITOR result buffer: filled by OUT phase, returned by IN phase */
|
/* TUNE_MONITOR result buffer: filled by OUT phase, returned by IN phase */
|
||||||
@ -120,6 +140,17 @@ static __xdata BYTE tm_result[10];
|
|||||||
/* DiSEqC message buffer (3-6 bytes) for full message transmission */
|
/* DiSEqC message buffer (3-6 bytes) for full message transmission */
|
||||||
static __xdata BYTE diseqc_msg[6];
|
static __xdata BYTE diseqc_msg[6];
|
||||||
|
|
||||||
|
/* PLL config diagnostic: captures EEPROM read results during boot.
|
||||||
|
* [0] = eeprom_check_present result (1=ok, 0=fail)
|
||||||
|
* [1] = first block count byte from EEPROM
|
||||||
|
* [2] = number of blocks successfully written
|
||||||
|
* [3] = last A9 value written (or 0xFF if none)
|
||||||
|
* [4] = last AA value written (or 0xFF if none)
|
||||||
|
* [5] = last AB count (or 0xFF if none)
|
||||||
|
* [6] = config_mode_exit result (1=ok, 0=fail)
|
||||||
|
* [7] = overall PLL result (1=ok, 0=fail) */
|
||||||
|
static __xdata BYTE pll_diag[8];
|
||||||
|
|
||||||
/* last error code for diagnostic reads via 0xBC */
|
/* last error code for diagnostic reads via 0xBC */
|
||||||
static __xdata BYTE last_error;
|
static __xdata BYTE last_error;
|
||||||
|
|
||||||
@ -154,18 +185,21 @@ static volatile __xdata BYTE wdt_armed;
|
|||||||
* BCM4500 register initialization data extracted from stock v2.06 firmware.
|
* BCM4500 register initialization data extracted from stock v2.06 firmware.
|
||||||
* FUN_CODE_0ddd writes these 3 blocks to BCM4500 indirect registers (page 0)
|
* FUN_CODE_0ddd writes these 3 blocks to BCM4500 indirect registers (page 0)
|
||||||
* via the A6/A7/A8 control interface during BOOT_8PSK.
|
* via the A6/A7/A8 control interface during BOOT_8PSK.
|
||||||
|
*
|
||||||
|
* Stock stores these at code:0x0B4F in 17-byte blocks: [length, data[0..15]].
|
||||||
|
* Only the data bytes (past the length prefix) go to A7.
|
||||||
*/
|
*/
|
||||||
static const __code BYTE bcm_init_block0[] = {
|
static const __code BYTE bcm_init_block0[] = {
|
||||||
0x06, 0x0b, 0x17, 0x38, 0x9f, 0xd9, 0x80
|
0x0b, 0x17, 0x38, 0x9f, 0xd9, 0x80
|
||||||
};
|
};
|
||||||
static const __code BYTE bcm_init_block1[] = {
|
static const __code BYTE bcm_init_block1[] = {
|
||||||
0x07, 0x09, 0x39, 0x4f, 0x00, 0x65, 0xb7, 0x10
|
0x09, 0x39, 0x4f, 0x00, 0x65, 0xb7, 0x10
|
||||||
};
|
};
|
||||||
static const __code BYTE bcm_init_block2[] = {
|
static const __code BYTE bcm_init_block2[] = {
|
||||||
0x0f, 0x0c, 0x09
|
0x0f, 0x0c, 0x09
|
||||||
};
|
};
|
||||||
#define BCM_INIT_BLOCK0_LEN 7
|
#define BCM_INIT_BLOCK0_LEN 6
|
||||||
#define BCM_INIT_BLOCK1_LEN 8
|
#define BCM_INIT_BLOCK1_LEN 7
|
||||||
#define BCM_INIT_BLOCK2_LEN 3
|
#define BCM_INIT_BLOCK2_LEN 3
|
||||||
|
|
||||||
/* ---------- BCM4500 I2C helpers ---------- */
|
/* ---------- BCM4500 I2C helpers ---------- */
|
||||||
@ -219,8 +253,9 @@ static BOOL ep0_wait_data(void) {
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* Combined I2C write-read with repeated START (no STOP between
|
* Combined I2C write-read with repeated START (no STOP between
|
||||||
* write and read phases). Many I2C devices including the BCM4500
|
* write and read phases). The BCM3440 tuner gateway expects this
|
||||||
* require this pattern instead of separate write+stop/read+stop.
|
* protocol for register-addressed reads (confirmed by stock firmware
|
||||||
|
* disassembly of function 0x1556).
|
||||||
*
|
*
|
||||||
* Sequence: START -> addr+W -> reg -> RESTART -> addr+R -> data -> STOP
|
* Sequence: START -> addr+W -> reg -> RESTART -> addr+R -> data -> STOP
|
||||||
*/
|
*/
|
||||||
@ -347,6 +382,92 @@ fail:
|
|||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ---------- EEPROM (calibration data) ---------- */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check EEPROM presence by reading 1 byte from address 0x3FFF.
|
||||||
|
* Uses 16-bit addressing: START -> 0xA2 -> 0x3F -> 0xFF -> RESTART -> 0xA3 -> data -> STOP
|
||||||
|
* Result stored in i2c_buf[0].
|
||||||
|
*
|
||||||
|
* Zero parameters to minimize DSEG usage (8051 has only 14 bytes free).
|
||||||
|
* Hardcodes EEPROM_ADDR and address 0x3FFF -- matches FUN_CODE_1556.
|
||||||
|
*
|
||||||
|
* ASSUMES: AT24C128 or AT24C256 (16KB/32KB). After reading 0x3FFF, the
|
||||||
|
* internal pointer advances to 0x4000 where PLL data blocks begin.
|
||||||
|
* Smaller devices (AT24C64) NAK during the address phase.
|
||||||
|
*/
|
||||||
|
static BOOL eeprom_check_present(void) {
|
||||||
|
BYTE i;
|
||||||
|
|
||||||
|
I2CS |= bmSTART;
|
||||||
|
I2DAT = 0xA2; /* EEPROM_ADDR << 1 = write */
|
||||||
|
if (!i2c_wait_done()) goto fail;
|
||||||
|
if (!(I2CS & bmACK)) { last_error = ERR_I2C_NAK; goto fail; }
|
||||||
|
|
||||||
|
I2DAT = 0x3F; /* address high byte */
|
||||||
|
if (!i2c_wait_done()) goto fail;
|
||||||
|
if (!(I2CS & bmACK)) { last_error = ERR_I2C_NAK; goto fail; }
|
||||||
|
|
||||||
|
I2DAT = 0xFF; /* address low byte */
|
||||||
|
if (!i2c_wait_done()) goto fail;
|
||||||
|
if (!(I2CS & bmACK)) { last_error = ERR_I2C_NAK; goto fail; }
|
||||||
|
|
||||||
|
I2CS |= bmSTART;
|
||||||
|
I2DAT = 0xA3; /* (EEPROM_ADDR << 1) | 1 = read */
|
||||||
|
if (!i2c_wait_done()) goto fail;
|
||||||
|
if (!(I2CS & bmACK)) { last_error = ERR_I2C_NAK; goto fail; }
|
||||||
|
|
||||||
|
I2CS |= bmLASTRD; /* single byte read */
|
||||||
|
i = I2DAT; /* dummy read to trigger clock */
|
||||||
|
if (!i2c_wait_done()) goto fail;
|
||||||
|
I2CS |= bmSTOP;
|
||||||
|
i2c_buf[0] = I2DAT;
|
||||||
|
|
||||||
|
i2c_wait_stop();
|
||||||
|
return TRUE;
|
||||||
|
|
||||||
|
fail:
|
||||||
|
I2CS |= bmSTOP;
|
||||||
|
i2c_wait_stop();
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Read 20 bytes sequentially from EEPROM into i2c_buf[].
|
||||||
|
* START -> 0xA3 -> data[0..19] -> STOP
|
||||||
|
*
|
||||||
|
* The EEPROM auto-increments its address pointer after each byte.
|
||||||
|
* After eeprom_check_present() sets addr to 0x3FFF+1 = 0x4000,
|
||||||
|
* successive calls read consecutive 20-byte PLL config blocks.
|
||||||
|
*
|
||||||
|
* Zero parameters to minimize DSEG (hardcodes EEPROM_ADDR and len=20).
|
||||||
|
*/
|
||||||
|
static BOOL eeprom_read_block(void) {
|
||||||
|
BYTE i;
|
||||||
|
|
||||||
|
I2CS |= bmSTART;
|
||||||
|
I2DAT = 0xA3; /* (EEPROM_ADDR << 1) | 1 = read */
|
||||||
|
if (!i2c_wait_done()) goto fail;
|
||||||
|
if (!(I2CS & bmACK)) { last_error = ERR_I2C_NAK; goto fail; }
|
||||||
|
|
||||||
|
i = I2DAT; /* dummy read (value discarded; i reused as loop counter) */
|
||||||
|
|
||||||
|
for (i = 0; i < 20; i++) {
|
||||||
|
if (!i2c_wait_done()) goto fail;
|
||||||
|
if (i == 18) I2CS |= bmLASTRD;
|
||||||
|
if (i == 19) I2CS |= bmSTOP;
|
||||||
|
i2c_buf[i] = I2DAT;
|
||||||
|
}
|
||||||
|
|
||||||
|
i2c_wait_stop();
|
||||||
|
return TRUE;
|
||||||
|
|
||||||
|
fail:
|
||||||
|
I2CS |= bmSTOP;
|
||||||
|
i2c_wait_stop();
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
/* ---------- Software watchdog ---------- */
|
/* ---------- Software watchdog ---------- */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -381,15 +502,15 @@ static void wdt_init(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Write one byte to a BCM4500 direct I2C register (subaddr).
|
* Write one byte to a BCM4500 register via BCM3440 tuner gateway.
|
||||||
*/
|
*/
|
||||||
static BOOL bcm_direct_write(BYTE reg, BYTE val) {
|
static BOOL bcm_direct_write(BYTE reg, BYTE val) {
|
||||||
return i2c_write_timeout(BCM4500_ADDR, reg, val);
|
return i2c_write_timeout(BCM4500_ADDR, reg, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Read one byte from a BCM4500 direct I2C register using
|
* Read one byte from a BCM4500 register via BCM3440 tuner gateway,
|
||||||
* combined write-read with repeated START.
|
* using combined write-read with repeated START.
|
||||||
*/
|
*/
|
||||||
static BOOL bcm_direct_read(BYTE reg, BYTE *val) {
|
static BOOL bcm_direct_read(BYTE reg, BYTE *val) {
|
||||||
return i2c_combined_read(BCM4500_ADDR, reg, 1, val);
|
return i2c_combined_read(BCM4500_ADDR, reg, 1, val);
|
||||||
@ -442,8 +563,19 @@ static BOOL bcm_indirect_write_block(BYTE page, __xdata BYTE *data, BYTE len) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Poll BCM4500 for readiness. Reads status registers and waits
|
* Poll BCM4500 for command completion via gateway register A8.
|
||||||
* for the command register to indicate idle.
|
*
|
||||||
|
* The BCM3440 gateway's A8 register does not always clear bit 0 after
|
||||||
|
* the BCM4500 DSP processes an indirect write command, even though the
|
||||||
|
* write succeeds internally (confirmed by reading the direct BCM4500
|
||||||
|
* address 0x08 where A8 bit 0 does clear and bit 1 becomes set).
|
||||||
|
*
|
||||||
|
* The stock firmware's 0x20C5 polls the same gateway A8 via 0x2258
|
||||||
|
* and expects bit 0=0, bit 1=1. On our device, bit 1 never becomes 1
|
||||||
|
* through the gateway after writes, but the init data IS applied.
|
||||||
|
*
|
||||||
|
* Strategy: poll for early completion, but treat timeout as success
|
||||||
|
* with a settling delay rather than hard failure.
|
||||||
*/
|
*/
|
||||||
static BOOL bcm_poll_ready(void) {
|
static BOOL bcm_poll_ready(void) {
|
||||||
BYTE i, val;
|
BYTE i, val;
|
||||||
@ -452,19 +584,21 @@ static BOOL bcm_poll_ready(void) {
|
|||||||
if (!(val & 0x01))
|
if (!(val & 0x01))
|
||||||
return TRUE;
|
return TRUE;
|
||||||
} else {
|
} else {
|
||||||
return FALSE; /* I2C error, last_error already set */
|
return FALSE; /* I2C error */
|
||||||
}
|
}
|
||||||
delay(2);
|
delay(2);
|
||||||
}
|
}
|
||||||
last_error = ERR_BCM_TIMEOUT;
|
delay(5); /* settling time for gateway A8 timeout path */
|
||||||
return FALSE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Write one block of initialization data to BCM4500 indirect registers.
|
* Write one block of initialization data to BCM4500 indirect registers.
|
||||||
* Replicates FUN_CODE_0ddd's per-iteration I2C sequence from stock firmware:
|
* Replicates FUN_CODE_0ddd's per-iteration I2C sequence from stock firmware:
|
||||||
|
* 0. Wait for BCM4500 readiness (stock calls 0x2000 before each block)
|
||||||
* 1. Write 0x00 to reg 0xA6 (page select = page 0)
|
* 1. Write 0x00 to reg 0xA6 (page select = page 0)
|
||||||
* 2. Write data[0..len-1] to reg 0xA7 (data buffer, auto-increment)
|
* 2. Write data[0..len-1] to reg 0xA7 (multi-byte I2C write; the BCM3440
|
||||||
|
* gateway does NOT auto-increment for A7 — it uses FIFO mode)
|
||||||
* 3. Write 0x00 to reg 0xA7 (trailing zero -- stock firmware sends this)
|
* 3. Write 0x00 to reg 0xA7 (trailing zero -- stock firmware sends this)
|
||||||
* 4. Write 0x03 to reg 0xA8 (commit indirect write)
|
* 4. Write 0x03 to reg 0xA8 (commit indirect write)
|
||||||
* 5. Wait for BCM4500 to finish processing
|
* 5. Wait for BCM4500 to finish processing
|
||||||
@ -480,7 +614,10 @@ static BOOL bcm_write_init_block(const __code BYTE *data, BYTE len) {
|
|||||||
for (i = 0; i < len; i++)
|
for (i = 0; i < len; i++)
|
||||||
i2c_buf[i] = data[i];
|
i2c_buf[i] = data[i];
|
||||||
|
|
||||||
/* Write data bytes to 0xA7 */
|
/* Multi-byte write to A7 — the BCM3440 gateway buffers these in a FIFO
|
||||||
|
* (confirmed by stock firmware disassembly at 0x0E29: same multi-byte
|
||||||
|
* write pattern). The gateway does NOT auto-increment register address
|
||||||
|
* for writes targeting A7. */
|
||||||
if (!i2c_write_multi_timeout(BCM4500_ADDR, BCM_REG_DATA, len, i2c_buf))
|
if (!i2c_write_multi_timeout(BCM4500_ADDR, BCM_REG_DATA, len, i2c_buf))
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
@ -496,9 +633,133 @@ static BOOL bcm_write_init_block(const __code BYTE *data, BYTE len) {
|
|||||||
return bcm_poll_ready();
|
return bcm_poll_ready();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Load PLL configuration from calibration EEPROM into BCM4500.
|
||||||
|
*
|
||||||
|
* Reverse-engineered from stock firmware FUN_CODE_10F2 (0x10F2-0x11FF).
|
||||||
|
* The AT24C256 EEPROM at I2C addr 0x51 stores BCM4500 DSP microcode at
|
||||||
|
* address 0x4000 (~8.5 KB in 536 blocks). A9/AA are address registers,
|
||||||
|
* AB+ is the data port with auto-increment. Without loading this
|
||||||
|
* firmware, the BCM4500 DSP core never starts (I2C responds but all
|
||||||
|
* registers read 0x00).
|
||||||
|
*
|
||||||
|
* EEPROM data format (20-byte blocks, sequential from addr 0x4000):
|
||||||
|
* buf[0] = count of data bytes (0 = end sentinel, max 16)
|
||||||
|
* buf[1] = register 0xA9 value (BCM4500 address high / page)
|
||||||
|
* buf[2] = register 0xAA value (BCM4500 address low / offset)
|
||||||
|
* buf[3] = (unused)
|
||||||
|
* buf[4..4+count-1] = data written to 0xAB with auto-increment
|
||||||
|
*
|
||||||
|
* Protocol:
|
||||||
|
* 1. Read 1 byte from EEPROM addr 0x3FFF (presence check, sets ptr to 0x4000)
|
||||||
|
* 2. Write 0x01 to BCM4500 reg 0xA0 (enter config mode)
|
||||||
|
* 3. Loop: read 20-byte block -> write [A9,AA,data] in single txn -> repeat until count=0
|
||||||
|
* 4. Write 0x00 to BCM4500 reg 0xA0 (exit config mode)
|
||||||
|
*/
|
||||||
|
static BOOL bcm4500_load_pll_config(void) {
|
||||||
|
BYTE count;
|
||||||
|
WORD blocks_left;
|
||||||
|
|
||||||
|
/* Initialize diagnostics to "not reached" defaults */
|
||||||
|
pll_diag[0] = 0; /* eeprom present */
|
||||||
|
pll_diag[1] = 0xFF; /* first count */
|
||||||
|
pll_diag[2] = 0; /* blocks written (saturates at 255) */
|
||||||
|
pll_diag[3] = 0xFF; /* last A9 */
|
||||||
|
pll_diag[4] = 0xFF; /* last AA */
|
||||||
|
pll_diag[5] = 0xFF; /* last AB count */
|
||||||
|
pll_diag[6] = 0xFF; /* config exit */
|
||||||
|
pll_diag[7] = 0; /* overall result */
|
||||||
|
|
||||||
|
/* Step 1: Check EEPROM presence (reads 1 byte from 0x3FFF, sets
|
||||||
|
* pointer to 0x4000 where BCM4500 firmware blocks begin) */
|
||||||
|
if (!eeprom_check_present())
|
||||||
|
return FALSE;
|
||||||
|
pll_diag[0] = 1;
|
||||||
|
|
||||||
|
/* Step 2: Enter BCM4500 config mode */
|
||||||
|
if (!bcm_direct_write(BCM_REG_CFG_MODE, 0x01))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
delay(5); /* let BCM4500 enter config mode */
|
||||||
|
|
||||||
|
/* Step 3: Load BCM4500 firmware from EEPROM.
|
||||||
|
*
|
||||||
|
* EEPROM 0x4000+ contains BCM4500 microcode in 20-byte blocks:
|
||||||
|
* [count, A9_val, AA_val, unused, data[count]]
|
||||||
|
* A9/AA serve as address registers; data goes to AB+ with auto-increment.
|
||||||
|
* Sentinel: count=0.
|
||||||
|
*
|
||||||
|
* Typical firmware: ~536 blocks, ~8.5 KB payload.
|
||||||
|
* Loop bounded to 820 (max possible in 16KB EEPROM half). */
|
||||||
|
for (blocks_left = 820; blocks_left > 0; blocks_left--) {
|
||||||
|
if (!eeprom_read_block())
|
||||||
|
goto fail_exit;
|
||||||
|
|
||||||
|
count = i2c_buf[0];
|
||||||
|
|
||||||
|
/* Capture first block's count for diagnostics */
|
||||||
|
if (pll_diag[2] == 0 && pll_diag[1] == 0xFF)
|
||||||
|
pll_diag[1] = count;
|
||||||
|
|
||||||
|
if (count == 0)
|
||||||
|
break; /* end sentinel -- firmware loaded */
|
||||||
|
|
||||||
|
/* Bounds check: 20-byte block has 4 header bytes, so max
|
||||||
|
* data count is 16. Reject corrupted EEPROM blocks. */
|
||||||
|
if (count > 16)
|
||||||
|
goto fail_exit;
|
||||||
|
|
||||||
|
/* Capture last-written values for diagnostics */
|
||||||
|
pll_diag[3] = i2c_buf[1]; /* A9 (address high) */
|
||||||
|
pll_diag[4] = i2c_buf[2]; /* AA (address low) */
|
||||||
|
pll_diag[5] = count; /* data byte count */
|
||||||
|
|
||||||
|
/* Single I2C transaction: A9 + AA + data with auto-increment.
|
||||||
|
* Stock firmware writes these together -- the BCM4500 requires the
|
||||||
|
* address (A9/AA) and data (AB+) in one transaction to latch.
|
||||||
|
* Shift data down by 1 to eliminate unused byte3 padding:
|
||||||
|
* Before: buf[1]=A9, buf[2]=AA, buf[3]=0x00, buf[4..]=data
|
||||||
|
* After: buf[1]=A9, buf[2]=AA, buf[3..]=data */
|
||||||
|
{ BYTE si;
|
||||||
|
for (si = 0; si < i2c_buf[0]; si++)
|
||||||
|
i2c_buf[3 + si] = i2c_buf[4 + si];
|
||||||
|
}
|
||||||
|
for (count = 5; count > 0; count--) {
|
||||||
|
if (i2c_write_multi_timeout(BCM4500_ADDR, BCM_REG_PLL_A9,
|
||||||
|
2 + i2c_buf[0], &i2c_buf[1]))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count == 0)
|
||||||
|
goto fail_exit;
|
||||||
|
|
||||||
|
/* Saturating block counter for diagnostics (BYTE, caps at 255) */
|
||||||
|
if (pll_diag[2] < 255)
|
||||||
|
pll_diag[2]++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (blocks_left == 0)
|
||||||
|
goto fail_exit; /* no sentinel found -- EEPROM corrupt */
|
||||||
|
|
||||||
|
/* Step 4: Exit config mode */
|
||||||
|
if (!bcm_direct_write(BCM_REG_CFG_MODE, 0x00)) {
|
||||||
|
pll_diag[6] = 0;
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
pll_diag[6] = 1;
|
||||||
|
delay(2);
|
||||||
|
pll_diag[7] = 1;
|
||||||
|
return TRUE;
|
||||||
|
|
||||||
|
fail_exit:
|
||||||
|
bcm_direct_write(BCM_REG_CFG_MODE, 0x00); /* best-effort cleanup */
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* BCM4500 full boot sequence, reverse-engineered from stock firmware
|
* BCM4500 full boot sequence, reverse-engineered from stock firmware
|
||||||
* FUN_CODE_1D4F (reset/power) + FUN_CODE_0ddd (register init).
|
* FUN_CODE_1D4F (reset/power) + FUN_CODE_10F2 (PLL config from EEPROM)
|
||||||
|
* + FUN_CODE_0ddd (register init blocks).
|
||||||
*
|
*
|
||||||
* GPIO sequence from disassembly:
|
* GPIO sequence from disassembly:
|
||||||
* P3 |= 0xE0 -- P3.7, P3.6, P3.5 HIGH (control lines idle)
|
* P3 |= 0xE0 -- P3.7, P3.6, P3.5 HIGH (control lines idle)
|
||||||
@ -507,7 +768,8 @@ static BOOL bcm_write_init_block(const __code BYTE *data, BYTE len) {
|
|||||||
* P0.1 set, P0.2 clr -- power supply enable
|
* P0.1 set, P0.2 clr -- power supply enable
|
||||||
* delay(30) -- wait for power settle
|
* delay(30) -- wait for power settle
|
||||||
* P0 |= 0x20 -- P0.5 HIGH = release BCM4500 from RESET
|
* P0 |= 0x20 -- P0.5 HIGH = release BCM4500 from RESET
|
||||||
* Write 3 register initialization blocks
|
* Load PLL config from EEPROM into BCM4500 (A9/AA/AB registers)
|
||||||
|
* Write 3 register initialization blocks (A6/A7/A8 indirect protocol)
|
||||||
*/
|
*/
|
||||||
static BOOL bcm4500_boot(void) {
|
static BOOL bcm4500_boot(void) {
|
||||||
boot_stage = 1; /* Stage 1: GPIO setup */
|
boot_stage = 1; /* Stage 1: GPIO setup */
|
||||||
@ -544,26 +806,48 @@ static BOOL bcm4500_boot(void) {
|
|||||||
|
|
||||||
boot_stage = 3; /* Stage 3: I2C probe */
|
boot_stage = 3; /* Stage 3: I2C probe */
|
||||||
|
|
||||||
/* Verify BCM4500 is alive on I2C before attempting register init.
|
/* Verify tuner+demod are alive on I2C before attempting register init.
|
||||||
* If we can't read a direct register, the chip didn't come out of reset. */
|
* Reads via BCM3440 gateway (0x10) -- if the tuner or demod is
|
||||||
|
* unpowered or stuck in reset, this combined read will NAK. */
|
||||||
if (!bcm_direct_read(BCM_REG_STATUS, &i2c_rd[0]))
|
if (!bcm_direct_read(BCM_REG_STATUS, &i2c_rd[0]))
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
boot_stage = 4; /* Stage 4: register init block 0 */
|
/* Stage 4: BCM4500 firmware load from EEPROM.
|
||||||
|
* vc_diag[0] bit 0 = skip this step (set via wIndex boot flags). */
|
||||||
|
boot_stage = 4;
|
||||||
|
|
||||||
/* Initialize BCM4500 registers -- 3 blocks from stock firmware */
|
if (!(vc_diag[0] & 0x01)) {
|
||||||
|
/* Load BCM4500 DSP firmware from calibration EEPROM (AT24C256 @ 0x51).
|
||||||
|
*
|
||||||
|
* The stock firmware's FUN_CODE_10F2 does this:
|
||||||
|
* reads ~8.5 KB of BCM4500 microcode from EEPROM 0x4000+ and writes
|
||||||
|
* to BCM4500 via registers A0 (config mode), A9/AA (address), and
|
||||||
|
* AB (data, multi-byte with auto-increment). ~536 blocks of 20 bytes.
|
||||||
|
*
|
||||||
|
* If EEPROM is absent, we continue — init blocks may still
|
||||||
|
* partially configure the chip for diagnostics. */
|
||||||
|
bcm4500_load_pll_config();
|
||||||
|
|
||||||
|
/* vc_diag[0] bit 1: extended DSP startup delay after download */
|
||||||
|
if (vc_diag[0] & 0x02)
|
||||||
|
delay(200);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Stages 5-7: register init blocks.
|
||||||
|
* vc_diag[0] bit 2 = skip this step. */
|
||||||
|
if (!(vc_diag[0] & 0x04)) {
|
||||||
|
boot_stage = 5;
|
||||||
if (!bcm_write_init_block(bcm_init_block0, BCM_INIT_BLOCK0_LEN))
|
if (!bcm_write_init_block(bcm_init_block0, BCM_INIT_BLOCK0_LEN))
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
boot_stage = 5; /* Stage 5: register init block 1 */
|
boot_stage = 6;
|
||||||
|
|
||||||
if (!bcm_write_init_block(bcm_init_block1, BCM_INIT_BLOCK1_LEN))
|
if (!bcm_write_init_block(bcm_init_block1, BCM_INIT_BLOCK1_LEN))
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
boot_stage = 6; /* Stage 6: register init block 2 */
|
boot_stage = 7;
|
||||||
|
|
||||||
if (!bcm_write_init_block(bcm_init_block2, BCM_INIT_BLOCK2_LEN))
|
if (!bcm_write_init_block(bcm_init_block2, BCM_INIT_BLOCK2_LEN))
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
boot_stage = 0xFF; /* Success */
|
boot_stage = 0xFF; /* Success */
|
||||||
return TRUE;
|
return TRUE;
|
||||||
@ -1444,7 +1728,7 @@ static void do_tune(void) {
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
/* Write all 12 configuration bytes to BCM4500 data register (0xA7).
|
/* Write all 12 configuration bytes to BCM4500 data register (0xA7).
|
||||||
* Uses our timeout-protected multi-byte write instead of fx2lib i2c_write(). */
|
* Multi-byte I2C write — BCM3440 gateway uses FIFO mode for A7. */
|
||||||
if (!i2c_write_multi_timeout(BCM4500_ADDR, BCM_REG_DATA, 12, tune_data))
|
if (!i2c_write_multi_timeout(BCM4500_ADDR, BCM_REG_DATA, 12, tune_data))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -1615,12 +1899,19 @@ BOOL handle_vendorcommand(BYTE cmd) {
|
|||||||
boot_stage = 0xE3;
|
boot_stage = 0xE3;
|
||||||
}
|
}
|
||||||
} else if (wval) {
|
} else if (wval) {
|
||||||
|
/* wIndex serves as boot flags for diagnostic A/B testing:
|
||||||
|
* bit 0 (0x01): skip EEPROM firmware download
|
||||||
|
* bit 1 (0x02): add 200ms DSP startup delay after download
|
||||||
|
* bit 2 (0x04): skip register init blocks
|
||||||
|
* Normal boot: wIndex=0. */
|
||||||
|
vc_diag[0] = (BYTE)SETUP_INDEX();
|
||||||
if (bcm4500_boot()) {
|
if (bcm4500_boot()) {
|
||||||
config_status |= (BM_STARTED | BM_FW_LOADED);
|
config_status |= (BM_STARTED | BM_FW_LOADED);
|
||||||
} else {
|
} else {
|
||||||
bcm4500_shutdown();
|
bcm4500_shutdown();
|
||||||
config_status &= ~(BM_STARTED | BM_FW_LOADED);
|
config_status &= ~(BM_STARTED | BM_FW_LOADED);
|
||||||
}
|
}
|
||||||
|
vc_diag[0] = 0;
|
||||||
} else {
|
} else {
|
||||||
bcm4500_shutdown();
|
bcm4500_shutdown();
|
||||||
config_status &= ~(BM_STARTED | BM_FW_LOADED);
|
config_status &= ~(BM_STARTED | BM_FW_LOADED);
|
||||||
@ -1736,14 +2027,22 @@ BOOL handle_vendorcommand(BYTE cmd) {
|
|||||||
do_spectrum_sweep();
|
do_spectrum_sweep();
|
||||||
return TRUE;
|
return TRUE;
|
||||||
|
|
||||||
/* 0xB1: RAW_DEMOD_READ -- read BCM4500 register */
|
/* 0xB1: RAW_DEMOD_READ -- read BCM4500 register
|
||||||
case RAW_DEMOD_READ:
|
* wIndex=0: indirect read (page = wValue)
|
||||||
|
* wIndex=1: direct read (register = wValue) */
|
||||||
|
case RAW_DEMOD_READ: {
|
||||||
|
WORD ridx;
|
||||||
|
ridx = SETUP_INDEX();
|
||||||
val = 0;
|
val = 0;
|
||||||
|
if (ridx == 1)
|
||||||
|
bcm_direct_read((BYTE)wval, &val);
|
||||||
|
else
|
||||||
bcm_indirect_read((BYTE)wval, &val);
|
bcm_indirect_read((BYTE)wval, &val);
|
||||||
EP0BUF[0] = val;
|
EP0BUF[0] = val;
|
||||||
EP0BCH = 0;
|
EP0BCH = 0;
|
||||||
EP0BCL = 1;
|
EP0BCL = 1;
|
||||||
return TRUE;
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
/* 0xB2: RAW_DEMOD_WRITE -- write BCM4500 register */
|
/* 0xB2: RAW_DEMOD_WRITE -- write BCM4500 register */
|
||||||
case RAW_DEMOD_WRITE: {
|
case RAW_DEMOD_WRITE: {
|
||||||
@ -1816,50 +2115,50 @@ BOOL handle_vendorcommand(BYTE cmd) {
|
|||||||
* Returns 8 bytes: [write_A6_ok, readback_A6, write_A8_ok, readback_A8,
|
* Returns 8 bytes: [write_A6_ok, readback_A6, write_A8_ok, readback_A8,
|
||||||
* readback_A7, direct_read_A6, direct_read_A7, direct_read_A8] */
|
* readback_A7, direct_read_A6, direct_read_A7, direct_read_A8] */
|
||||||
case 0xB6: {
|
case 0xB6: {
|
||||||
/* Use shared xdata diag buffer to save DSEG */
|
/* Use shared xdata diag buffer to save DSEG.
|
||||||
vc_diag[0] = (BYTE)wval; /* target_reg */
|
* wValue = page to read, wIndex = A8 command (0 defaults to READ) */
|
||||||
|
BYTE cmd_b6;
|
||||||
|
vc_diag[0] = (BYTE)wval; /* target page */
|
||||||
|
cmd_b6 = (BYTE)SETUP_INDEX();
|
||||||
|
if (cmd_b6 == 0) cmd_b6 = BCM_CMD_READ;
|
||||||
|
|
||||||
/* Step 1: Write target register to page select (0xA6) */
|
/* Step 1: Write page to A6 */
|
||||||
vc_diag[1] = bcm_direct_write(BCM_REG_PAGE, vc_diag[0]) ? 0x01 : 0x00;
|
vc_diag[1] = bcm_direct_write(BCM_REG_PAGE, vc_diag[0]) ? 0x01 : 0x00;
|
||||||
|
|
||||||
/* Step 2: Read back 0xA6 to verify write */
|
/* Step 2: Read back A6 */
|
||||||
vc_diag[2] = 0xEE;
|
vc_diag[2] = 0xEE;
|
||||||
i2c_combined_read(BCM4500_ADDR, BCM_REG_PAGE, 1, &vc_diag[2]);
|
i2c_combined_read(BCM4500_ADDR, BCM_REG_PAGE, 1, &vc_diag[2]);
|
||||||
|
|
||||||
/* Step 3: Write read command (0x01) to 0xA8 */
|
/* Step 3: Write command to A8 */
|
||||||
vc_diag[3] = bcm_direct_write(BCM_REG_CMD, BCM_CMD_READ) ? 0x01 : 0x00;
|
vc_diag[3] = bcm_direct_write(BCM_REG_CMD, cmd_b6) ? 0x01 : 0x00;
|
||||||
|
|
||||||
/* Step 4: Read back 0xA8 to check command status */
|
/* Step 4: IMMEDIATE readback of A8 (no delay) */
|
||||||
vc_diag[4] = 0xEE;
|
vc_diag[4] = 0xEE;
|
||||||
i2c_combined_read(BCM4500_ADDR, BCM_REG_CMD, 1, &vc_diag[4]);
|
i2c_combined_read(BCM4500_ADDR, BCM_REG_CMD, 1, &vc_diag[4]);
|
||||||
|
|
||||||
/* Step 5: Small delay for command execution */
|
/* Step 5: Delay for command execution */
|
||||||
delay(2);
|
delay(2);
|
||||||
|
|
||||||
/* Step 6: Read 0xA7 (data register) — this is the result */
|
/* Step 6: Read A8 AGAIN after delay */
|
||||||
vc_diag[5] = 0xEE;
|
vc_diag[5] = 0xEE;
|
||||||
i2c_combined_read(BCM4500_ADDR, BCM_REG_DATA, 1, &vc_diag[5]);
|
i2c_combined_read(BCM4500_ADDR, BCM_REG_CMD, 1, &vc_diag[5]);
|
||||||
|
|
||||||
/* Step 7: Read back all three control regs for final state */
|
/* Step 7: Read A7 (data result) */
|
||||||
vc_diag[6] = 0xEE;
|
|
||||||
i2c_combined_read(BCM4500_ADDR, BCM_REG_PAGE, 1, &vc_diag[6]);
|
|
||||||
vc_diag[7] = 0xEE;
|
|
||||||
i2c_combined_read(BCM4500_ADDR, BCM_REG_DATA, 1, &vc_diag[7]);
|
|
||||||
|
|
||||||
EP0BUF[0] = vc_diag[1]; /* write_A6_ok */
|
|
||||||
EP0BUF[1] = vc_diag[2]; /* readback_A6 */
|
|
||||||
EP0BUF[2] = vc_diag[3]; /* write_A8_ok */
|
|
||||||
EP0BUF[3] = vc_diag[4]; /* readback_A8 */
|
|
||||||
EP0BUF[4] = vc_diag[5]; /* readback_A7 */
|
|
||||||
EP0BUF[5] = vc_diag[6]; /* direct_read_A6 */
|
|
||||||
|
|
||||||
/* Read remaining registers directly into EP0BUF */
|
|
||||||
vc_diag[6] = 0xEE;
|
vc_diag[6] = 0xEE;
|
||||||
i2c_combined_read(BCM4500_ADDR, BCM_REG_DATA, 1, &vc_diag[6]);
|
i2c_combined_read(BCM4500_ADDR, BCM_REG_DATA, 1, &vc_diag[6]);
|
||||||
EP0BUF[6] = vc_diag[6]; /* direct_read_A7 */
|
|
||||||
|
/* Step 8: Final A6 state */
|
||||||
vc_diag[7] = 0xEE;
|
vc_diag[7] = 0xEE;
|
||||||
i2c_combined_read(BCM4500_ADDR, BCM_REG_CMD, 1, &vc_diag[7]);
|
i2c_combined_read(BCM4500_ADDR, BCM_REG_PAGE, 1, &vc_diag[7]);
|
||||||
EP0BUF[7] = vc_diag[7]; /* direct_read_A8 */
|
|
||||||
|
EP0BUF[0] = vc_diag[1]; /* write_A6_ok */
|
||||||
|
EP0BUF[1] = vc_diag[2]; /* A6 readback */
|
||||||
|
EP0BUF[2] = vc_diag[3]; /* write_A8_ok */
|
||||||
|
EP0BUF[3] = vc_diag[4]; /* A8 IMMEDIATE readback */
|
||||||
|
EP0BUF[4] = vc_diag[5]; /* A8 after 2ms delay */
|
||||||
|
EP0BUF[5] = vc_diag[6]; /* A7 data */
|
||||||
|
EP0BUF[6] = vc_diag[7]; /* A6 final */
|
||||||
|
EP0BUF[7] = cmd_b6; /* echo: command sent */
|
||||||
|
|
||||||
EP0BCH = 0;
|
EP0BCH = 0;
|
||||||
EP0BCL = 8;
|
EP0BCL = 8;
|
||||||
@ -2044,6 +2343,115 @@ BOOL handle_vendorcommand(BYTE cmd) {
|
|||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 0xC0: EEPROM_READ -- read bytes from EEPROM at 16-bit address
|
||||||
|
* wValue = 16-bit EEPROM address
|
||||||
|
* wIndex = number of bytes to read (1-64)
|
||||||
|
* Returns the raw EEPROM data */
|
||||||
|
case EEPROM_READ: {
|
||||||
|
BYTE ei, elen;
|
||||||
|
elen = (BYTE)SETUP_INDEX();
|
||||||
|
if (elen > 64) elen = 64;
|
||||||
|
if (elen == 0) elen = 1;
|
||||||
|
|
||||||
|
/* Set EEPROM address: START → 0xA2 → addr_hi → addr_lo */
|
||||||
|
I2CS |= bmSTART;
|
||||||
|
I2DAT = 0xA2;
|
||||||
|
if (!i2c_wait_done()) goto ee_fail;
|
||||||
|
if (!(I2CS & bmACK)) goto ee_fail;
|
||||||
|
I2DAT = (BYTE)(wval >> 8); /* address high byte */
|
||||||
|
if (!i2c_wait_done()) goto ee_fail;
|
||||||
|
if (!(I2CS & bmACK)) goto ee_fail;
|
||||||
|
I2DAT = (BYTE)(wval); /* address low byte */
|
||||||
|
if (!i2c_wait_done()) goto ee_fail;
|
||||||
|
if (!(I2CS & bmACK)) goto ee_fail;
|
||||||
|
|
||||||
|
/* Repeated START → 0xA3 → read data */
|
||||||
|
I2CS |= bmSTART;
|
||||||
|
I2DAT = 0xA3;
|
||||||
|
if (!i2c_wait_done()) goto ee_fail;
|
||||||
|
if (!(I2CS & bmACK)) goto ee_fail;
|
||||||
|
|
||||||
|
if (elen == 1) I2CS |= bmLASTRD;
|
||||||
|
ei = I2DAT; /* dummy read */
|
||||||
|
|
||||||
|
for (ei = 0; ei < elen; ei++) {
|
||||||
|
if (!i2c_wait_done()) goto ee_fail;
|
||||||
|
if (ei == elen - 2) I2CS |= bmLASTRD;
|
||||||
|
if (ei == elen - 1) I2CS |= bmSTOP;
|
||||||
|
EP0BUF[ei] = I2DAT;
|
||||||
|
}
|
||||||
|
i2c_wait_stop();
|
||||||
|
EP0BCH = 0;
|
||||||
|
EP0BCL = elen;
|
||||||
|
return TRUE;
|
||||||
|
|
||||||
|
ee_fail:
|
||||||
|
I2CS |= bmSTOP;
|
||||||
|
i2c_wait_stop();
|
||||||
|
for (ei = 0; ei < elen; ei++)
|
||||||
|
EP0BUF[ei] = 0xFF;
|
||||||
|
EP0BCH = 0;
|
||||||
|
EP0BCL = elen;
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 0xBF: GET_PLL_DIAG -- PLL config diagnostic from last boot
|
||||||
|
* Returns 10 bytes:
|
||||||
|
* [0] eeprom_present (1=ok, 0=fail)
|
||||||
|
* [1] first_block_count (0xFF if not reached)
|
||||||
|
* [2] blocks_written
|
||||||
|
* [3] last_A9 value (0xFF if none)
|
||||||
|
* [4] last_AA value (0xFF if none)
|
||||||
|
* [5] last_AB_count (0xFF if none)
|
||||||
|
* [6] config_exit (1=ok, 0=fail, 0xFF=not reached)
|
||||||
|
* [7] overall_result (1=ok, 0=fail)
|
||||||
|
* [8] boot_stage (0xFF=complete, else stage where it stopped)
|
||||||
|
* [9] last_error snapshot */
|
||||||
|
case GET_PLL_DIAG: {
|
||||||
|
BYTE di;
|
||||||
|
for (di = 0; di < 8; di++)
|
||||||
|
EP0BUF[di] = pll_diag[di];
|
||||||
|
EP0BUF[8] = boot_stage;
|
||||||
|
EP0BUF[9] = last_error;
|
||||||
|
EP0BCH = 0;
|
||||||
|
EP0BCL = 10;
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 0xC1: I2C_RAW_WRITE -- write 1 byte to any I2C register
|
||||||
|
* wValue = 7-bit I2C address
|
||||||
|
* wIndex high byte = register, low byte = data
|
||||||
|
* Returns 1 byte: 0x01 on success, 0x00 on failure */
|
||||||
|
case 0xC1: {
|
||||||
|
WORD widx_c1 = SETUP_INDEX();
|
||||||
|
EP0BUF[0] = i2c_write_timeout((BYTE)wval,
|
||||||
|
(BYTE)(widx_c1 >> 8), (BYTE)widx_c1) ? 0x01 : 0x00;
|
||||||
|
EP0BCH = 0;
|
||||||
|
EP0BCL = 1;
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 0xC2: MULTI_WRITE_TEST -- auto-increment test for BCM3440 gateway.
|
||||||
|
* Writes 3 test bytes to register A7 as multi-byte I2C, then reads
|
||||||
|
* A6/A7/A8/A9. Returns 8 bytes. Uses vc_diag[] to avoid DSEG. */
|
||||||
|
case 0xC2:
|
||||||
|
i2c_buf[0] = 0xAA;
|
||||||
|
i2c_buf[1] = 0xBB;
|
||||||
|
i2c_buf[2] = 0xCC;
|
||||||
|
vc_diag[0] = i2c_write_multi_timeout(BCM4500_ADDR, BCM_REG_DATA, 3, i2c_buf) ? 1 : 0;
|
||||||
|
delay(1);
|
||||||
|
vc_diag[1] = 0xEE; bcm_direct_read(BCM_REG_PAGE, &vc_diag[1]);
|
||||||
|
vc_diag[2] = 0xEE; bcm_direct_read(BCM_REG_DATA, &vc_diag[2]);
|
||||||
|
vc_diag[3] = 0xEE; bcm_direct_read(BCM_REG_CMD, &vc_diag[3]);
|
||||||
|
vc_diag[4] = 0xEE; bcm_direct_read(0xA9, &vc_diag[4]);
|
||||||
|
EP0BUF[0] = vc_diag[0]; EP0BUF[1] = vc_diag[1];
|
||||||
|
EP0BUF[2] = vc_diag[2]; EP0BUF[3] = vc_diag[3];
|
||||||
|
EP0BUF[4] = vc_diag[4]; EP0BUF[5] = 0;
|
||||||
|
EP0BUF[6] = 0; EP0BUF[7] = 0;
|
||||||
|
EP0BCH = 0;
|
||||||
|
EP0BCL = 8;
|
||||||
|
return TRUE;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user