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:
Ryan Malloy 2026-02-12 10:34:15 -07:00
parent 890a38bfa0
commit d9f51548e0
7 changed files with 1296 additions and 51 deletions

171
tools/test_boot.py Normal file
View file

@ -0,0 +1,171 @@
#!/usr/bin/env python3
"""Test BOOT_8PSK on SkyWalker-1 with custom firmware v3.01.0"""
import usb.core
import usb.util
import sys
import time
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):
"""Detach kernel driver and set configuration."""
try:
if dev.is_kernel_driver_active(0):
dev.detach_kernel_driver(0)
print("Detached kernel driver from interface 0")
except Exception as e:
print(f"Driver detach note: {e}")
try:
dev.set_configuration()
except usb.core.USBError:
# Already configured, that's fine
pass
def main():
dev = find_device()
setup_device(dev)
# GET_FW_VERS (0x92)
print("=" * 50)
ret = dev.ctrl_transfer(0xC0, 0x92, 0, 0, 6)
major, minor, patch = ret[2], ret[1], ret[0]
day, month, year = ret[3], ret[4], ret[5] + 2000
print(f"Firmware: v{major}.{minor:02d}.{patch} ({year}-{month:02d}-{day:02d})")
# GET_8PSK_CONFIG (0x80)
ret = dev.ctrl_transfer(0xC0, 0x80, 0, 0, 1)
print(f"Config before boot: 0x{ret[0]:02X}")
# BOOT_8PSK (0x89) with wValue=1
print()
print("=" * 50)
print("Sending BOOT_8PSK(1)...")
print(" (This triggers: P0.5 reset, power on, 3-block register init)")
print()
try:
ret = dev.ctrl_transfer(0xC0, 0x89, 1, 0, 3, timeout=10000)
except usb.core.USBError as e:
print(f"BOOT_8PSK USB error: {e}")
print("The device may have timed out during init.")
print("Trying to read config status anyway...")
try:
ret = dev.ctrl_transfer(0xC0, 0x80, 0, 0, 1)
print(f"Config after attempted boot: 0x{ret[0]:02X}")
except:
print("Device not responding. May need power cycle.")
sys.exit(1)
status = ret[0]
stage = ret[1] if len(ret) > 1 else 0
stage_names = {
0: "NOT_STARTED", 1: "GPIO_SETUP", 2: "PWR_SETTLED",
3: "I2C_PROBE", 4: "INIT_BLK0", 5: "INIT_BLK1",
6: "INIT_BLK2", 0xFF: "COMPLETE"
}
flags = []
if status & 0x01: flags.append("STARTED")
if status & 0x02: flags.append("FW_LOADED")
if status & 0x04: flags.append("INTERSIL")
if status & 0x08: flags.append("DVB_MODE")
if status & 0x10: flags.append("22KHZ")
if status & 0x20: flags.append("SEL18V")
if status & 0x40: flags.append("DC_TUNED")
if status & 0x80: flags.append("ARMED")
print(f"BOOT_8PSK response: 0x{status:02X} [{' | '.join(flags) if flags else 'none'}]")
print(f"Boot stage: 0x{stage:02X} [{stage_names.get(stage, 'UNKNOWN')}]")
if status & 0x03 == 0x03:
print()
print("*** BCM4500 BOOT SUCCESS! ***")
print()
# Read direct I2C registers
print("BCM4500 direct registers (via I2C_RAW_READ 0xB5):")
for reg in [0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8]:
try:
r = dev.ctrl_transfer(0xC0, 0xB5, 0x08, reg, 1)
print(f" Reg 0x{reg:02X} = 0x{r[0]:02X}")
except Exception as e:
print(f" Reg 0x{reg:02X}: ERROR {e}")
# Read indirect registers through our protocol
print()
print("BCM4500 indirect registers (via RAW_DEMOD_READ 0xB1):")
for page in range(16):
try:
r = dev.ctrl_transfer(0xC0, 0xB1, page, 0, 1)
print(f" Page 0x{page:02X} = 0x{r[0]:02X}")
except Exception as e:
print(f" Page 0x{page:02X}: ERROR {e}")
# I2C diagnostic
print()
print("I2C diagnostic (0xB6) for page 0x00:")
try:
r = dev.ctrl_transfer(0xC0, 0xB6, 0x00, 0, 8)
labels = ["wr_A6", "rb_A6", "wr_A8", "rb_A8",
"rb_A7", "fin_A6", "fin_A7", "fin_A8"]
for i, lab in enumerate(labels):
print(f" {lab}: 0x{r[i]:02X}")
except Exception as e:
print(f" ERROR: {e}")
# Signal strength
print()
try:
r = dev.ctrl_transfer(0xC0, 0x87, 0, 0, 6)
print(f"Signal strength: {' '.join(f'{b:02X}' for b in r)}")
except Exception as e:
print(f"Signal strength ERROR: {e}")
# Signal lock
try:
r = dev.ctrl_transfer(0xC0, 0x90, 0, 0, 1)
print(f"Signal lock: 0x{r[0]:02X}")
except Exception as e:
print(f"Signal lock ERROR: {e}")
else:
print()
print("*** BOOT FAILED ***")
print()
# I2C bus scan
print("I2C bus scan:")
try:
r = dev.ctrl_transfer(0xC0, 0xB4, 0, 0, 16)
addrs = []
for bi in range(16):
for bit in range(8):
if r[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 Exception as e:
print(f" Scan error: {e}")
# Try raw I2C reads anyway
print()
print("Raw I2C reads to BCM4500 (0x08):")
for reg in [0xA2, 0xA4, 0xA6, 0xA7, 0xA8]:
try:
r = dev.ctrl_transfer(0xC0, 0xB5, 0x08, reg, 1)
print(f" Reg 0x{reg:02X} = 0x{r[0]:02X}")
except Exception as e:
print(f" Reg 0x{reg:02X}: ERROR {e}")
print()
print("=" * 50)
if __name__ == "__main__":
main()

127
tools/test_boot_debug.py Normal file
View 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()

118
tools/test_i2c_debug.py Normal file
View file

@ -0,0 +1,118 @@
#!/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()

126
tools/test_i2c_isolate.py Normal file
View file

@ -0,0 +1,126 @@
#!/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()

122
tools/test_i2c_pinpoint.py Normal file
View file

@ -0,0 +1,122 @@
#!/usr/bin/env python3
"""Pinpoint which element in mode 0x82 causes bcm_direct_read to fail.
Test sequence:
1. Power on via 0x81, confirm alive with raw read
2. 0x84: bcm_direct_read ONLY (no GPIO, no reset, no bus reset)
3. 0x85: GPIO + reset + power but NO I2C bus reset (no bmSTOP)
4. 0x82: GPIO + I2C bus reset + reset + power + probe (the one that fails)
"""
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", 0xA1: "GPIO_OK", 0xA2: "PROBE_OK(0x82)",
0xA3: "BLK0_OK", 0xA4: "PROBE_OK(0x84)", 0xA5: "PROBE_OK(0x85)",
0xE3: "PROBE_FAIL", 0xE4: "BLK0_FAIL",
}
return names.get(stage, f"0x{stage:02X}")
def test_boot_mode(dev, wval, label, timeout_ms=3000):
print(f"\n{'' * 55}")
print(f" Mode 0x{wval:02X}: {label}")
print(f"{'' * 55}")
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" TIMEOUT after {elapsed:.0f}ms: {e}")
return None
elapsed = (time.monotonic() - t0) * 1000
stage = ret[1]
probe = ret[2]
ok = stage not in (0xE3, 0xE4)
status_str = "SUCCESS" if ok else "FAILED"
print(f" {status_str} in {elapsed:.0f}ms")
print(f" stage=0x{stage:02X} [{decode_stage(stage)}] probe=0x{probe:02X}")
return ret
def raw_read(dev, addr, reg):
try:
r = dev.ctrl_transfer(0xC0, 0xB5, addr, reg, 1, timeout=1000)
return r[0]
except:
return None
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}")
# Step 1: Power on via GPIO-only mode
print("\n=== STEP 1: Power on BCM4500 (mode 0x81) ===")
ret = dev.ctrl_transfer(0xC0, BOOT_8PSK, 0x81, 0, 3, timeout=3000)
print(f" GPIO setup done, stage=0x{ret[1]:02X}")
time.sleep(1.0)
# Confirm alive
val = raw_read(dev, 0x08, 0xA2)
print(f" Raw read 0x08:0xA2 = 0x{val:02X}" if val is not None else " Raw read FAILED")
# Step 2: Test 0x84 (I2C read ONLY, no GPIO manipulation)
test_boot_mode(dev, 0x84, "bcm_direct_read ONLY (no GPIO, chip already powered)")
# Confirm still alive
val = raw_read(dev, 0x08, 0xA2)
print(f" Raw read after 0x84: 0x{val:02X}" if val is not None else " Raw read FAILED")
# Step 3: Test 0x85 (GPIO + reset but NO I2C bus reset)
test_boot_mode(dev, 0x85, "GPIO + reset + power, NO bmSTOP (no I2C bus reset)")
# Confirm still alive
time.sleep(0.1)
val = raw_read(dev, 0x08, 0xA2)
print(f" Raw read after 0x85: 0x{val:02X}" if val is not None else " Raw read FAILED")
# Step 4: For comparison, test 0x82 (the one that fails)
test_boot_mode(dev, 0x82, "GPIO + I2C bmSTOP + reset + power + probe")
# Confirm still alive
val = raw_read(dev, 0x08, 0xA2)
print(f" Raw read after 0x82: 0x{val:02X}" if val is not None else " Raw read FAILED")
print(f"\n{'=' * 55}")
print("Analysis complete.")
print()
print("If 0x84 works → bcm_direct_read is fine, issue is in reset/GPIO sequence")
print("If 0x84 fails → bcm_direct_read itself has a bug")
print("If 0x85 works → I2CS bmSTOP (I2C bus reset) is the culprit in 0x82")
print("If 0x85 fails → re-reset of BCM4500 needs more delay")
if __name__ == "__main__":
main()