skywalker-1/docs/EEPROM-RECOVERY.md
Ryan Malloy 33b6aa92db Update EEPROM recovery guide with I2C bus latch root cause
Replaces speculative "corrupted boot data" with confirmed I2C bus
latch diagnosis from 100+ failed power cycle attempts.  Mark
Option D (wait + watch) as exhausted.  Add pending firmware fixes
table for post-recovery testing.
2026-02-20 12:04:18 -07:00

187 lines
6.0 KiB
Markdown

# SkyWalker-1 EEPROM Recovery Guide
The device is soft-bricked: the FX2 boot ROM hangs trying to load
corrupted firmware from EEPROM, preventing USB enumeration.
## Symptoms
- Hub shows `0101 power connect []` (D+ pull-up active, no enumeration)
- dmesg: `device descriptor read/8, error -110` (timeout)
- Does not enumerate as bare FX2 (04B4:8613) either
- NanoVNA on same hub works fine (hub hardware is OK)
## Root Cause
The FX2LP boot ROM reads EEPROM (24C128 at I2C 0x51) at power-up.
The I2C bus is shared with the BCM3440 tuner and BCM4500 demod. Two
failure modes cause the hang:
1. **I2C bus latch** (confirmed): The BCM4500 or BCM3440 holds SDA
LOW from a prior incomplete transaction. I2C is open-drain — any
device sinking SDA prevents all communication. The boot ROM loops
waiting for ACK from the EEPROM, which can never respond because
SDA is stuck.
2. **Corrupted C2 image** (possible): If the EEPROM's C2 header is
intact but load records have invalid lengths or addresses, the boot
ROM hangs mid-load.
Either way, the boot ROM occupies the 8051 core and never reaches
the USB enumeration handler. The D+ pull-up activates (hub sees the
device as "present") but descriptor reads time out.
**Tested and failed (2025-02-19/20):**
- 100+ USB power cycles via uhubctl (varied off-times: 0.2s to 30s)
- Host-side I2C manipulation via 0xA0 vendor commands
- xHCI controller reset, port deauthorization
- Ultra-rapid cycling (0.2-1.0s off)
- Historically ~3-6% success rate on power cycles, now 0%
The I2C bus state degrades with each failed attempt. Software-only
recovery is not viable. Physical intervention (Options A or B) is
required.
## Recovery Options (pick one)
### Option A: SOIC Clip + External Programmer (Recommended)
Blank the first byte of the EEPROM so the boot ROM falls back to
bare FX2 enumeration. Then reload via USB.
**Hardware needed:**
- SOIC-8 test clip (Pomona 5250 or similar, ~$5)
- CH341A USB programmer (~$3) or Bus Pirate or any I2C-capable tool
- OR: Raspberry Pi / Arduino with I2C
**Steps:**
1. Power OFF the SkyWalker-1 (unplug USB)
2. Locate the 24C128 EEPROM on the PCB (SOIC-8 package near the FX2)
3. Clip the SOIC clip onto the EEPROM
4. Connect to your I2C programmer (SDA, SCL, VCC, GND)
5. Read and save the EEPROM contents (16KB backup!)
6. Write 0xFF to address 0x0000 (corrupts the C2 magic byte)
7. Remove clip, plug in SkyWalker-1
8. Device should enumerate as bare FX2 (04B4:8613)
9. Load custom firmware via `fw_load.py`
10. Use the custom firmware to write good C2 image back to EEPROM
**With CH341A:**
```bash
# Read backup
flashrom -p ch341a_spi -c "AT24C128" -r eeprom_backup.bin
# Or use i2c-tools if CH341A is in I2C mode:
# i2cdetect -l (find the CH341A bus)
# i2cdump -y <bus> 0x51 b > dump.txt
```
**With Raspberry Pi (I2C):**
```bash
# Enable I2C: raspi-config -> Interfaces -> I2C
# Connect EEPROM: SDA->GPIO2, SCL->GPIO3, VCC->3.3V, GND->GND
i2cdetect -y 1 # Should show 0x51
# Read first byte
i2cget -y 1 0x51 0x00
# Write 0xFF to byte 0 (corrupts C2 header)
i2cset -y 1 0x51 0x00 0xFF
```
### Option B: Hold SDA HIGH During Boot
Prevent the EEPROM from responding by holding SDA HIGH, forcing
the boot ROM to see "no EEPROM" and enumerate as bare FX2.
**Steps:**
1. Locate the SDA test point or EEPROM pin 5 (SDA)
2. Connect a 1kΩ pull-up to 3.3V on SDA
3. Power on the SkyWalker-1
4. If it enumerates as bare FX2 (04B4:8613), load firmware:
```bash
python3 tools/fw_load.py load firmware/build/skywalker1.ihx
```
5. Remove the pull-up
6. Use the loaded firmware to reprogram the EEPROM
**Note:** This only works if the SDA pull-up is strong enough to
override the EEPROM's SDA output. May need to experiment with
pull-up values (470Ω to 4.7kΩ).
### Option C: Desolder EEPROM Pin
Most reliable but requires soldering skill.
1. Lift EEPROM pin 5 (SDA) from the PCB pad
2. Power on → enumerates as bare FX2
3. Load firmware via USB
4. Resolder pin 5
5. Use firmware to reprogram EEPROM with good C2 image
### Option D: Wait + Watch (Exhausted — Do Not Use)
Tested extensively (100+ attempts, 2025-02-19/20) with no success.
The I2C bus latch does not clear on its own.
```bash
# Watch for bare FX2 enumeration
sudo dmesg -w | grep -E "04b4|8613|New USB"
# In another terminal, keep power cycling every 5 minutes
while true; do
sudo uhubctl -l 1-5.4.4 -p 3 -a off
sleep 5
sudo uhubctl -l 1-5.4.4 -p 3 -a on
sleep 300 # wait 5 minutes
done
```
If it appears even briefly:
```bash
python3 tools/fw_load.py load firmware/build/skywalker1.ihx --force
```
## After Recovery
Once the device enumerates (as bare FX2 or with loaded firmware):
1. **Load custom firmware to RAM:**
```bash
python3 tools/fw_load.py load firmware/build/skywalker1.ihx
```
2. **Reprogram EEPROM with good C2 image:**
```bash
# The custom firmware needs EEPROM write support first
# (vendor command to relay I2C writes to EEPROM)
python3 tools/eeprom_write.py flash firmware/build/skywalker1_eeprom.bin
```
3. **Or restore stock firmware:**
If you have a backup of the original EEPROM contents, flash that
instead of the custom firmware.
## Firmware Fixes Pending Hardware Test
The custom firmware at `firmware/build/skywalker1.ihx` (15,171 bytes)
includes three fixes discovered from stock firmware analysis:
| Commit | Fix | Stock Reference |
|-----------|------------------------------|--------------------|
| `e9e5ab8` | Init block readback verify | 0x0DDD readback |
| `dffef75` | `bcm_wait_ready()` 3-check | 0x2000 pre-write |
| `0259950` | GPIF stop before boot | 0x1D6A GPIF abort |
Load immediately after recovery:
```bash
python3 tools/fw_load.py load firmware/build/skywalker1.ihx
```
Then test: `BOOT_8PSK` (wValue=1), read signal, verify lock.
## Prevention
- Never send `BOOT_8PSK (0x89)` with mode 0x84 ("firmware load")
unless you know what data the firmware expects
- Always backup EEPROM before experiments that touch vendor commands
- The stock firmware's I2C proxy (0x83/0x84) may have side effects
on the EEPROM that aren't documented