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
127
tools/test_boot_debug.py
Normal file
127
tools/test_boot_debug.py
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Incremental BOOT_8PSK debug tester for SkyWalker-1.
|
||||
|
||||
Sends debug boot modes (wValue=0x80..0x83) one at a time to isolate
|
||||
which stage of the BCM4500 boot sequence hangs the FX2 firmware.
|
||||
|
||||
Usage:
|
||||
sudo python3 test_boot_debug.py # run all debug stages
|
||||
sudo python3 test_boot_debug.py 0x82 # run only stage 0x82
|
||||
"""
|
||||
|
||||
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 decode_stage(stage):
|
||||
names = {
|
||||
0x00: "NOT_STARTED",
|
||||
0x01: "GPIO_SETUP",
|
||||
0x02: "PWR_SETTLED",
|
||||
0x03: "I2C_PROBE",
|
||||
0x04: "INIT_BLK0",
|
||||
0x05: "INIT_BLK1",
|
||||
0x06: "INIT_BLK2",
|
||||
0xA1: "DEBUG_GPIO_OK",
|
||||
0xA2: "DEBUG_PROBE_OK",
|
||||
0xA3: "DEBUG_BLK0_OK",
|
||||
0xE3: "DEBUG_PROBE_FAIL",
|
||||
0xE4: "DEBUG_BLK0_FAIL",
|
||||
0xFF: "COMPLETE",
|
||||
}
|
||||
return names.get(stage, f"UNKNOWN(0x{stage:02X})")
|
||||
|
||||
def test_mode(dev, wval, label, timeout_ms=3000):
|
||||
"""Send a debug boot mode and read 3-byte response."""
|
||||
print(f"\n{'─' * 50}")
|
||||
print(f" Testing wValue=0x{wval:02X}: {label}")
|
||||
print(f"{'─' * 50}")
|
||||
|
||||
t0 = time.monotonic()
|
||||
try:
|
||||
ret = dev.ctrl_transfer(0xC0, BOOT_8PSK, wval, 0, 3, timeout=timeout_ms)
|
||||
except usb.core.USBError as e:
|
||||
elapsed = (time.monotonic() - t0) * 1000
|
||||
print(f" FAILED after {elapsed:.0f}ms: {e}")
|
||||
# Try to see if device is still alive
|
||||
try:
|
||||
dev.ctrl_transfer(0xC0, 0x92, 0, 0, 6, timeout=1000)
|
||||
print(" Device still responds to GET_FW_VERS")
|
||||
except:
|
||||
print(" Device is HUNG (no response to GET_FW_VERS)")
|
||||
return None
|
||||
elapsed = (time.monotonic() - t0) * 1000
|
||||
|
||||
status = ret[0]
|
||||
stage = ret[1] if len(ret) > 1 else 0
|
||||
probe = ret[2] if len(ret) > 2 else 0
|
||||
|
||||
print(f" Response in {elapsed:.0f}ms:")
|
||||
print(f" config_status: 0x{status:02X}")
|
||||
print(f" boot_stage: 0x{stage:02X} [{decode_stage(stage)}]")
|
||||
print(f" probe_byte: 0x{probe:02X}")
|
||||
return ret
|
||||
|
||||
def main():
|
||||
dev = find_device()
|
||||
setup_device(dev)
|
||||
|
||||
# Verify firmware is responding
|
||||
try:
|
||||
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}")
|
||||
except usb.core.USBError as e:
|
||||
print(f"GET_FW_VERS failed: {e}")
|
||||
print("Device may be hung. Try reloading firmware with fw_load.py.")
|
||||
sys.exit(1)
|
||||
|
||||
ret = dev.ctrl_transfer(0xC0, 0x80, 0, 0, 1)
|
||||
print(f"Config: 0x{ret[0]:02X}")
|
||||
|
||||
# Parse optional argument for single-stage testing
|
||||
single_stage = None
|
||||
if len(sys.argv) > 1:
|
||||
single_stage = int(sys.argv[1], 0)
|
||||
|
||||
stages = [
|
||||
(0x80, "No-op: return current state only"),
|
||||
(0x81, "GPIO setup + power + delays (no I2C)"),
|
||||
(0x82, "GPIO + I2C bus reset + BCM4500 probe read"),
|
||||
(0x83, "GPIO + I2C probe + write init block 0"),
|
||||
]
|
||||
|
||||
for wval, label in stages:
|
||||
if single_stage is not None and wval != single_stage:
|
||||
continue
|
||||
result = test_mode(dev, wval, label)
|
||||
if result is None:
|
||||
print("\n*** STOPPING: device not responding ***")
|
||||
break
|
||||
|
||||
print(f"\n{'=' * 50}")
|
||||
print("Debug complete.")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Loading…
Add table
Add a link
Reference in a new issue