Fix BCM4500 boot: spurious I2C STOP corrupted FX2 controller
Removed I2CS bmSTOP "bus reset" from bcm4500_boot() and debug modes. Sending STOP with no active transaction puts the FX2 I2C controller into an inconsistent state where subsequent START+ACK detection fails. Root cause identified through incremental debug modes (wValue 0x80-0x85) on live hardware: mode 0x82 (with bmSTOP) fails, mode 0x85 (identical but without bmSTOP) succeeds. Raw I2C reads confirm BCM4500 is alive the entire time -- only the controller state is corrupted. BCM4500 now boots successfully in ~90ms. Three I2C devices found on bus: 0x08 (BCM4500), 0x10 (tuner/LNB), 0x51 (EEPROM). Also in this commit: - Timeout-protected I2C functions replacing fx2lib bare while loops - I2C bus scan and debug mode infrastructure - Kernel driver blacklist for dvb_usb_gp8psk - Test tools for incremental boot debugging - Technical findings documented in docs/boot-debug-findings.md
This commit is contained in:
parent
890a38bfa0
commit
d9f51548e0
7 changed files with 1296 additions and 51 deletions
|
|
@ -65,28 +65,78 @@
|
|||
#define BM_ARMED 0x80
|
||||
|
||||
/* GPIO pin definitions for v2.06 hardware */
|
||||
#define PIN_22KHZ 0x08 /* P0.3 */
|
||||
#define PIN_LNB_VOLT 0x10 /* P0.4 */
|
||||
#define PIN_DISEQC 0x80 /* P0.7 */
|
||||
#define PIN_PWR_EN 0x02 /* P0.1 -- power supply enable */
|
||||
#define PIN_PWR_DIS 0x04 /* P0.2 -- power supply disable */
|
||||
#define PIN_22KHZ 0x08 /* P0.3 */
|
||||
#define PIN_LNB_VOLT 0x10 /* P0.4 */
|
||||
#define PIN_BCM_RESET 0x20 /* P0.5 -- BCM4500 hardware reset (active LOW) */
|
||||
#define PIN_DISEQC 0x80 /* P0.7 */
|
||||
|
||||
/* configuration status byte -- stored in ordinary variable */
|
||||
static volatile BYTE config_status;
|
||||
|
||||
/* boot progress tracker for diagnostics (0=not started, 1-6=step, 0xFF=done) */
|
||||
static volatile BYTE boot_stage;
|
||||
|
||||
/* ISR flag */
|
||||
volatile __bit got_sud;
|
||||
|
||||
/* I2C scratch buffers in xdata */
|
||||
static __xdata BYTE i2c_buf[8];
|
||||
static __xdata BYTE i2c_buf[16];
|
||||
static __xdata BYTE i2c_rd[8];
|
||||
|
||||
/*
|
||||
* BCM4500 register initialization data extracted from stock v2.06 firmware.
|
||||
* FUN_CODE_0ddd writes these 3 blocks to BCM4500 indirect registers (page 0)
|
||||
* via the A6/A7/A8 control interface during BOOT_8PSK.
|
||||
*/
|
||||
static const __code BYTE bcm_init_block0[] = {
|
||||
0x06, 0x0b, 0x17, 0x38, 0x9f, 0xd9, 0x80
|
||||
};
|
||||
static const __code BYTE bcm_init_block1[] = {
|
||||
0x07, 0x09, 0x39, 0x4f, 0x00, 0x65, 0xb7, 0x10
|
||||
};
|
||||
static const __code BYTE bcm_init_block2[] = {
|
||||
0x0f, 0x0c, 0x09
|
||||
};
|
||||
#define BCM_INIT_BLOCK0_LEN 7
|
||||
#define BCM_INIT_BLOCK1_LEN 8
|
||||
#define BCM_INIT_BLOCK2_LEN 3
|
||||
|
||||
/* ---------- BCM4500 I2C helpers ---------- */
|
||||
|
||||
/*
|
||||
* I2C timeout: ~5ms at 48MHz CPU clock (4 clocks/cycle, ~12 MIPS).
|
||||
* At 400kHz I2C, one byte = 22.5us; 5ms gives >200x margin.
|
||||
* The FX2 I2C controller has no hardware timeout -- if a slave holds
|
||||
* SCL low (clock stretching), the master waits forever without this.
|
||||
*/
|
||||
#define I2C_TIMEOUT 6000
|
||||
|
||||
static BOOL i2c_wait_done(void) {
|
||||
WORD timeout = I2C_TIMEOUT;
|
||||
while (!(I2CS & bmDONE)) {
|
||||
if (--timeout == 0)
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static BOOL i2c_wait_stop(void) {
|
||||
WORD timeout = I2C_TIMEOUT;
|
||||
while (I2CS & bmSTOP) {
|
||||
if (--timeout == 0)
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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
|
||||
* 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;
|
||||
|
|
@ -95,23 +145,23 @@ static BOOL i2c_combined_read(BYTE addr, BYTE reg, BYTE len, BYTE *buf) {
|
|||
/* START + write address */
|
||||
I2CS |= bmSTART;
|
||||
I2DAT = addr << 1;
|
||||
while (!(I2CS & bmDONE))
|
||||
;
|
||||
if (!i2c_wait_done())
|
||||
goto fail;
|
||||
if (!(I2CS & bmACK))
|
||||
goto fail;
|
||||
|
||||
/* Write register address */
|
||||
I2DAT = reg;
|
||||
while (!(I2CS & bmDONE))
|
||||
;
|
||||
if (!i2c_wait_done())
|
||||
goto fail;
|
||||
if (!(I2CS & bmACK))
|
||||
goto fail;
|
||||
|
||||
/* REPEATED START + read address */
|
||||
I2CS |= bmSTART;
|
||||
I2DAT = (addr << 1) | 1;
|
||||
while (!(I2CS & bmDONE))
|
||||
;
|
||||
if (!i2c_wait_done())
|
||||
goto fail;
|
||||
if (!(I2CS & bmACK))
|
||||
goto fail;
|
||||
|
||||
|
|
@ -123,8 +173,8 @@ static BOOL i2c_combined_read(BYTE addr, BYTE reg, BYTE len, BYTE *buf) {
|
|||
tmp = I2DAT;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
while (!(I2CS & bmDONE))
|
||||
;
|
||||
if (!i2c_wait_done())
|
||||
goto fail;
|
||||
if (i == len - 2)
|
||||
I2CS |= bmLASTRD;
|
||||
if (i == len - 1)
|
||||
|
|
@ -132,25 +182,84 @@ static BOOL i2c_combined_read(BYTE addr, BYTE reg, BYTE len, BYTE *buf) {
|
|||
buf[i] = I2DAT;
|
||||
}
|
||||
|
||||
while (I2CS & bmSTOP)
|
||||
;
|
||||
i2c_wait_stop();
|
||||
return TRUE;
|
||||
|
||||
fail:
|
||||
I2CS |= bmSTOP;
|
||||
while (I2CS & bmSTOP)
|
||||
;
|
||||
i2c_wait_stop();
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/*
|
||||
* I2C write with timeout -- writes addr+reg+data without using fx2lib,
|
||||
* so we have full control over timeout behavior.
|
||||
* Sends: START -> (addr<<1) -> reg -> data -> STOP
|
||||
*/
|
||||
static BOOL i2c_write_timeout(BYTE addr, BYTE reg, BYTE val) {
|
||||
/* START + write address */
|
||||
I2CS |= bmSTART;
|
||||
I2DAT = addr << 1;
|
||||
if (!i2c_wait_done()) goto fail;
|
||||
if (!(I2CS & bmACK)) goto fail;
|
||||
|
||||
/* Register address */
|
||||
I2DAT = reg;
|
||||
if (!i2c_wait_done()) goto fail;
|
||||
if (!(I2CS & bmACK)) goto fail;
|
||||
|
||||
/* Data byte */
|
||||
I2DAT = val;
|
||||
if (!i2c_wait_done()) goto fail;
|
||||
|
||||
/* STOP */
|
||||
I2CS |= bmSTOP;
|
||||
i2c_wait_stop();
|
||||
return TRUE;
|
||||
|
||||
fail:
|
||||
I2CS |= bmSTOP;
|
||||
i2c_wait_stop();
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Multi-byte I2C write with timeout.
|
||||
* Sends: START -> (addr<<1) -> reg -> data[0..len-1] -> STOP
|
||||
*/
|
||||
static BOOL i2c_write_multi_timeout(BYTE addr, BYTE reg, BYTE len,
|
||||
__xdata BYTE *data) {
|
||||
BYTE i;
|
||||
|
||||
I2CS |= bmSTART;
|
||||
I2DAT = addr << 1;
|
||||
if (!i2c_wait_done()) goto fail;
|
||||
if (!(I2CS & bmACK)) goto fail;
|
||||
|
||||
I2DAT = reg;
|
||||
if (!i2c_wait_done()) goto fail;
|
||||
if (!(I2CS & bmACK)) goto fail;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
I2DAT = data[i];
|
||||
if (!i2c_wait_done()) goto fail;
|
||||
}
|
||||
|
||||
I2CS |= bmSTOP;
|
||||
i2c_wait_stop();
|
||||
return TRUE;
|
||||
|
||||
fail:
|
||||
I2CS |= bmSTOP;
|
||||
i2c_wait_stop();
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Write one byte to a BCM4500 direct I2C register (subaddr).
|
||||
* This writes to the I2C register directly, not through the
|
||||
* indirect protocol.
|
||||
*/
|
||||
static BOOL bcm_direct_write(BYTE reg, BYTE val) {
|
||||
i2c_buf[0] = val;
|
||||
return i2c_write(BCM4500_ADDR, 1, ®, 1, i2c_buf);
|
||||
return i2c_write_timeout(BCM4500_ADDR, reg, val);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -167,11 +276,10 @@ static BOOL bcm_direct_read(BYTE reg, BYTE *val) {
|
|||
* [0xA6] = page, [0xA7] = data, [0xA8] = 0x03 (write cmd)
|
||||
*/
|
||||
static BOOL bcm_indirect_write(BYTE reg, BYTE val) {
|
||||
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);
|
||||
return i2c_write_multi_timeout(BCM4500_ADDR, BCM_REG_PAGE, 3, i2c_rd);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -181,12 +289,11 @@ static BOOL bcm_indirect_write(BYTE reg, BYTE val) {
|
|||
* Then read the result from 0xA7.
|
||||
*/
|
||||
static BOOL bcm_indirect_read(BYTE reg, BYTE *val) {
|
||||
BYTE start_reg = BCM_REG_PAGE;
|
||||
/* page, placeholder data, read command — written to A6,A7,A8 in one shot */
|
||||
/* 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))
|
||||
if (!i2c_write_multi_timeout(BCM4500_ADDR, BCM_REG_PAGE, 3, i2c_rd))
|
||||
return FALSE;
|
||||
delay(1);
|
||||
return i2c_combined_read(BCM4500_ADDR, BCM_REG_DATA, 1, val);
|
||||
|
|
@ -197,15 +304,10 @@ static BOOL bcm_indirect_read(BYTE reg, BYTE *val) {
|
|||
* Page select, then N data bytes to 0xA7, then commit with 0x03.
|
||||
*/
|
||||
static BOOL bcm_indirect_write_block(BYTE page, __xdata BYTE *data, BYTE len) {
|
||||
BYTE reg;
|
||||
|
||||
reg = BCM_REG_PAGE;
|
||||
i2c_buf[0] = page;
|
||||
if (!i2c_write(BCM4500_ADDR, 1, ®, 1, i2c_buf))
|
||||
if (!bcm_direct_write(BCM_REG_PAGE, page))
|
||||
return FALSE;
|
||||
|
||||
reg = BCM_REG_DATA;
|
||||
if (!i2c_write(BCM4500_ADDR, 1, ®, len, data))
|
||||
if (!i2c_write_multi_timeout(BCM4500_ADDR, BCM_REG_DATA, len, data))
|
||||
return FALSE;
|
||||
|
||||
if (!bcm_direct_write(BCM_REG_CMD, BCM_CMD_WRITE))
|
||||
|
|
@ -220,16 +322,134 @@ static BOOL bcm_indirect_write_block(BYTE page, __xdata BYTE *data, BYTE len) {
|
|||
*/
|
||||
static BOOL bcm_poll_ready(void) {
|
||||
BYTE i, val;
|
||||
for (i = 0; i < 20; i++) {
|
||||
for (i = 0; i < 10; i++) {
|
||||
if (bcm_direct_read(BCM_REG_CMD, &val)) {
|
||||
if (!(val & 0x01))
|
||||
return TRUE;
|
||||
}
|
||||
delay(5);
|
||||
delay(2);
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Write one block of initialization data to BCM4500 indirect registers.
|
||||
* Replicates FUN_CODE_0ddd's per-iteration I2C sequence from stock firmware:
|
||||
* 1. Write 0x00 to reg 0xA6 (page select = page 0)
|
||||
* 2. Write data[0..len-1] to reg 0xA7 (data buffer, auto-increment)
|
||||
* 3. Write 0x00 to reg 0xA7 (trailing zero -- stock firmware sends this)
|
||||
* 4. Write 0x03 to reg 0xA8 (commit indirect write)
|
||||
* 5. Wait for BCM4500 to finish processing
|
||||
*/
|
||||
static BOOL bcm_write_init_block(const __code BYTE *data, BYTE len) {
|
||||
BYTE i;
|
||||
|
||||
/* Page select = 0 */
|
||||
if (!bcm_direct_write(BCM_REG_PAGE, 0x00))
|
||||
return FALSE;
|
||||
|
||||
/* Copy block data from code space to xdata scratch buffer */
|
||||
for (i = 0; i < len; i++)
|
||||
i2c_buf[i] = data[i];
|
||||
|
||||
/* Write data bytes to 0xA7 */
|
||||
if (!i2c_write_multi_timeout(BCM4500_ADDR, BCM_REG_DATA, len, i2c_buf))
|
||||
return FALSE;
|
||||
|
||||
/* Trailing zero to 0xA7 (stock firmware does this as separate write) */
|
||||
if (!bcm_direct_write(BCM_REG_DATA, 0x00))
|
||||
return FALSE;
|
||||
|
||||
/* Commit: write command 0x03 to 0xA8 */
|
||||
if (!bcm_direct_write(BCM_REG_CMD, BCM_CMD_WRITE))
|
||||
return FALSE;
|
||||
|
||||
/* Wait for BCM4500 to process the write */
|
||||
return bcm_poll_ready();
|
||||
}
|
||||
|
||||
/*
|
||||
* BCM4500 full boot sequence, reverse-engineered from stock firmware
|
||||
* FUN_CODE_1D4F (reset/power) + FUN_CODE_0ddd (register init).
|
||||
*
|
||||
* GPIO sequence from disassembly:
|
||||
* P3 |= 0xE0 -- P3.7, P3.6, P3.5 HIGH (control lines idle)
|
||||
* P0 &= ~0x20 -- P0.5 LOW = assert BCM4500 hardware RESET
|
||||
* I2C bus reset
|
||||
* P0.1 set, P0.2 clr -- power supply enable
|
||||
* delay(30) -- wait for power settle
|
||||
* P0 |= 0x20 -- P0.5 HIGH = release BCM4500 from RESET
|
||||
* Write 3 register initialization blocks
|
||||
*/
|
||||
static BOOL bcm4500_boot(void) {
|
||||
boot_stage = 1; /* Stage 1: GPIO setup */
|
||||
|
||||
/* Ensure fx2lib I2C functions won't spin forever */
|
||||
cancel_i2c_trans = FALSE;
|
||||
|
||||
/* P3.7, P3.6, P3.5 HIGH (idle state for control lines) */
|
||||
IOD |= 0xE0;
|
||||
|
||||
/* Assert BCM4500 hardware RESET (P0.5 LOW) */
|
||||
OEA |= PIN_BCM_RESET;
|
||||
IOA &= ~PIN_BCM_RESET;
|
||||
|
||||
/* NOTE: Do NOT send I2CS bmSTOP here. Sending STOP when no transaction
|
||||
* is active corrupts the FX2 I2C controller state, causing subsequent
|
||||
* START+ACK detection to fail. The I2C bus will be in a clean state
|
||||
* when we reach the probe step -- any prior transaction ended with STOP. */
|
||||
|
||||
/* Power on: P0.1 HIGH (enable), P0.2 LOW (disable off) */
|
||||
OEA |= (PIN_PWR_EN | PIN_PWR_DIS);
|
||||
IOA = (IOA & ~PIN_PWR_DIS) | PIN_PWR_EN;
|
||||
|
||||
boot_stage = 2; /* Stage 2: power settled, releasing reset */
|
||||
|
||||
/* Wait for power supply to settle (stock firmware uses 30 iterations) */
|
||||
delay(30);
|
||||
|
||||
/* Release BCM4500 from RESET (P0.5 HIGH) */
|
||||
IOA |= PIN_BCM_RESET;
|
||||
|
||||
/* Wait for BCM4500 internal POR and mask ROM boot to complete */
|
||||
delay(50);
|
||||
|
||||
boot_stage = 3; /* Stage 3: I2C probe */
|
||||
|
||||
/* Verify BCM4500 is alive on I2C before attempting register init.
|
||||
* If we can't read a direct register, the chip didn't come out of reset. */
|
||||
if (!bcm_direct_read(BCM_REG_STATUS, &i2c_rd[0]))
|
||||
return FALSE;
|
||||
|
||||
boot_stage = 4; /* Stage 4: register init block 0 */
|
||||
|
||||
/* Initialize BCM4500 registers -- 3 blocks from stock firmware */
|
||||
if (!bcm_write_init_block(bcm_init_block0, BCM_INIT_BLOCK0_LEN))
|
||||
return FALSE;
|
||||
|
||||
boot_stage = 5; /* Stage 5: register init block 1 */
|
||||
|
||||
if (!bcm_write_init_block(bcm_init_block1, BCM_INIT_BLOCK1_LEN))
|
||||
return FALSE;
|
||||
|
||||
boot_stage = 6; /* Stage 6: register init block 2 */
|
||||
|
||||
if (!bcm_write_init_block(bcm_init_block2, BCM_INIT_BLOCK2_LEN))
|
||||
return FALSE;
|
||||
|
||||
boot_stage = 0xFF; /* Success */
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/*
|
||||
* BCM4500 shutdown -- reverse of boot.
|
||||
* From stock firmware FUN_CODE_1D4F shutdown path at 0x1D93.
|
||||
*/
|
||||
static void bcm4500_shutdown(void) {
|
||||
/* Power off: P0.1 LOW (enable off), P0.2 HIGH (disable) */
|
||||
IOA = (IOA & ~PIN_PWR_EN) | PIN_PWR_DIS;
|
||||
}
|
||||
|
||||
/* ---------- GPIF streaming ---------- */
|
||||
|
||||
static void gpif_start(void) {
|
||||
|
|
@ -658,21 +878,120 @@ BOOL handle_vendorcommand(BYTE cmd) {
|
|||
EP0BCL = 6;
|
||||
return TRUE;
|
||||
|
||||
/* 0x89: BOOT_8PSK -- initialize BCM4500 demodulator */
|
||||
/* 0x89: BOOT_8PSK -- initialize BCM4500 demodulator
|
||||
* wValue=0: shutdown
|
||||
* wValue=1: full boot (reset + power + register init)
|
||||
* wValue=0x80: debug -- return boot_stage only (no-op)
|
||||
* wValue=0x81: debug -- GPIO setup + delays only
|
||||
* wValue=0x82: debug -- GPIO + I2C probe only
|
||||
* wValue=0x83: debug -- GPIO + I2C probe + 1 init block */
|
||||
case BOOT_8PSK:
|
||||
if (wval) {
|
||||
/* Power on: scan for BCM4500 at address 0x10 */
|
||||
val = 0;
|
||||
if (bcm_direct_read(BCM_REG_STATUS, &val)) {
|
||||
config_status |= BM_STARTED;
|
||||
config_status |= BM_FW_LOADED;
|
||||
if (wval == 0x80) {
|
||||
/* Debug: no-op, just return current state */
|
||||
} else if (wval == 0x81) {
|
||||
/* Debug: GPIO only, no I2C */
|
||||
boot_stage = 1;
|
||||
IOD |= 0xE0;
|
||||
OEA |= PIN_BCM_RESET;
|
||||
IOA &= ~PIN_BCM_RESET;
|
||||
OEA |= (PIN_PWR_EN | PIN_PWR_DIS);
|
||||
IOA = (IOA & ~PIN_PWR_DIS) | PIN_PWR_EN;
|
||||
boot_stage = 2;
|
||||
delay(30);
|
||||
IOA |= PIN_BCM_RESET;
|
||||
delay(50);
|
||||
boot_stage = 0xA1; /* success marker for debug 0x81 */
|
||||
} else if (wval == 0x82) {
|
||||
/* Debug: GPIO + probe read (same as 0x85 now -- bmSTOP removed) */
|
||||
boot_stage = 1;
|
||||
IOD |= 0xE0;
|
||||
OEA |= PIN_BCM_RESET;
|
||||
IOA &= ~PIN_BCM_RESET;
|
||||
/* bmSTOP removed -- corrupts FX2 I2C controller */
|
||||
OEA |= (PIN_PWR_EN | PIN_PWR_DIS);
|
||||
IOA = (IOA & ~PIN_PWR_DIS) | PIN_PWR_EN;
|
||||
boot_stage = 2;
|
||||
delay(30);
|
||||
IOA |= PIN_BCM_RESET;
|
||||
delay(50);
|
||||
boot_stage = 3;
|
||||
if (bcm_direct_read(BCM_REG_STATUS, &i2c_rd[0])) {
|
||||
EP0BUF[2] = i2c_rd[0];
|
||||
boot_stage = 0xA2;
|
||||
} else {
|
||||
EP0BUF[2] = 0xEE;
|
||||
boot_stage = 0xE3; /* failed at I2C probe */
|
||||
}
|
||||
} else if (wval == 0x83) {
|
||||
/* Debug: GPIO + probe + first init block */
|
||||
boot_stage = 1;
|
||||
cancel_i2c_trans = FALSE;
|
||||
IOD |= 0xE0;
|
||||
OEA |= PIN_BCM_RESET;
|
||||
IOA &= ~PIN_BCM_RESET;
|
||||
/* bmSTOP removed -- corrupts FX2 I2C controller */
|
||||
OEA |= (PIN_PWR_EN | PIN_PWR_DIS);
|
||||
IOA = (IOA & ~PIN_PWR_DIS) | PIN_PWR_EN;
|
||||
boot_stage = 2;
|
||||
delay(30);
|
||||
IOA |= PIN_BCM_RESET;
|
||||
delay(50);
|
||||
boot_stage = 3;
|
||||
if (!bcm_direct_read(BCM_REG_STATUS, &i2c_rd[0])) {
|
||||
boot_stage = 0xE3;
|
||||
} else {
|
||||
boot_stage = 4;
|
||||
if (bcm_write_init_block(bcm_init_block0, BCM_INIT_BLOCK0_LEN))
|
||||
boot_stage = 0xA3;
|
||||
else
|
||||
boot_stage = 0xE4;
|
||||
}
|
||||
} else if (wval == 0x84) {
|
||||
/* Debug: I2C-only probe, no GPIO (assumes chip already powered) */
|
||||
boot_stage = 3;
|
||||
if (bcm_direct_read(BCM_REG_STATUS, &i2c_rd[0])) {
|
||||
EP0BUF[2] = i2c_rd[0];
|
||||
boot_stage = 0xA4;
|
||||
} else {
|
||||
EP0BUF[2] = 0xEE;
|
||||
boot_stage = 0xE3;
|
||||
}
|
||||
} else if (wval == 0x85) {
|
||||
/* Debug: Same as 0x82 but WITHOUT I2C bus reset (no bmSTOP) */
|
||||
boot_stage = 1;
|
||||
IOD |= 0xE0;
|
||||
OEA |= PIN_BCM_RESET;
|
||||
IOA &= ~PIN_BCM_RESET;
|
||||
/* NOTE: no I2CS bmSTOP here, unlike 0x82 */
|
||||
OEA |= (PIN_PWR_EN | PIN_PWR_DIS);
|
||||
IOA = (IOA & ~PIN_PWR_DIS) | PIN_PWR_EN;
|
||||
boot_stage = 2;
|
||||
delay(30);
|
||||
IOA |= PIN_BCM_RESET;
|
||||
delay(50);
|
||||
boot_stage = 3;
|
||||
if (bcm_direct_read(BCM_REG_STATUS, &i2c_rd[0])) {
|
||||
EP0BUF[2] = i2c_rd[0];
|
||||
boot_stage = 0xA5;
|
||||
} else {
|
||||
EP0BUF[2] = 0xEE;
|
||||
boot_stage = 0xE3;
|
||||
}
|
||||
} else if (wval) {
|
||||
if (bcm4500_boot()) {
|
||||
config_status |= (BM_STARTED | BM_FW_LOADED);
|
||||
} else {
|
||||
bcm4500_shutdown();
|
||||
config_status &= ~(BM_STARTED | BM_FW_LOADED);
|
||||
}
|
||||
} else {
|
||||
config_status &= ~BM_STARTED;
|
||||
bcm4500_shutdown();
|
||||
config_status &= ~(BM_STARTED | BM_FW_LOADED);
|
||||
}
|
||||
EP0BUF[0] = config_status;
|
||||
EP0BUF[1] = boot_stage;
|
||||
EP0BCH = 0;
|
||||
EP0BCL = 1;
|
||||
EP0BCL = 3;
|
||||
return TRUE;
|
||||
|
||||
/* 0x8A: START_INTERSIL -- enable LNB power supply */
|
||||
|
|
@ -736,10 +1055,10 @@ BOOL handle_vendorcommand(BYTE cmd) {
|
|||
|
||||
/* 0x92: GET_FW_VERS -- return firmware version and build date */
|
||||
case GET_FW_VERS:
|
||||
EP0BUF[0] = 0x01; /* patch -> version 3.00.1 */
|
||||
EP0BUF[1] = 0x00; /* minor */
|
||||
EP0BUF[0] = 0x00; /* patch -> version 3.01.0 */
|
||||
EP0BUF[1] = 0x01; /* minor */
|
||||
EP0BUF[2] = 0x03; /* major */
|
||||
EP0BUF[3] = 0x0B; /* day = 11 */
|
||||
EP0BUF[3] = 0x0C; /* day = 12 */
|
||||
EP0BUF[4] = 0x02; /* month = 2 */
|
||||
EP0BUF[5] = 0x1A; /* year - 2000 = 26 */
|
||||
EP0BCH = 0;
|
||||
|
|
@ -973,11 +1292,16 @@ void main(void) {
|
|||
/* Configure I2C: 400kHz */
|
||||
I2CTL = bm400KHZ;
|
||||
|
||||
/* Configure GPIO output enables for LNB/tone/DiSEqC (v2.06 pin map) */
|
||||
OEA |= (PIN_22KHZ | PIN_LNB_VOLT | PIN_DISEQC); /* P0.3, P0.4, P0.7 output */
|
||||
/* Configure GPIO output enables (v2.06 pin map):
|
||||
* P0.1=power_en, P0.2=power_dis, P0.3=22kHz, P0.4=LNB,
|
||||
* P0.5=BCM_reset, P0.7=DiSEqC/streaming */
|
||||
OEA |= (PIN_PWR_EN | PIN_PWR_DIS | PIN_22KHZ | PIN_LNB_VOLT |
|
||||
PIN_BCM_RESET | PIN_DISEQC);
|
||||
|
||||
/* Initial GPIO state: LNB off, tone off, DiSEqC idle */
|
||||
IOA = 0x84; /* P0.7=1 (idle), P0.2=1 (BCM4500 control) */
|
||||
/* Initial GPIO state matches stock firmware:
|
||||
* P0.7=1 (DiSEqC idle), P0.5=0 (BCM4500 held in reset),
|
||||
* P0.2=1 (power disable), all others LOW */
|
||||
IOA = 0x84;
|
||||
IOD = 0xE1; /* P3.7:5=1 (controls idle), P3.0=1 */
|
||||
|
||||
/* EP2 is bulk IN (0x82), 512 byte, double-buffered */
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue