skywalker-1/tools/test_i2c_debug.py
Ryan Malloy d9f51548e0 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
2026-02-12 10:34:15 -07:00

119 lines
4.0 KiB
Python

#!/usr/bin/env python3
"""I2C debug tool for SkyWalker-1.
First powers on the BCM4500 via GPIO debug mode (0x81), then:
1. Runs I2C bus scan (0xB4) to find any devices
2. Tries raw I2C reads (0xB5) to common BCM4500 addresses
3. Tests different post-reset delays
"""
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 main():
dev = find_device()
setup_device(dev)
# Verify firmware
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}")
# Step 1: Power on BCM4500 via GPIO-only debug mode
print("\n--- Step 1: Power on BCM4500 (GPIO mode 0x81) ---")
ret = dev.ctrl_transfer(0xC0, BOOT_8PSK, 0x81, 0, 3, timeout=3000)
print(f" GPIO setup: stage=0x{ret[1]:02X}")
# Step 2: I2C bus scan immediately
print("\n--- Step 2: I2C bus scan (immediately after power-on) ---")
try:
ret = dev.ctrl_transfer(0xC0, 0xB4, 0, 0, 16, timeout=5000)
addrs = []
for bi in range(16):
for bit in range(8):
if ret[bi] & (1 << bit):
addrs.append(bi * 8 + bit)
if addrs:
print(f" Found devices at: {[f'0x{a:02X}' for a in addrs]}")
else:
print(" No I2C devices found!")
except usb.core.USBError as e:
print(f" Bus scan error: {e}")
# Step 3: Wait longer and scan again
print("\n--- Step 3: Wait 500ms and scan again ---")
time.sleep(0.5)
try:
ret = dev.ctrl_transfer(0xC0, 0xB4, 0, 0, 16, timeout=5000)
addrs = []
for bi in range(16):
for bit in range(8):
if ret[bi] & (1 << bit):
addrs.append(bi * 8 + bit)
if addrs:
print(f" Found devices at: {[f'0x{a:02X}' for a in addrs]}")
else:
print(" No I2C devices found!")
except usb.core.USBError as e:
print(f" Bus scan error: {e}")
# Step 4: Try raw I2C reads to various addresses
print("\n--- Step 4: Raw I2C reads (0xB5) to likely BCM4500 addresses ---")
# BCM4500 could be at different addresses depending on pin strapping
# Common: 0x08 (AD=low), 0x0A (AD=high), or even other addresses
candidates = [0x08, 0x09, 0x0A, 0x0B, 0x10, 0x11, 0x68, 0x69, 0x60, 0x61]
for addr in candidates:
for reg in [0xA2, 0x00]:
try:
r = dev.ctrl_transfer(0xC0, 0xB5, addr, reg, 1, timeout=1000)
print(f" Addr 0x{addr:02X} Reg 0x{reg:02X} = 0x{r[0]:02X} <--- RESPONDS!")
except usb.core.USBError:
print(f" Addr 0x{addr:02X} Reg 0x{reg:02X} = (no response)")
# Step 5: Check I2C bus state
print("\n--- Step 5: I2C controller state ---")
try:
# Read I2CTL and I2CS by inspecting them through a known-working address
# Actually, we can just observe what happens when we try reads
print(" (Bus scan and raw reads above show bus health)")
except Exception as e:
print(f" Error: {e}")
# Step 6: Try I2C probe via debug mode 0x82 again with a delay
print("\n--- Step 6: Debug probe (0x82) after additional 1s delay ---")
time.sleep(1.0)
ret = dev.ctrl_transfer(0xC0, BOOT_8PSK, 0x82, 0, 3, timeout=3000)
stage = ret[1]
probe = ret[2]
if stage == 0xA2:
print(f" PROBE SUCCESS! BCM4500 status = 0x{probe:02X}")
else:
print(f" Probe failed: stage=0x{stage:02X} probe=0x{probe:02X}")
print("\nDone.")
if __name__ == "__main__":
main()