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
127 lines
4.1 KiB
Python
127 lines
4.1 KiB
Python
#!/usr/bin/env python3
|
|
"""Isolate whether bcm_direct_read is broken or if re-reset causes the failure.
|
|
|
|
Test sequence:
|
|
1. Power on BCM4500 with 0x81 (GPIO only)
|
|
2. Wait 1s for chip to settle
|
|
3. Confirm chip alive via raw read 0xB5
|
|
4. Try bcm_direct_read via debug mode 0x82 (which RE-RESETS the chip)
|
|
5. Immediately try raw read 0xB5 again (is chip alive after 0x82's reset?)
|
|
6. Wait various delays and retry raw reads
|
|
|
|
This tells us if the issue is bcm_direct_read vs insufficient post-reset delay.
|
|
"""
|
|
|
|
import usb.core
|
|
import usb.util
|
|
import sys
|
|
import time
|
|
|
|
BOOT_8PSK = 0x89
|
|
|
|
def find_device():
|
|
dev = usb.core.find(idVendor=0x09C0, idProduct=0x0203)
|
|
if not dev:
|
|
print("Device not found!")
|
|
sys.exit(1)
|
|
return dev
|
|
|
|
def setup_device(dev):
|
|
try:
|
|
if dev.is_kernel_driver_active(0):
|
|
dev.detach_kernel_driver(0)
|
|
except Exception:
|
|
pass
|
|
try:
|
|
dev.set_configuration()
|
|
except usb.core.USBError:
|
|
pass
|
|
|
|
def raw_read(dev, addr, reg, label=""):
|
|
"""Read via 0xB5 raw I2C handler."""
|
|
try:
|
|
r = dev.ctrl_transfer(0xC0, 0xB5, addr, reg, 1, timeout=1000)
|
|
val = r[0]
|
|
ok = val != 0xFF
|
|
mark = "OK" if ok else "no-resp"
|
|
print(f" {label}Raw read addr=0x{addr:02X} reg=0x{reg:02X} → 0x{val:02X} ({mark})")
|
|
return val, ok
|
|
except usb.core.USBError as e:
|
|
print(f" {label}Raw read addr=0x{addr:02X} reg=0x{reg:02X} → USB ERROR: {e}")
|
|
return None, False
|
|
|
|
def main():
|
|
dev = find_device()
|
|
setup_device(dev)
|
|
|
|
ret = dev.ctrl_transfer(0xC0, 0x92, 0, 0, 6, timeout=2000)
|
|
major, minor, patch = ret[2], ret[1], ret[0]
|
|
print(f"Firmware: v{major}.{minor:02d}.{patch}\n")
|
|
|
|
# --- Test A: Verify BCM4500 alive from cold ---
|
|
print("=" * 55)
|
|
print("TEST A: Power on BCM4500, wait, then raw read")
|
|
print("=" * 55)
|
|
print(" Sending 0x81 (GPIO power on + reset release)...")
|
|
ret = dev.ctrl_transfer(0xC0, BOOT_8PSK, 0x81, 0, 3, timeout=3000)
|
|
print(f" GPIO done: stage=0x{ret[1]:02X}")
|
|
|
|
print(" Waiting 1000ms for BCM4500 to settle...")
|
|
time.sleep(1.0)
|
|
|
|
raw_read(dev, 0x08, 0xA2, "After 1s: ")
|
|
|
|
# --- Test B: Now try bcm_direct_read (which re-resets) ---
|
|
print()
|
|
print("=" * 55)
|
|
print("TEST B: Run debug mode 0x82 (re-resets + probe via bcm_direct_read)")
|
|
print("=" * 55)
|
|
ret = dev.ctrl_transfer(0xC0, BOOT_8PSK, 0x82, 0, 3, timeout=3000)
|
|
stage = ret[1]
|
|
probe = ret[2]
|
|
if stage == 0xA2:
|
|
print(f" bcm_direct_read SUCCEEDED: status=0x{probe:02X}")
|
|
else:
|
|
print(f" bcm_direct_read FAILED: stage=0x{stage:02X} probe=0x{probe:02X}")
|
|
|
|
# --- Test C: Immediately try raw read after 0x82 (same I2C function, no reset) ---
|
|
print()
|
|
print("=" * 55)
|
|
print("TEST C: Immediately try raw read 0xB5 (same i2c_combined_read)")
|
|
print("=" * 55)
|
|
raw_read(dev, 0x08, 0xA2, "Immediate: ")
|
|
|
|
# --- Test D: Wait and retry at various intervals ---
|
|
print()
|
|
print("=" * 55)
|
|
print("TEST D: Raw reads with increasing delays after 0x82's reset")
|
|
print("=" * 55)
|
|
for delay_ms in [100, 200, 500, 1000, 2000]:
|
|
time.sleep(delay_ms / 1000.0)
|
|
raw_read(dev, 0x08, 0xA2, f"After {delay_ms}ms: ")
|
|
|
|
# --- Test E: Redo power-on without reset, then probe ---
|
|
print()
|
|
print("=" * 55)
|
|
print("TEST E: Run 0x81 again (re-power), wait 1s, then 0x82")
|
|
print("=" * 55)
|
|
ret = dev.ctrl_transfer(0xC0, BOOT_8PSK, 0x81, 0, 3, timeout=3000)
|
|
print(f" GPIO done: stage=0x{ret[1]:02X}")
|
|
time.sleep(1.0)
|
|
raw_read(dev, 0x08, 0xA2, "After 0x81+1s: ")
|
|
|
|
print(" Now running 0x82 (re-reset + probe)...")
|
|
ret = dev.ctrl_transfer(0xC0, BOOT_8PSK, 0x82, 0, 3, timeout=3000)
|
|
stage = ret[1]
|
|
probe = ret[2]
|
|
if stage == 0xA2:
|
|
print(f" bcm_direct_read SUCCEEDED: status=0x{probe:02X}")
|
|
else:
|
|
print(f" bcm_direct_read FAILED: stage=0x{stage:02X} probe=0x{probe:02X}")
|
|
|
|
print("\n" + "=" * 55)
|
|
print("Analysis complete.")
|
|
|
|
if __name__ == "__main__":
|
|
main()
|