Context
#96 lands the bare-metal flash agent for hi3520dv200 (V1-era HiSilicon DVR/NVR SoC, Cortex-A9, MX25L25635E 32 MiB NOR on CS1). The PR is verified end-to-end for the parts that don't need many iterations:
- ✅ Agent boots via the standard fastboot SPL handoff
- ✅ HISFC350 driver detects the chip on CS1
- ✅ Macronix EAR-banking exposes the full 32 MiB (lower and upper halves return distinct real data)
- ✅ Selfupdate works (cache-aware trampoline + cache-flush around copy)
- ✅ INFO v3 `flash_mem` lets the host stop hardcoding `0x14000000`
Everything else needs a power-cycle relay so we can iterate without burning a manual reset for every guess. Once the relay arrives, the work below should be picked up.
TODO (in order)
1. Stock-firmware backup
Before any write/erase test:
```
defib agent read -p $PORT -a 0x58000000 -s 32MB -o dumps/hi3520dv200-stock-$(date +%Y%m%d-%H%M%S).bin
```
Verify the dump matches a fresh boot (CRC over the same range twice). Store the dump in the repo's `dumps/` (or wherever the project keeps stock backups).
2. High-baud UART (the big blocker)
The bootrom hands UART running off a slow ~2 MHz reference clock (IBRD=1, FBRD=5 → 115200 baud). Anything above ~125 kbaud needs IBRD < 1, which is illegal — `uart_set_baud` rejects it (safety check), and the agent stays at 115200.
Vendor U-Boot `board/godarm/board.c` does:
```c
reg = readl(CRG_REG_BASE + 0xe4);
reg &= ~UART_CKSEL_APB; /* clear bit 13 */
writel(reg, CRG_REG_BASE + 0xe4);
```
to switch UART onto APB clock, then assumes `CONFIG_PL011_CLOCK = CFG_CLK_BUS/4 = 99 MHz` for divisor calculation.
In this PR I implemented the same switch (gated by `UART_CKSEL_REG` / `UART_CKSEL_BIT` Makefile defines) and tried both 99 MHz (vendor U-Boot value) and 198 MHz (`busclk/2`, the value vendor Linux `mach-godarm/core.c uart_clk_init` uses) — neither produced clean 115200 from the agent. Empirical measurements suggested actual rate around 760 kbaud at host=115200 sampling, which doesn't map cleanly to any standard PLL divider.
With a relay, iterate quickly:
- (a) Read CRG state via `defib agent read -a 0x20030000 -s 4096` to dump the full PLL/divider config; correlate against vendor's `get_bus_clk()` formula in `include/configs/hi3520d.h`.
- (b) Build with various `UART_CLOCK` values (24M, 54M, 99M, 198M, 396M, 792M); for each, check whether the agent talks at 115200 after CKSEL clear.
- (c) Once the right value is found, re-enable `UART_CKSEL_REG` in the Makefile, drop the IBRD safety bypass, and confirm `set_baud(921600)` works.
- (d) Optional: PMU-based runtime calibration (transmit known sequence in PL011 loopback mode, time with CCNT) so the agent self-derives UART_CLOCK without a hardcoded constant.
3. Write / erase / scan verification
After (1) and (2):
- Erase a known-empty sector (likely `0xFE0000` or anywhere in the upper half), read back, confirm 0xFF.
- Write a small known pattern (e.g. 256 B of incrementing bytes), read back, confirm match.
- Re-flash the original sector contents, verify chip back to stock.
- Run `defib agent scan -p $PORT` over the full 32 MiB and confirm reasonable per-sector classification.
4. Bank-switch boundary cases
Make sure operations that span the 16 MiB boundary work cleanly:
- A flash_read of length 1 MiB starting at offset `0x0FF8000` (crosses the boundary by 32 KiB).
- A CRC32 over the full chip — should match the stock dump's CRC.
- A flash_write_page of a page that lies right on `0x1000000` (first page of upper half).
5. True 4-byte mode investigation (optional, low priority)
EN4B (0xB7) was tried and rejected by the chip even though the controller's `GLOBAL_CONFIG.ADDR_MODE_4B` latched. EAR-banking is a fine workaround for now, but figuring out why the chip ignores EN4B might unlock a small simplification (no per-operation bank-switch overhead). Theories: needs WREN before EN4B, needs reset (RSTEN+RST 0x66/0x99) first, or chip is in a sticky state from boot. Not worth chasing until everything else is solid.
6. Profile JSON
The existing `src/defib/profiles/data/hi3520dv200.json` already has the right `ADDRESS`/`SRAMLIMIT`/etc. for the boot-protocol path. After hardware verification, update its description / tested-flag to mark it as agent-supported.
Hardware pin notes (for whoever picks this up)
- Board on `/dev/ttyUSB3` (FT232R, ID `A5069RR4`)
- Vendor SDK U-Boot used as SPL: `/home/dima/work/hi3520dv200_sdk/Hi3520D_SDK_V2.0.5.1/package/image_glibc/u-boot_hi3520d.bin` (228344 B). Cached as `u-boot-hi3520dv200-universal.bin` in defib's firmware cache.
- Bootrom enters serial-boot mode only on cold reset and only when host floods 0xAA before flash boot — defib's manual-mode handshake handles this when the board is power-cycled while defib is already listening.
- Board has valid stock firmware in NOR, so power-on always boots into vendor U-Boot. defib catches the bootrom on the next cold reset by detecting silence-after-activity and starting to flood 0xAA.
Context
#96 lands the bare-metal flash agent for hi3520dv200 (V1-era HiSilicon DVR/NVR SoC, Cortex-A9, MX25L25635E 32 MiB NOR on CS1). The PR is verified end-to-end for the parts that don't need many iterations:
Everything else needs a power-cycle relay so we can iterate without burning a manual reset for every guess. Once the relay arrives, the work below should be picked up.
TODO (in order)
1. Stock-firmware backup
Before any write/erase test:$PORT -a 0x58000000 -s 32MB -o dumps/hi3520dv200-stock-$ (date +%Y%m%d-%H%M%S).bin
```
defib agent read -p
```
Verify the dump matches a fresh boot (CRC over the same range twice). Store the dump in the repo's `dumps/` (or wherever the project keeps stock backups).
2. High-baud UART (the big blocker)
The bootrom hands UART running off a slow ~2 MHz reference clock (IBRD=1, FBRD=5 → 115200 baud). Anything above ~125 kbaud needs IBRD < 1, which is illegal — `uart_set_baud` rejects it (safety check), and the agent stays at 115200.
Vendor U-Boot `board/godarm/board.c` does:
```c
reg = readl(CRG_REG_BASE + 0xe4);
reg &= ~UART_CKSEL_APB; /* clear bit 13 */
writel(reg, CRG_REG_BASE + 0xe4);
```
to switch UART onto APB clock, then assumes `CONFIG_PL011_CLOCK = CFG_CLK_BUS/4 = 99 MHz` for divisor calculation.
In this PR I implemented the same switch (gated by `UART_CKSEL_REG` / `UART_CKSEL_BIT` Makefile defines) and tried both 99 MHz (vendor U-Boot value) and 198 MHz (`busclk/2`, the value vendor Linux `mach-godarm/core.c uart_clk_init` uses) — neither produced clean 115200 from the agent. Empirical measurements suggested actual rate around 760 kbaud at host=115200 sampling, which doesn't map cleanly to any standard PLL divider.
With a relay, iterate quickly:
3. Write / erase / scan verification
After (1) and (2):
4. Bank-switch boundary cases
Make sure operations that span the 16 MiB boundary work cleanly:
5. True 4-byte mode investigation (optional, low priority)
EN4B (0xB7) was tried and rejected by the chip even though the controller's `GLOBAL_CONFIG.ADDR_MODE_4B` latched. EAR-banking is a fine workaround for now, but figuring out why the chip ignores EN4B might unlock a small simplification (no per-operation bank-switch overhead). Theories: needs WREN before EN4B, needs reset (RSTEN+RST 0x66/0x99) first, or chip is in a sticky state from boot. Not worth chasing until everything else is solid.
6. Profile JSON
The existing `src/defib/profiles/data/hi3520dv200.json` already has the right `ADDRESS`/`SRAMLIMIT`/etc. for the boot-protocol path. After hardware verification, update its description / tested-flag to mark it as agent-supported.
Hardware pin notes (for whoever picks this up)