docs/02-system-architecture.md
59 KB · 620 lines · markdown · View source · Download from the repository page
PoppyUp — System Architecture & Block Diagram
Document: 02 of 13 — Phase 1 Hardware Design
Source of truth: 00-architecture-decisions.md (ADR, Baseline v0.3 — owner review decisions D1–D13, see ADR §15; v0.2 removed the built-in cellular modem, ADR §14). Where this document explains why, the ADR states what.
Related: 03-datapath-bottlenecks.md (throughput numbers), 05-power-budget.md (rails and budgets), 09-pcb-architecture.md (board partitioning, signal integrity), 07-rf-antenna.md (antenna placement), 12-risks-open-questions.md.
1. Architecture in one paragraph
PoppyUp is a PCIe-centric design. Every card slot, the network interfaces, the optional NVMe and the external-SSD / USB-tether host controller are PCIe endpoints hanging off a Rockchip RK3588 root complex. The top-priority slot (CFexpress Type B) has its own dedicated Gen3 ×2 root port and a 2:1 lane mux so that the card can be handed, electrically intact, to a USB4-to-PCIe bridge for the "pro" card-reader path (Port C — Pro SKU only, D7). Everything else sits behind a single Gen3 packet switch. The SoC's own USB 3 dual-role controller is the universal card-reader path (Port A — works on any host including USB 2-only iPhones, all SKUs) and the wired management link. Both host paths present media read-only by default (D8, §4.10). The RK3588 carrier form — Firefly or Forlinx SoM, or board-down RK3588J on the ODM's reference design — is undecided until PoC exit (D12, §4.1); the lane map below is identical for all three. An always-on Nordic nRF5340 embedded controller (EC) owns power, thermal, BLE provisioning, the display, and — critically — is the sole arbiter of the mode-switch sequencing so that the PCIe mux is never flipped with a live link.
2. Top-level block diagram
flowchart TB
subgraph COMPUTE["Compute domain"]
SOM["RK3588(J) compute<br/>SoM (Firefly / Forlinx) or board-down<br/>decided at PoC exit<br/>4xA76 + 4xA55"]
DDR["LPDDR4x 8 GB"]
EMMC["eMMC 64 GB<br/>OS A/B + job DB"]
TPM["TPM 2.0<br/>Infineon SLB9672<br/>SPI"]
SOM --- DDR
SOM --- EMMC
SOM -- "SPI" --- TPM
end
subgraph FABRIC["PCIe fabric"]
MUX["Diodes PI3DBS16412<br/>2:1 x2 lane mux<br/>+ 100 MHz REFCLK mux"]
CFEB["CFexpress Type B slot<br/>card = NVMe"]
ASM2464["ASMedia ASM2464PD<br/>USB4 40G to PCIe Gen4 x4"]
SW["ASMedia ASM2812<br/>PCIe Gen3 packet switch<br/>x2 up / 12 lanes down"]
CFEA["CFexpress Type A slot<br/>card = NVMe"]
NVME["M.2 2280 M-key NVMe<br/>optional / Pro SKU"]
ETH["Realtek RTL8126<br/>5GBASE-T MAC+PHY"]
SDC["Genesys GL9755<br/>SD UHS-II host"]
USDC["Genesys GL9750<br/>SD UHS-I host"]
SATA["ASMedia ASM1064<br/>AHCI SATA"]
XHCI["ASMedia ASM3142<br/>xHCI USB 3.2 Gen2"]
WIFI["M.2 Key-E 2230<br/>MediaTek MT7925 Wi-Fi 7"]
SPARE["2x spare Gen2 x1<br/>test headers"]
end
subgraph SLOTS["Top-edge card slots (reader daughterboard)"]
SD["SD UHS-II slot"]
USD["microSD slot"]
CFAST["CFast 2.0 slot"]
end
subgraph PORTS["External ports (bottom end)"]
PA["Port A USB-C<br/>PD sink (priority) + gadget + mgmt<br/>RO default"]
PB["Port B USB-C SSD / TETHER<br/>USB 3.2 Gen2 host, 5 V 0.5/1.5/3 A out"]
PC["Port C USB-C<br/>USB4 device, CFe B FAST<br/>Pro only, RO default<br/>PD sink (Pro)"]
RJ["RJ45 5GBASE-T<br/>hinged door"]
end
TETHER["USB tether (external):<br/>phone hotspot over USB or<br/>USB cellular adapter"]
subgraph RF["Radios and antennas"]
ANTW["2x Wi-Fi 2.4/5/6 GHz<br/>front frame upper band"]
ANTB["1x BLE chip antenna<br/>on EC"]
end
subgraph POWER["Power domain"]
PACK["Battery pack 2S2P 21700<br/>72 Wh, BQ40Z50-R2 gauge"]
CHG["TI BQ25792<br/>buck-boost NVDC charger"]
PDA["TPS25750 PD ctrl<br/>Port A"]
PDB["PD/CC ctrl Port B<br/>5 V 0.5/1.5/3 A source<br/>3 A only with PD present"]
PDC["TPS25750 PD sink ctrl<br/>Port C (Pro only)"]
VMUX["TPS2121-class<br/>VBUS priority mux<br/>Port A > Port C, break-before-make<br/>(Pro only)"]
RAILS["Rails: 5.0 V/6 A SYS (SoM + Port A out),<br/>5.0 V/3 A PORT B (tether supply, EC-limited),<br/>3.3 V/4 A slots, 3.3 V/4 A logic,<br/>1.8/1.2/1.05 V local"]
AON["Always-on 3.3 V LDO<br/>EC domain"]
end
subgraph EC["Embedded controller domain (always on)"]
NRF["Nordic nRF5340<br/>EC + BLE 5.4"]
OLED["2.42 in OLED 128x64<br/>SSD1309 SPI"]
LED["5-segment RGB LED bar"]
BTN["Power/action button"]
HALL["Hall sensor<br/>shutter closed"]
NTC["Thermistors x6<br/>incl. skin + internal air<br/>(thermal safety rule)"]
DET["Card-detect x5"]
end
SOM -- "pcie3x4 as Gen3 x2" --> MUX
MUX -- "A: Gen3 x2" --> CFEB
ASM2464 -- "B: Gen4 x2" --> MUX
ASM2464 -- "USB4 40G / USB3.2 Gen2 fallback" --> PC
SOM -- "pcie3x2 Gen3 x2" --> SW
SW -- "Gen3 x1" --> CFEA
SW -- "Gen3 x2" --> NVME
SW -- "Gen3 x1" --> ETH
SW -- "Gen3 x1" --> SDC
SW -- "Gen3 x1" --> USDC
SW -- "Gen3 x1" --> SATA
SW -- "Gen3 x2" --> XHCI
ETH -- "5GBASE-T MDI" --> RJ
SDC -- "UHS-II 312 MB/s" --> SD
USDC -- "UHS-I 104 MB/s" --> USD
SATA -- "SATA III 6 Gb/s" --> CFAST
XHCI -- "USB 3.2 Gen2 10 Gb/s" --> PB
PB -. "USB network-class device<br/>ipheth / NCM / RNDIS / MBIM / QMI" .-> TETHER
SOM -- "pcie2x1l0 Gen2 x1" --> SPARE
SOM -- "pcie2x1l1 Gen2 x1" --> WIFI
SOM -- "pcie2x1l2 Gen2 x1" --> SPARE
SOM -- "USB3 OTG1 5 Gb/s device" --> PA
WIFI -- "2x MHF4" --> ANTW
NRF --- ANTB
PA -- "CC / PD" --> PDA
PA -- "VBUS 5-20 V" --> VMUX
PC -- "CC / PD (Pro)" --> PDC
PC -- "VBUS 5-20 V (Pro)" --> VMUX
VMUX -- "VIN" --> CHG
PDA -- "I2C" --> CHG
PDC -- "I2C" --> CHG
PACK --> CHG
CHG -- "VSYS 6-8.4 V" --> RAILS
CHG --> AON
AON --> NRF
RAILS --> SOM
RAILS --> FABRIC
PDB --> PB
NRF -- "UART + I2C + GPIO" --- SOM
NRF -- "MUX_SEL, PERST#, PWR_EN" --> MUX
NRF -- "ASM2464_PWR_EN" --> ASM2464
NRF -- "SMBus" --- PACK
NRF -- "I2C" --- CHG
NRF -- "PORTC_PD_PRESENT, mux status" --- VMUX
NRF --- OLED
NRF --- LED
NRF --- BTN
NRF --- HALL
NRF --- NTC
NRF --- DET
Edge annotations show the link rate; achievable payload rates per link are tabulated in 03-datapath-bottlenecks.md. Base SKU depopulates the ASM2464PD, the Port C PD sink controller and the VBUS priority mux (Port C receptacle fitted, blanked); the PCB is the same.
3. PCIe / USB topology tree
flowchart LR
RC["RK3588 root complex"]
RC --> P3X4["pcie3x4 ctrl<br/>Gen3 x2"]
RC --> P3X2["pcie3x2 ctrl<br/>Gen3 x2"]
RC --> P2L0["pcie2x1l0<br/>Gen2 x1"]
RC --> P2L1["pcie2x1l1<br/>Gen2 x1"]
RC --> P2L2["pcie2x1l2<br/>Gen2 x1"]
RC --> OTG1["USB3 OTG1 dwc3<br/>peripheral 5 Gb/s"]
RC --> OTG0["USB3 OTG0<br/>spare"]
RC --> U2A["USB2 host 0"]
RC --> U2B["USB2 host 1"]
P3X4 --> MX{"PI3DBS16412<br/>2:1 mux"}
MX -- "A" --> CFEB["CFe B slot<br/>NVMe"]
BR["ASM2464PD<br/>USB4 bridge"] -- "B, Gen4 x2" --> MX
BR --> PC["Port C USB-C<br/>USB4 device (Pro), RO default<br/>+ PD sink to charger"]
P3X2 --> SW["ASM2812 switch"]
SW --> D1["CFe A slot x1"]
SW --> D2["M.2 NVMe x2"]
SW --> D3["RTL8126 x1"] --> RJ["RJ45"]
SW --> D4["GL9755 x1"] --> SD["SD UHS-II"]
SW --> D5["GL9750 x1"] --> USD["microSD"]
SW --> D6["ASM1064 x1"] --> CF["CFast 2.0"]
SW --> D7["ASM3142 x2"] --> PB["Port B USB-C host<br/>SSD / TETHER"]
PB -. "tethered phone or<br/>USB cellular adapter" .-> TETH["USB network device<br/>(external)"]
P2L0 --> TH0["test header<br/>(spare, v0.1 modem)"]
U2A --> TH0
P2L1 --> WF["M.2 Key-E<br/>MT7925"]
U2B -. "BT unused" .-> WF
P2L2 --> TH2["test header<br/>(spare)"]
OTG1 --> PA["Port A USB-C<br/>gadget (RO default) + PD sink"]
Lane accounting against the RK3588: 4 Gen3 lanes (2 to the CFe B mux, 2 to the switch) and 3 Gen2 lanes (Wi-Fi, two spare — pcie2x1l0 was the v0.1 modem lane). This lane map is identical whether the RK3588 arrives on a Firefly or Forlinx SoM or is placed board-down (D12); only the connector stack and who lays out DDR/PMIC change. Switch downstream lanes used: 9 of 12 (see ADR §3.2). Nothing is oversubscribed at the root: the aggregate of all realistic concurrent flows (CFe B read 1.6 GB/s + NVMe write 1.6 GB/s + Ethernet 0.6 GB/s) fits in the two ×2 Gen3 ports with headroom because the CFe B slot does not share its port with anything.
4. Subsystem narrative
4.1 Compute: RK3588 — SoM or board-down, decided at PoC exit
The RK3588 is the only sub-$50 application processor that simultaneously offers four Gen3 lanes split across two controllers, three additional Gen2 lanes, and two genuinely dual-role USB 3 controllers with mature dwc3 gadget support in mainline Linux. The four Cortex-A76 cores with ARMv8 crypto extensions comfortably sustain AES-GCM TLS plus SHA-256/xxHash at 5GbE rates on one to two cores, leaving the rest for the job engine and filesystem. Mainline support (PCIe, USB3, GMAC, crypto, thermal) is solid as of 6.x kernels; the vendor BSP remains available as a fallback for EVT.
Carrier form is undecided (D12). Three paths are carried through PoC and compared against the ADR §3.1 criteria (pcie3x4 ×2+×2 bifurcation and exposure of all seven lanes; both USB3 OTG controllers with working dwc3 gadget to iOS/macOS; thermal interface — SoC top height, boss geometry, measured ΔT at 8 W; ≥ 5-year longevity/EOL terms in writing; price at 1 k / 10 k; second-source / pin-compatibility story; the ODM's ability to support the path):
- Firefly Core-3588J SoM (v0.2 default). A SoM collapses the highest-risk part of the layout (LPDDR4x fly-by, PMIC sequencing, 1088-ball BGA fan-out) into a purchased, pre-validated block. The Core-3588J exposes all seven PCIe lanes and both USB3 OTG ports on its board-to-board connectors, which is the gating requirement; several other RK3588 SoMs route only the ×4 port and one Gen2 lane and would break this topology.
- Forlinx FET3588-C SoM — pin-incompatible with Firefly; a carrier respin (~3 weeks) if selected after the PoC rig is built on Firefly.
- Board-down RK3588J on the main board, using the ODM's RK3588 reference design if they have one. Removes the SoM connector stack from the Z-budget (
08-mechanical.md§3) and the single-vendor SoM risk; adds DDR/PMIC layout, PMIC/DDR sourcing and RK boot-firmware work on the ODM side.
The decision is taken at PoC exit (month 2, ADR §12). Everything downstream of the SoC pins — the lane map in §3, the mux, the switch, the EC interface — is identical for all three; the Turing RK1 is a dev-kit convenience for PoC only. The TPM sits on the SoC SPI bus because the secure-boot chain (ADR §3.6) needs it early, before the EC-to-SoC link is trusted.
4.2 The CFexpress B path: dedicated root port and mux
CFexpress Type B is the top-priority medium, so it gets a root port to itself. Three reasons:
- No contention. A CFe B read at 1.6 GB/s (Gen3 ×2) never competes with NVMe buffering or Ethernet on the same upstream link.
- Clean hand-off. A mux on a dedicated root port means the SoC sees a simple link-down / link-up on one port when we switch modes. If the CFe B slot were behind the switch, the hand-off would have to remove a downstream port from a live switch and the host would enumerate an ASMedia switch it never asked for.
- Signal integrity. The mux path (slot ↔ PI3DBS16412 ↔ ASM2464PD) is Gen4-rated because in host mode the bridge trains at Gen4 ×2 to the card. On the SoC side the same mux only has to carry Gen3. Keeping this path short and on low-loss laminate is a
09-pcb-architecture.mdrequirement.
The mux is a passive analog 2:1 differential switch (PI3DBS16412, two lanes, 16 GT/s rated). It has no protocol awareness: the REFCLK must be switched alongside (a separate 100 MHz HCSL 2:1 clock mux), and the sideband signals PERST#, CLKREQ# and WAKE# are routed to the EC, which fans them out to the currently selected root (SoC or ASM2464PD). See §6 for the sequencing.
4.3 The switch: ASM2812 behind pcie3x2
Every other endpoint is behind one Gen3 packet switch wired ×2 upstream. The upstream ×2 (≈1.6–1.7 GB/s payload) is sized by the worst realistic concurrency: CFe A read (≤850 MB/s) into NVMe write (≤850 MB/s at that rate) while Ethernet drains at 500 MB/s. That saturates the link in the quick-copy case but nothing user-visible slows down, because the network is always the smaller pipe (03-datapath-bottlenecks.md).
Endpoints on the switch and why each is a PCIe device rather than a SoC-native interface:
| Endpoint | Why PCIe |
|---|---|
| CFe A slot | The card is an NVMe device; there is nothing else to connect it to. |
| M.2 NVMe | Gen3 ×2 gives ~1.6 GB/s buffering, sufficient to keep up with a CFe B read. Optional; slot is empty on the base SKU and its downstream port simply reports no link. |
| RTL8126 5GbE | RK3588's native GMACs are 1 GbE. A PCIe NIC is the only way to 5GbE. |
| GL9755 SD UHS-II | RK3588's native SD host is UHS-I only (≈100 MB/s); UHS-II V90 cards read at ~280–300 MB/s. GL9755 is a laptop-proven PCIe UHS-II controller with a mainline sdhci-pci-gli driver. |
| GL9750 microSD | Cheap PCIe UHS-I host; keeps microSD on the same fabric so it can be exposed in host mode without a second gadget path. |
| ASM1064 CFast | CFast is SATA III. The RK3588 combo PHYs could do SATA natively; one is used as a PCIe lane for Wi-Fi and two are spare in v0.2, so SATA on a combo PHY (dropping the ASM1064) remains an EVT option. The baseline keeps the PCIe AHCI controller so that CFast stays on the switched fabric and on the reader daughterboard's mezzanine lane budget. |
| ASM3142 USB host | Gives Port B a 10 Gb/s host (≈900 MB/s from a Samsung T7-class SSD). The SoC's own USB3 host is 5 Gb/s and would halve SSD ingest speed. |
4.4 Gen2 lanes: Wi-Fi plus two spares
The Wi-Fi module gets one of the RK3588's three Gen2 ×1 combo lanes (pcie2x1l1). It needs no more: a 2×2 Wi-Fi 7 client realistically moves 1.5–2.5 Gb/s, under a Gen2 lane's ~3.2 Gb/s usable payload. The module's BT lines are wired to a SoC USB 2.0 host port but held in reset (BLE lives on the EC). The other two Gen2 lanes (pcie2x1l0, which carried the v0.1 modem, and pcie2x1l2) and the second SoC USB 2.0 host port go to internal test headers; they are the natural attach points for a future daughterboard, and the freed ~30 × 52 mm of main-board area is used to spread the power section (ADR §9). No PCIe WWAN / MHI power-management risk remains on the board.
4.5 USB and the three USB-C ports
- Port A is the "always" port: it charges the device (PD sink up to 65 W, priority input to the charger), exposes cards to a host as read-only LUNs (D8; app toggle for a RW session), pauses uploads while a host is attached (D9; concurrent RO sharing is an opt-in), and carries the wired management network (CDC-NCM). It is the port a user plugs an iPhone into. It is driven by the SoC's USB3 OTG1 in peripheral mode, 5 Gb/s. Through a host app the host's own internet link is the bottleneck, so Port A's ~380 MB/s is sufficient for app-driven cloud upload (D7).
- Port B "SSD · TETHER" is a pure host port, 10 Gb/s via ASM3142, with a 5 V source at a user-set 0.5 / 1.5 / 3 A limit. Users plug a Blackmagic or Samsung SSD in and it is ingested like a card — or they plug in their phone (or a USB cellular adapter) and it becomes the cellular uplink (§4.9). One receptacle, one role at a time.
- Port C is a USB4 device port owned by the ASM2464PD. It exists for one reason: to deliver CFexpress B at native speed to a Mac/PC/iPad Pro for fast local copies — which is why it is Pro SKU only and depopulated on Base (same PCB; final, D7). The card is presented read-only by default; enforcement is layered and is the subject of §4.10 (D8). New in v0.3, Port C is also a PD sink (Pro): a Port C PD sink controller (TPS25750 class per ADR §6 — verify PD ownership against the ASM2464PD's integrated PD function, one entity must own the CC lines) and a TPS2121-class priority power mux OR the Port C VBUS with Port A VBUS into the BQ25792 input. Port A has priority, switching is break-before-make, 5–20 V ≤ 3.25 A (verify mux rating at 20 V/65 W); a connected Mac (typically 15 W) powers the device for the whole reader session and the NVDC charger supplements from the battery as needed (§4.7). Base validation and launch do not depend on Port C: firmware keeps it behind a feature flag, the Base FCT does not exercise it, and the Pro USB4 population is a later regulatory addendum (
13-roadmap.md§2.7).
Why three connectors instead of two is answered in §6.2.
4.6 Radios and antennas
There are two radios: the Wi-Fi 7 module and the EC's BLE. Wi-Fi is an M.2 Key-E 2230 module so it can be swapped by region or upgraded (MT7925 → a future Wi-Fi 7 320 MHz part); two MHF4 cables run from it to flex antennas in the front PC frame's upper band, one either side of the OLED (07-rf-antenna.md). BLE is deliberately not on the Wi-Fi module: it lives on the EC with a chip antenna so the phone app can provision, wake and query the device while the SoC and Wi-Fi are asleep. Antenna count is three (v0.1: seven, with four cellular antennas in the end caps); there is no SIM, no eSIM and no cellular module — cellular reach is the tethered uplink of §4.9.
4.7 Power domain
The pack (2S2P, 72 Wh) is permanently wired through a BQ25792 buck-boost NVDC charger. NVDC means the system rail is always powered from whichever source is available and the transition on PD unplug is seamless, which is what lets us drop a supercapacitor hold-up: the battery is the hold-up (05-power-budget.md). The TPS25750 on Port A negotiates PD and programs the charger's input limit over I²C. On the Pro SKU a second sink controller on Port C does the same, and a TPS2121-class priority mux selects which VBUS reaches the charger input: Port A whenever it is present, otherwise Port C; the hand-over is break-before-make and the NVDC path rides it from the battery with no reset (§6.7). The EC reads PORTC_PD_PRESENT and the mux status so it knows whether the device is on external power — which also gates the Port B 3 A tether setting (§4.9). The EC has a separate always-on 3.3 V LDO directly from VSYS so it can sequence every other rail.
4.8 EC domain
The nRF5340 is the "brain stem": it is the only thing that is always on, it has the only BLE radio, and it owns every signal that must be right even when the SoC is off, hung or booting. Its most safety-relevant roles are arbitrating the mode switch (§6) and the thermal safety rule (§7, D2): when skin ≥ 52 °C, internal air ≥ 60 °C, battery ≥ 55 °C or a component limit is crossed, it asks the job engine to checkpoint, takes slots and radios down, puts the device in standby with "Too hot — paused, will resume" on the OLED, and auto-resumes when skin ≤ 42 °C and air ≤ 45 °C with link and cards still present. It also hosts the BLE Job Location GATT characteristic (D13) that the phone app writes at job start (lat, lon, alt, horizontal accuracy, timestamp, source; vendor UUID, write-with-response, bonded/encrypted) and forwards to the SoC job DB. It drives the OLED and LED bar, so the user sees battery and last-status information at any time.
4.9 Tethered uplink on Port B
With no built-in modem, cellular reach comes from the user's phone or from an off-the-shelf USB LTE/5G adapter attached to Port B (ADR §4.3, §5). Electrically nothing is added: the device enumerates on the ASM3142 xHCI as a USB network-class device — ipheth for an iPhone with Personal Hotspot enabled, cdc_ncm or rndis_host for Android USB tethering, cdc_mbim / qmi_wwan for cellular adapters (hotspot-class adapters present as NCM/RNDIS). It appears to the job engine as one more network interface and enters the link policy behind Ethernet and Wi-Fi STA, tagged metered by default, so per-link caps, data-cap warnings and the adaptive 8 MB chunk size apply exactly as for a phone hotspot over Wi-Fi.
Two hardware-adjacent details:
- VBUS current limit. Port B's 5 V source is set by the EC through the Port B CC/PD controller (Rp advertisement) and the VBUS eFuse limit to 0.5 / 1.5 / 3 A (user setting, default 1.5 A; ADR §13 item 14, D13), so the phone charges rather than drains while tethered. The EC permits 3 A only while PoppyUp has external PD power present (Port A, or Port C on Pro — PORTC_PD_PRESENT / Port A contract); on battery the cap is 1.5 A regardless of the setting, so 3 A is never a battery-runtime case. Port B has its own 5.0 V/3 A buck through the Port B eFuse (ADR §6,
05-power-budget.md§3.3); the tether row of ADR §6.2 (14.1 W Σ at 1.5 A) is the runtime cost of keeping the phone charged. - SSD-vs-tether exclusivity. Port B is a single receptacle. If a tethered device is attached, SSD ingest is unavailable and vice versa; a hub carrying both is not supported. The EC/SoC report the active role and the OLED says so ("Port B: phone tethered — SSD not available").
The iOS trust prompt ("Trust This Computer") and hotspot enable are on-phone actions; the device surfaces the wait state on the OLED and in the app. Behaviour across iOS/Android versions is a firmware-validation item at PoC (01-requirements.md FR-026).
4.10 Read-only enforcement on Port C (Pro, D8)
Source media must never be modified unless the user explicitly allows it (01-requirements.md FR-003, FR-045, FR-045-A). On Port A this is trivial: the SoC owns the LUNs and sets the SCSI write-protect bit. On Port C the SoC is not in the data path once the mux is flipped — a USB4 host talks NVMe to the card directly — so read-only has to be arranged before the hand-off, and no single mechanism covers every host/card combination. macOS in particular writes .Spotlight-V100, .fseventsd, .Trashes and ._* files to any writable volume within seconds of mounting it.
| # | Mechanism | Where it acts | Applies to | What the host sees | Limits |
|---|---|---|---|---|---|
| 1 | NVMe Namespace Write Protection (NVMe 1.4 Feature 0x84, Namespace Write Protection Config, state 1 Write Protect) — set by the SoC, which owns the card at that moment, immediately before CFEB_RELEASED |
In the card's controller | Native USB4/PCIe-tunnelled hosts and UAS/BOT-fallback hosts | The volume mounts read-only; Disk Utility / Finder report the disk as read-only ("disk is locked"); every write returns EROFS / a write-protect sense; macOS creates no .Spotlight-V100 or .fseventsd; Windows reports "The disk is write-protected". |
Only on cards whose controller reports NWPC support (Identify Controller byte 531, bit 0) — expected a minority of camera cards; survey the 01-requirements.md IF-001 card list at PoC. State 2 ("until power cycle") is unusable because the hand-off power-cycles the slot; state 1 persists across the power cycle and is cleared only by another Set Features, which generic macOS/iOS NVMe drivers never issue. The SoC must clear state 1 on return (before "Safe to remove"); a card pulled from the slot during a Port C session leaves write-protected and will refuse camera writes until re-inserted in PoppyUp (or any NVMe host) — the OLED warns, and re-insertion clears it (§6.7). |
| 2 | ASM2464PD bridge write-protect — SCSI Mode Sense WP bit / firmware read-only configuration of the bridge's UAS/BOT personality, if ASMedia's configuration tool exposes it (verify with the ASMedia-access team) | In the bridge, UAS/BOT path only | USB 3.2 Gen2 hosts (iPhone 15/16/17 Pro, TB3-less PCs) where the bridge is in UAS fallback | Finder / Files app show the volume as locked (read-only); writes fail with a write-protect error; no hidden metadata written. | No effect in native USB4 mode: with a PCIe tunnel the host's NVMe driver talks to the card and no bridge logic is in the data path. Availability of the RO configuration is unconfirmed (verify). |
| 3 | Fallback — explicit per-session consent when neither 1 nor 2 applies (native USB4 host + non-NWPC card) | EC/SoC policy, before the mux flips | Everything else | The device does not switch to pass-through automatically. OLED and app: "Mac wants write access to CFe B — allow?". Nothing appears on the host until the user confirms (button hold or app toggle). | Read-only cannot be enforced; the session is logged; on return the job engine runs the full re-verification of §6.5 (identity + directory/mtime scan) and reports changed or added files. Tracked as a risk (12-risks-open-questions.md). |
Recommended layered approach (firmware, verify at PoC). At HOST_C_ATTACH:
- The SoC reads Identify Controller byte 531 on the CFe B card (it does this at every mount and caches it per card serial;
nvme id-ctrlshows it asnwpc). If NWPC is supported →Set Features 0x84, WPS = 1 → checkpoint, release, flip (§6.3). Host gets a read-only NVMe device whatever its USB generation. This is the clean case. - Otherwise the EC powers the ASM2464PD before flipping the mux (the bridge boots and completes USB/PD enumeration with no downstream device — verify the ASM2464PD tolerates a downstream hot-add after enumeration, §10) and reads back from the bridge or the Port C PD controller whether the host entered USB4 or fell back to USB 3.2. If the host is in UAS fallback and mechanism 2 is available → arm the bridge write-protect → flip. Host sees a locked volume.
- Otherwise (native USB4 host, non-NWPC card, or mechanism 2 unavailable) → do not flip; prompt for consent; flip only after an explicit yes; log the session; force full re-verification on return.
- The per-session RW toggle in the app reverses the default on either path (it skips step 1's WPS=1 and the step-3 prompt); it is never persisted and reverts on detach.
- On
PORTC_DETACHthe SoC, once it owns the card again, clears WPS to 0 if it set it, then remounts read-only and only then shows "Safe to remove".
What the user sees in each case is in §6.8. Whether mechanism 1 covers a useful fraction of the card library, and whether mechanism 2 exists at all, are the two PoC questions that decide how often the fallback prompt appears (ADR §13 remaining open items).
5. Operating modes and concurrency
| Mode | CFe B slot | CFe A / SD / microSD / CFast | Port B (SSD or tether) | Network uploads | Notes |
|---|---|---|---|---|---|
| Standalone | SoC, RO mount, uploading | SoC, RO mount, uploading | SSD ingesting | active | Default |
| Standalone, tethered uplink | SoC, RO mount, uploading | SoC, RO mount, uploading | phone / USB cellular adapter (no SSD) | active over the tether (metered) | ADR §4.3; phone charged at 0.5/1.5/3 A |
| Reader via Port C (Pro) | handed to ASM2464PD → host, RO default (§4.10) | SoC, continue uploading | continue | active for other slots | CFe B job checkpointed; Port C PD sink: the Mac powers the device (typ. 15 W), charger supplements from battery — session not battery-limited |
| Reader via Port A (default, D9) | exposed as RO LUN | exposed as RO LUNs | exposed as RO LUN | paused (checkpointed) | RO default (D8); app RW toggle per session; a Mac ≥ 15 W may charge the device |
| Reader via Port A (opt-in concurrent) | RO LUN + uploading | RO LUN + uploading | RO LUN + ingesting | active | Safe: both sides are readers |
| Port A + Port C simultaneously | via Port C (RO default) | RO LUNs via Port A | RO LUN | paused (D9) | Allowed; CFe B is absent from Port A's LUN list; Port A VBUS has priority at the charger input |
| Thermal pause (safety rule, D2) | checkpointed, unmounted, slot off | checkpointed, unmounted, slots off | off | stopped; radios off | Skin ≥ 52 °C / air ≥ 60 °C / battery ≥ 55 °C / component limit → standby, "Too hot — paused, will resume"; auto-resume at skin ≤ 42 °C and air ≤ 45 °C with link and cards present (§7) |
| Low battery graceful | unmounted | unmounted | unmounted | stopped | Then off |
The "opt-in concurrent" row is technically safe because the SoC mounts read-only and exposes read-only LUNs; two readers of one block device cannot conflict. It is not the default because the owner decided (D9) that uploads pause while a host is attached on Port A, and because bandwidth sharing would make host-mode speed unpredictable. Every host-facing row is read-only unless the user has enabled a RW session in the app (D8).
6. Mode switching — the critical hardware path
6.1 Hardware routing summary
| Element | Standalone | Port C host mode |
|---|---|---|
| PI3DBS16412 lanes | A: SoC pcie3x4 ↔ slot | B: ASM2464PD ↔ slot |
| REFCLK mux (100 MHz HCSL) | SoC root REFCLK → slot | ASM2464PD REFCLK → slot |
| CFEB_PERST# | driven by EC, follows SoC's PERST request | driven by EC, follows ASM2464PD's PERST output |
| CLKREQ# / WAKE# from slot | routed by EC to SoC | routed by EC to ASM2464PD |
| CFEB_PWR_EN (slot eFuse) | on | on, after mux settles |
| ASM2464_PWR_EN | off (bridge unpowered, zero draw) | on |
| Port C VBUS detect | monitored by EC | monitored by EC |
| Port C VBUS → charger (Pro, D7) | priority mux passes Port A only (Port C absent) | Port C VBUS OR-ed into the BQ25792 input through the TPS2121-class priority mux; Port A takes over whenever present (break-before-make) |
| CFe B namespace write protect (NWPC cards) | WPS = 0 | WPS = 1 set by SoC before release (§4.10); cleared on return |
Sideband signals are not passed through the analog mux; they terminate at the EC, which implements the selection in firmware. This keeps a single owner for every reset and clock-request line and lets the EC guarantee ordering.
6.2 Why Port C is a separate connector
The obvious "one USB-C for everything" design was evaluated and rejected. Two problems:
Signal integrity of a USB4 lane mux. A single port would require a 2:1 mux on the USB-C SuperSpeed pairs to steer them either to the SoC (USB 3.2 Gen1 gadget) or to the ASM2464PD (USB4). USB4 Gen3 runs 20 GBd per lane. Passive muxes qualified for that rate are scarce, add ~1.5–2 dB of insertion loss and a return-loss discontinuity right at the connector where the USB4 budget is tightest, and every candidate part we could source with a public datasheet was rated to 16 GT/s (PCIe 4.0). Falling back to USB4 Gen2 (20 Gb/s aggregate) would cap the pro path at roughly Gen3 ×2 speeds and remove its reason to exist.
PD negotiation ordering. We cannot know whether the attached host is USB4-capable until PD Discover Identity / Enter_USB messaging has happened, but the mux must be set before SuperSpeed link training starts. The ASM2464PD expects to own its USB-C port's PD negotiation (it responds to Enter_USB). Splitting PD ownership between the ASM2464PD, the TPS25750 and the SoC across one connector creates a race the standards do not contemplate, and behaviour would differ across Apple, Intel and AMD hosts. That is a certification-lab and field-support problem, not a lab-bench one.
A dedicated connector makes both problems disappear: Port C is a textbook ASM2464PD reference design with its own PD controller, identical to shipping USB4 CFexpress readers. The cost is one more sealed receptacle and a mux on the PCIe side (16 GT/s, exactly what PI3DBS16412 is rated for, and on a path we control end to end).
6.3 Entering host mode on Port C
sequenceDiagram
participant Host as USB4 host
participant PC as Port C PD ctrl
participant EC as nRF5340 EC
participant SoC as RK3588 job engine
participant MX as Lane and REFCLK mux
participant BR as ASM2464PD
participant Card as CFe B card
Host->>PC: cable attach, CC detect
PC->>EC: PORTC_ATTACH interrupt
EC->>SoC: UART: HOST_C_ATTACH
Note over SoC: t = 0
SoC->>SoC: finish current part, write checkpoint, fsync job DB
SoC->>Card: unmount RO fs, nvme flush not required
SoC->>Card: Set Features 0x84 WPS=1 (if NWPC, RO default, section 4.10)
Note over EC,SoC: else: bridge WP (UAS) or user consent before flipping
SoC->>SoC: pci remove device, put root port in D3
SoC->>EC: UART: CFEB_RELEASED
Note over EC: t <= 1500 ms
EC->>Card: CFEB_PERST# low
EC->>Card: CFEB_PWR_EN off, wait 200 ms
EC->>MX: MUX_SEL = B, REFCLK_SEL = B
EC->>BR: ASM2464_PWR_EN on
Note over BR: bridge boots from SPI flash, 300-500 ms
EC->>Card: CFEB_PWR_EN on, wait 100 ms
EC->>Card: CFEB_PERST# released, controlled by BR
BR->>Card: PCIe link train Gen4 x2
BR->>Host: USB4 tunnel up or USB3.2 UAS enumerate
Host->>Host: mount card
Note over Host: t <= 4500 ms target
EC->>SoC: UART: MODE_READER_C
SoC->>EC: OLED and LED state
Timing budget (target: card visible to host in under 5 s):
| Step | Budget | Notes |
|---|---|---|
| Attach detect → SoC notified | 50 ms | CC debounce + UART |
| Job checkpoint (finish part, fsync) | ≤ 1000 ms | S3 parts are ≤16 MB; at 500 MB/s a part completes in ~32 ms; fsync on eMMC dominates |
| NVMe Set Features 0x84 (NWPC cards) | ≤ 50 ms | One admin command; skipped for non-NWPC cards or a user-enabled RW session (§4.10) |
| Unmount + PCI device removal | ≤ 400 ms | read-only fs, no dirty pages |
| Slot power-off dwell | 200 ms | guarantees card controller reset; CFe cards re-initialise from cold |
| Mux switch + settle | 10 ms | analog switch; settle is µs, margin for firmware |
| ASM2464PD power-up and firmware boot | 500 ms | measure at EVT |
| Slot power-on, PERST release, link train | 300 ms | PCIe spec allows 100 ms after PERST; NVMe controller ready ≤ 200 ms typical |
| USB4 tunnel or UAS enumeration + host mount | 1000–2000 ms | host-dependent; macOS mounts exFAT in ~1 s |
| Total | ≈ 3.5–4.5 s |
If Port C is attached while no card is present, the EC still performs the switch (cheap) so that a later card insert enumerates directly to the host — but a card inserted while the host owns the slot cannot receive WPS = 1 from the SoC, so for non-NWPC-capable enforcement the consent rule of §4.10 applies (the EC keeps the slot unpowered until the SoC has had the card or the user has consented). On the Pro SKU the Mac's VBUS is accepted at the charger input through the priority mux from the moment of attach, before any of the above.
6.4 Exiting host mode on Port C
sequenceDiagram
participant Host as USB4 host
participant PC as Port C PD ctrl
participant EC as nRF5340 EC
participant BR as ASM2464PD
participant MX as Lane and REFCLK mux
participant Card as CFe B card
participant SoC as RK3588 job engine
Host->>PC: cable detach, CC open
PC->>EC: PORTC_DETACH
EC->>Card: CFEB_PERST# low
EC->>Card: CFEB_PWR_EN off, wait 200 ms
EC->>BR: ASM2464_PWR_EN off
EC->>MX: MUX_SEL = A, REFCLK_SEL = A
EC->>Card: CFEB_PWR_EN on, wait 100 ms
EC->>SoC: UART: CFEB_RETURNED
SoC->>Card: root port D0, CFEB_PERST# released via EC
SoC->>SoC: pci rescan, nvme probe
SoC->>Card: Set Features 0x84 WPS=0 if set at hand-off
SoC->>Card: mount RO, read serial + FS UUID
SoC->>SoC: compare identity with checkpoint
alt identity matches, no mtime changes
SoC->>SoC: resume from checkpoint offset
else host modified card or different card
SoC->>SoC: rescan tree, diff against job manifest, requeue changed files
end
SoC->>EC: UART: MODE_UPLOADING
The "unsafe eject" case (host unplugged mid-write) is not a data-integrity problem for PoppyUp: the card was never mounted by the SoC while the host owned it, and on return we mount read-only, so we never write to a filesystem the host left dirty. We do detect the dirty bit and show a warning that the card should be checked in-camera.
6.5 Card identity verification on return
The job checkpoint stores, for each card: NVMe controller serial and model (from Identify Controller), namespace GUID/EUI-64 if present, filesystem volume UUID/serial, volume label, the directory tree manifest (path, size, mtime, first-chunk hash) and the per-file upload offset. On return the job engine:
- Matches serial + FS UUID. Mismatch → treated as a brand-new card; a new job is created and the old job is marked "card removed".
- Walks the tree and compares size/mtime against the manifest. Files unchanged → resume at the stored offset (S3 multipart part list, SFTP append offset, Drive/Dropbox resumable session URI). Files changed or new → re-queued in full. Files deleted by the host → job marked complete-with-note.
- For files whose offset > 0, re-hashes the first 16 MB and compares to the manifest hash before resuming, catching the case where a host wrote a different file under the same name and mtime.
This is a firmware-phase deliverable; hardware guarantees only (a) the card sees a clean power cycle on every hand-off, so no stale controller state crosses the boundary, and (b) the SoC receives an unambiguous CFEB_RETURNED event.
6.6 Entering and exiting host mode on Port A
sequenceDiagram
participant Host as iPhone or Mac
participant PD as TPS25750 Port A
participant EC as nRF5340 EC
participant SoC as RK3588 dwc3 gadget
Host->>PD: attach, VBUS present, Rp detected
PD->>EC: PORTA_ATTACH, source caps
alt source is a phone or offers less than 15 W
EC->>PD: sink current limit = 0 mA
opt user enabled phone top-up
EC->>PD: request PR_Swap, source 5 V 1.5 A
end
else source offers 15 W or more and user allows charging from hosts
EC->>PD: negotiate PD contract, program BQ25792 input limit
end
EC->>SoC: UART: HOST_A_ATTACH
SoC->>SoC: checkpoint all jobs, pause uploaders
SoC->>SoC: bind composite gadget: UAS LUN per card, RO, plus CDC-NCM
SoC->>Host: enumerate, LUNs appear
Host->>Host: mount cards, app connects over NCM link
Note over Host: t <= 3 s
Host->>PD: detach
PD->>EC: PORTA_DETACH
EC->>SoC: UART: HOST_A_DETACH
SoC->>SoC: unbind gadget, remount RO, resume jobs from checkpoints
Port A specifics:
- Composite gadget. Functions: UAS (f_tcm) as primary with BOT (f_mass_storage) fallback selected per host quirk table; one LUN per inserted card (or per SSD on Port B); all LUNs
ro=1. Plus CDC-NCM for the management network (iOS, macOS and Windows 11 all have class drivers). Vendor/product IDs and an Apple-compatible descriptor set are firmware-phase items. - Read-only, uploads paused. LUNs are exposed read-only by default (SCSI WP bit; D8) and uploads pause while the host is attached (D9); the app can toggle RW for the session (never persisted, reverts on detach, logged) and can opt in to concurrent RO sharing. Port C's enforcement is different in kind (§4.10) because the SoC is not in its data path.
- iPhone power. An iPhone is a 4.5 W source. The EC sets the sink limit to zero so PoppyUp draws nothing measurable, and optionally requests a power-role swap to top up the phone at 5 V/1.5 A from PoppyUp's battery. A Mac offering ≥ 15 W is accepted as a charge source if the user allows it (default on).
- Card inserted while in Port A host mode. The SoC adds a LUN dynamically (UAS supports it; for BOT hosts we re-enumerate). Uploads remain paused per the mode rule.
- Simultaneous Port A and Port C. Allowed. CFe B is excluded from Port A's LUN list while it belongs to Port C.
6.7 Failure modes
| Event | Hardware behaviour | Firmware/user outcome |
|---|---|---|
| Host unplugged from Port C mid-transfer | EC sees detach, forces slot power-cycle before mux flips | Card returns to SoC clean; host sees an unsafe eject (its problem, not ours); we warn if FS dirty |
| Card removed while in Port C mode | SLOT_DET[CFEB] to EC; EC forwards to bridge via PERST#; bridge reports device gone | Host sees eject; EC stays in Port C mode awaiting a new card. If the SoC had set NVMe WPS = 1 (§4.10) the card leaves write-protected and a camera will refuse to record to it until it is re-inserted in PoppyUp (clears WPS) — OLED/app: "Card is write-protected — re-insert to clear" |
| Card removed while uploading (standalone) | SLOT_DET edge → EC → SoC; slot eFuse cuts power | Job engine marks "card removed", keeps checkpoint for 30 days; re-insert resumes |
| Card inserted with shutter closed | Impossible mechanically | — |
| Mux flipped while link active | Never allowed. EC firmware enforces the state machine: MUX_SEL changes only when CFEB_PWR_EN is off and CFEB_PERST# is low, verified in hardware by an AND gate that gates MUX_SEL with PWR_EN=0 | Hard interlock plus firmware interlock |
| ASM2464PD fails to boot | EC times out at 1.5 s, retries once, then reports | OLED: "USB4 port error", LED red; standalone unaffected |
| SoC hangs during hand-off | EC watchdog (2 s heartbeat) | EC completes the hand-off autonomously (card to host), then hard-resets the SoC; other slots' jobs resume from checkpoints after reboot |
| Low battery during host mode | EC at 5 % SoC requests graceful; at 3 % forces | Pro: a Mac on Port C powers the device through the Port C PD sink and priority mux (D7), so the USB4 session is not battery-limited; Port A path charges from Macs ≥ 15 W on all SKUs. On Base, Port C does not exist |
| Port A source plugged or unplugged while a Mac on Port C powers the device (Pro) | TPS2121-class mux switches break-before-make to/from Port A (priority); BQ25792 NVDC path supplies VSYS from the battery during the gap | No reset, no VSYS droop > 3 %, USB4/UAS link unaffected; EC re-evaluates PORTB_ILIM (3 A allowed while any PD source is present) |
| Thermal safety threshold crossed (skin ≥ 52 °C, internal air ≥ 60 °C, battery ≥ 55 °C or component limit — e.g. packed away mid-job) | EC raises THERMAL_PAUSE_REQ; SoC checkpoints all jobs, unmounts, slots and radios off; EC cuts PMIC_EN → standby; EC keeps sampling | OLED "Too hot — paused, will resume", app notified over BLE; EC wakes the SoC (THERMAL_RESUME) when skin ≤ 42 °C and air ≤ 45 °C with link and cards present; jobs resume from checkpoints (§7). Independent 48 °C mechanical thermal switch remains the hardware backstop |
| Port A attached to a charger only (no host) | No SuperSpeed enumeration within 2 s | Normal charging, no mode change |
| Tethered phone detached from Port B mid-upload (or hotspot disabled on the phone) | xHCI reports device gone; EC drops Port B VBUS limit to default | Job engine checkpoints and fails over to the next permitted link (FR-022) or waits amber; SSD ingest becomes available again |
6.8 What the user sees
| State | OLED line 1 | OLED lines 2–4 | LED bar | App |
|---|---|---|---|---|
| Standalone, uploading | UPLOADING ETH 5GbE |
per-slot: B 47% 412MB/s, SD 100% ✓ |
per-slot green pulsing / solid | Jobs tab live |
| Standalone, uploading via tethered phone | UPLOADING via iPhone (or via Android, via USB modem) |
per-slot: B 3% 6MB/s, metered 12/50 GB |
per-slot green pulsing | Jobs tab; metered-link cap shown |
| Tethered phone attached, waiting on the phone | TETHER unlock phone / trust |
Port B: phone tethered — SSD not available |
amber steady | prompt to accept on the phone |
| Waiting for link | NO LINK searching |
per-slot queued |
amber steady | Network tab prompts |
| Port C attached, switching | USB4 handing off B |
B checkpointing… |
B segment blue blink | banner "Reader mode: CFexpress B" |
| Port C attached, RO not enforceable (§4.10 fallback) | USB4 allow write access? |
Mac wants write access to CFe B — hold button to allow |
B segment blue/amber alternating | prompt "Allow write access for this session?" |
| Port C active | READER USB4 Mac (RO) ((RW) in a consented/toggled session) |
B → host, others uploading; powered by Mac (Pro PD sink) |
B blue, others green | banner + throughput of others |
| Port A active (iPhone) | READER iPhone (RO) |
5 cards shared RO, uploads paused |
all blue | Files app shows drives; app shows paused |
| Port A active, concurrent opt-in | READER+UPLOAD |
mixed | blue/green alternating per slot | both |
| Low battery graceful | BATTERY 5% saving |
flushing 3 jobs |
all amber | push notification via BLE |
| Thermal pause (safety rule) | TOO HOT paused, will resume |
3 jobs checkpointed, skin 53 °C → resumes ≤ 42 °C |
all amber, slow pulse | push notification via BLE; resume notification later |
| Off / standby | PoppyUp 78% ✓ 3 jobs done (persists 30 s after button) |
last summary | off | BLE: battery, last status |
7. Boot and power state machine
stateDiagram-v2
[*] --> Off
Off --> Standby: pack connected or PD attach
Standby --> Booting: button press or card insert or PD attach or BLE wake
Booting --> Idle: SoC heartbeat ok
Booting --> Standby: boot fail x3, EC logs
Idle --> Uploading: card present and link up and endpoint assigned
Uploading --> Idle: all jobs done or link lost with nothing queued
Idle --> ReaderA: Port A host attach
Uploading --> ReaderA: Port A host attach, jobs checkpointed
ReaderA --> Idle: Port A detach
Idle --> ReaderC: Port C attach
Uploading --> ReaderC: Port C attach, CFe B job checkpointed, others continue
ReaderC --> Uploading: Port C detach, rescan ok
ReaderC --> Idle: Port C detach, nothing queued
Uploading --> LowBatteryGraceful: SoC 5 percent and no PD
Idle --> LowBatteryGraceful: SoC 5 percent and no PD
ReaderA --> LowBatteryGraceful: SoC 5 percent and no PD
ReaderC --> LowBatteryGraceful: SoC 5 percent and no PD
LowBatteryGraceful --> Standby: flush and unmount done, SoC off
LowBatteryGraceful --> Standby: SoC 3 percent, EC forces off
Uploading --> ThermalPause: skin >= 52 C or air >= 60 C or battery >= 55 C or component limit
Idle --> ThermalPause: same thresholds
ReaderA --> ThermalPause: same thresholds
ReaderC --> ThermalPause: same thresholds
ThermalPause --> ThermalStandby: all jobs checkpointed, slots and radios off, SoC off, OLED Too hot paused will resume
ThermalStandby --> Booting: skin <= 42 C and air <= 45 C and link and cards present, auto-resume
ThermalStandby --> Standby: cards removed or user power-off, jobs kept for resume
Standby --> Off: cell UVLO 2.8 V per cell
Idle --> Standby: 15 min idle, no cards, no host, user setting
Standby --> Booting: PD attach restores power, auto-resume queued jobs
Triggers owned by the EC: button (short = wake/status, long 3 s = shutdown, 10 s = hard reset), any SLOT_DET edge (wakes the SoC so uploading starts on insert), Port A/C attach, BLE "wake" command from the app, fuel-gauge thresholds, and the thermal safety thresholds (D2). The SoC never powers itself off; it requests shutdown and the EC cuts PMIC_EN after the heartbeat stops or after a 30 s timeout.
Thermal safety state (D2, firmware only). ThermalPause is entered from any active state when the EC measures skin ≥ 52 °C, internal air ≥ 60 °C, battery ≥ 55 °C, or the SoC reports any component over its limit — the case being a device packed away with a job running (the design assumes desk operation; a bag is transport only, ADR §1). The EC sends THERMAL_PAUSE_REQ; the job engine finishes the current part of every job and records offsets (same code path as LowBatteryGraceful), unmounts, and powers slots and radios down; the EC then cuts PMIC_EN and shows "Too hot — paused, will resume" on the OLED and notifies the app over BLE. In ThermalStandby the EC keeps sampling the skin, air and battery NTCs; when skin ≤ 42 °C and air ≤ 45 °C (10 K / 15 K hysteresis) and the link (Ethernet carrier or a known Wi-Fi SSID) and the cards are still present, it boots the SoC with THERMAL_RESUME and the jobs resume from their checkpoints without user action. If the cards were removed or the user powered off, the checkpoints are kept exactly as for any other pause. The 48 °C mechanical thermal switch (THERM_ALERT) is independent of this logic and remains the hardware backstop. There is no in-bag detection heuristic or in-bag operating mode (deleted in v0.3); the throttle ladder of 06-thermal.md keeps operating below these thresholds.
8. EC ↔ SoC interface
| Signal | Dir (EC view) | Type | Purpose |
|---|---|---|---|
| EC_SOC_UART_TX/RX | bidir | UART 1 Mbaud, framed, CRC | Command/event channel: mode events, display content, thermal, battery, job summary for standby display. v0.3 messages: JOB_LOCATION (EC → SoC: lat, lon, alt, horizontal accuracy, timestamp, source — forwarded from the BLE GATT "Job Location" characteristic, D13), THERMAL_PAUSE_REQ (EC → SoC) / THERMAL_PAUSE_ACK (SoC → EC, all jobs checkpointed) / THERMAL_RESUME (EC → SoC at boot, D2), PD_PRESENT state changes (Port A / Port C, for the Port B ILIM policy and UI) |
| EC_SOC_I2C | EC = target | I²C 400 kHz | SoC reads EC registers (battery, temps, shutter, mode) without waking the UART protocol; also shared bus to BQ25792 and TPM-independent sensors |
| PMIC_EN | out | GPIO | Enables the SoM's PMIC; SoC power on/off |
| SOC_RESET# | out | GPIO, open-drain | Hard reset of SoM |
| SOC_HEARTBEAT | in | GPIO toggling 1 Hz | Watchdog input |
| WAKE (SOC_WAKE#) | out | GPIO | Wake SoC from suspend (card insert, BLE, PD) |
| MUX_SEL | out | GPIO | PI3DBS16412 select, gated by hardware interlock with CFEB_PWR_EN |
| REFCLK_SEL | out | GPIO | Clock mux select (tied to MUX_SEL logically, separate pin for test) |
| CFEB_PERST# | out | GPIO | To slot; EC combines SoC_PERST_REQ or BR_PERST depending on mode |
| SOC_CFEB_PERST_REQ# | in | GPIO | SoC root port's PERST output |
| BR_CFEB_PERST# | in | GPIO | ASM2464PD's PERST output |
| CFEB_CLKREQ#, CFEB_WAKE# | in | GPIO | From slot; EC forwards to active root via two open-drain outputs |
| CFEB_PWR_EN | out | GPIO | Slot eFuse enable |
| CFEA_PWR_EN, SD_PWR_EN, USD_PWR_EN, CFAST_PWR_EN | out | GPIO | Other slot eFuses (fault input shared) |
| SLOT_DET[4:0] | in | GPIO, interrupt | Card presence, one per slot |
| SLOT_FAULT# | in | GPIO | Wired-OR eFuse fault |
| ASM2464_PWR_EN | out | GPIO | Bridge power gate |
| PORTC_ATTACH, PORTA_ATTACH, PORTB_ATTACH | in | GPIO from PD/CC controllers | Attach/detach (Port B: SSD or tethered device) |
| PORTB_ILIM (register via PD_I2C) + PORTB_VBUS_EN | out | I²C / GPIO | Port B 5 V source current limit 0.5 / 1.5 / 3 A (Rp advertisement + eFuse limit) and VBUS enable. Policy (D13, EC-enforced): effective limit = min(user setting, 1.5 A) unless PD_PRESENT (Port A contract, or PORTC_PD_PRESENT on Pro); 3 A drops to 1.5 A within 500 ms of losing external power |
| PORTC_PD_PRESENT | in | GPIO / I²C from Port C PD sink controller (Pro) | A PD source (Mac) is attached on Port C and a contract exists; gates PORTB_ILIM 3 A; shown as "powered by Mac" |
| VBUS_MUX_STAT | in | GPIO from TPS2121-class priority mux (Pro) | Which input (Port A / Port C) feeds the BQ25792; used for UI and for logging hand-over events |
| PD_I2C | EC = controller | I²C | TPS25750 (Port A), Port B/C PD controllers, BQ25792 |
| PACK_SMBUS | EC = controller | SMBus | BQ40Z50-R2 fuel gauge |
| HALL | in | GPIO | Shutter closed |
| THERM_ALERT | in | GPIO | Hardware over-temp from the 48 °C mechanical thermal switch on the chassis — hardware backstop, independent of the firmware thermal safety rule |
| NTC[5:0] | in | analog | SoC/SoM boss (doubles as chassis-skin reference), switch, CFe B cage, Ethernet PHY, battery, internal air (the v0.1 modem channel; used with the skin estimate for the D2 thermal safety thresholds — sensor allocation in 06-thermal.md §6) |
| OLED_SPI, OLED_RST, OLED_DC | out | SPI | Display |
| LED_DATA | out | serial LED | 5-segment RGB bar |
| BTN | in | GPIO | Button |
| EC_SWD | debug | SWD | EC programming; disabled at PVT |
Hardware interlock: MUX_SEL_EFFECTIVE = MUX_SEL AND NOT CFEB_PWR_EN_SENSE is not correct on its own (we need the mux to hold while powered). The implemented interlock is a D-latch: MUX_SEL is latched into the mux select pin only while CFEB_PWR_EN is low; when the slot is powered the latch is frozen. This costs one 74LVC1G373 and makes an unintended flip with a live card impossible regardless of EC firmware state.
9. Firmware-phase handoff notes
These items are consequences of the hardware choices; they are not Phase 1 deliverables but the hardware team must confirm them during EVT bring-up.
- Device tree, CFe B root port.
pcie3x4in ×2 mode withpcie3x2enabled (bifurcation per Rockchip TRMPCIE30PHYmode setting — verify on the chosen carrier's bootloader: Firefly, Forlinx or the ODM's board-down BSP, D12). The CFe B root port needs hot-plug capable behaviour: either native PCIe hotplug (hotplugin the DesignWare controller node) or a userspaceremove/rescandriven by EC events. Plan for the latter; it is deterministic. - PERST# ownership. The SoC's PERST GPIO for the CFe B port must be routed to the EC, not to the slot. Device tree
reset-gpiosbecomes a virtual GPIO or the kernel driver is patched to request reset via the EC. Simplest: SoC drives SOC_CFEB_PERST_REQ#, EC mirrors it in standalone mode with < 1 ms latency. - ASM2812 switch. Enumerates with no driver. Verify ASPM L1 substates work with all endpoints (power target 0.5 W idle) and disable per-endpoint if an ASMedia/Realtek pairing misbehaves.
- dwc3 OTG1 in peripheral mode.
dr_mode = "peripheral"fixed; role switching is not used (host role is on ASM3142). Composite via configfs:f_tcm(UAS) +f_mass_storage(BOT fallback) +f_ncm. Measure iOS behaviour with UAS early — if iOS refuses UAS from our descriptors, ship BOT for iOS host IDs. - Tethered uplink (Port B). Kernel drivers:
ipheth(iPhone Personal Hotspot over USB),cdc_ncmandrndis_host(Android USB tethering; Android 14+ defaults to NCM),cdc_mbimandqmi_wwan(USB cellular adapters). NetworkManager treats these as ordinary interfaces; ModemManager is started only when an MBIM/QMI adapter is present (it must not touch phones). Nousbmuxdis needed for theiphethdata path, but the on-phone trust prompt must be accepted — surface the wait state. Disable theipheth/NCM interfaces' USB autosuspend; enforce the SSD-vs-tether exclusivity in udev/job-engine policy (a UAS device and a network device on Port B at once = reject with message). - Wi-Fi.
mt7925efor Wi-Fi; BT lines are wired but the module's BT should be held in reset to save power. - Ethernet.
r8169(RTL8126 support from 6.9). Enable EEE; expose link speed to the job engine so it can choose parallelism. - GL9755 / GL9750.
sdhci-pci-gli; UHS-II requires the 6.x UHS-II core support — validate with V90 cards at EVT. Fallback is UHS-I at ~100 MB/s. - ASM1064.
ahci; hot-plug on CFast insertion works with AHCI hot-plug capability; confirm the ASM1064 firmware reports it. - TPM.
tpm_tis_spi; PCR-sealed LUKS2 key viasystemd-cryptenroll. - EC protocol. Define a versioned, CRC-framed UART protocol early; both sides must tolerate the other rebooting.
- Thermal. Kernel thermal zones for SoC; EC provides skin/chassis/air/card temperatures over I²C (no modem sensor in v0.2); the job engine implements the throttling policy from
06-thermal.mdand honoursTHERMAL_PAUSE_REQ(D2): checkpoint every job, unmount, power slots and radios down, ack, then let the EC cut power; onTHERMAL_RESUMEboot resume from checkpoints without user action. No in-bag heuristic exists. - Port C read-only (D8, §4.10). At every CFe B mount read Identify Controller byte 531 (
nvme id-ctrl /dev/nvmeX→nwpc) and cache per card serial; onHOST_C_ATTACHwith an NWPC card issuenvme set-feature /dev/nvmeX -f 0x84 -v 1beforeCFEB_RELEASED(i.e. before the mux flips); onCFEB_RETURNEDissue-v 0before remounting and before "Safe to remove". Non-NWPC cards: query the bridge/PD controller for USB4 vs UAS fallback and arm the ASM2464PD write-protect if ASMedia's tool exposes such a configuration (verify); otherwise hold the switch and prompt for per-session consent; log every RW session; run the full §6.5 re-verification afterwards. Survey NWPC across the card library at PoC. - Port A read-only (D8/D9). All gadget LUNs
ro=1; RW only via the per-session app toggle (revert on detach); uploads pause onHOST_A_ATTACHby default, concurrent RO sharing is the opt-in setting. - XQD detection (D10). A non-NVMe PCIe device (class code ≠ 01h/08h NVMe) enumerating in the CFe B slot → power the slot down and show "XQD not supported". This is the only XQD-related behaviour; XQD is not supported, licensed or marketed.
- Job Location (D13). EC GATT "Job Location" characteristic (vendor UUID; write-with-response; bonded/encrypted link required): lat, lon, alt, horizontal accuracy, timestamp, source →
JOB_LOCATIONover UART → job DB → upload manifest/sidecar. The same payload is accepted on the NCM/Wi-Fi management API. Jobs proceed without it. - Port B / Port C power policy (D13, D7). EC enforces PORTB_ILIM 3 A only while PD_PRESENT (Port A or PORTC_PD_PRESENT); Port C sink controller programmed like Port A's (input-current limit to BQ25792 over I²C); log VBUS_MUX_STAT hand-overs. Port C, its PD controller and the mux are behind a Pro feature flag; Base firmware never touches them and the Base FCT does not exercise them.
10. Open items specific to this document
- Resolved in v0.3 (D7): Port C is a PD sink on the Pro SKU (TPS25750-class sink + TPS2121-class priority mux into the BQ25792 input, Port A priority). Still to verify: mux rating at 20 V / 3.25 A (65 W), and PD ownership on Port C — the ASM2464PD integrates a USB-PD function; one entity must own the CC lines, so either the ASM2464PD's PD reports the sink contract to the EC or the external sink controller does and the bridge is configured PD-passive (verify with the ASMedia-access team; inconsistency noted to the ADR).
- NWPC survey (D8): at PoC, read Identify Controller byte 531 on every card in the
01-requirements.mdIF-001/IF-002 library and record which support Namespace Write Protection; this decides how often the Port C consent prompt (§4.10 mechanism 3) appears. - ASM2464PD read-only configuration (D8): confirm with ASMedia whether the bridge's UAS/BOT personality can be set write-protected (SCSI WP / firmware RO config) and whether it can be armed at run time by the EC/SoC; no effect in native USB4 mode either way.
- Confirm whether the ASM2464PD firmware tolerates a card that is removed and re-inserted under a live USB4 tunnel (PCIe surprise removal / hot-add on the downstream port), and whether it can be powered and enumerated to the host before a downstream device is present (needed for the §4.10 layered approach). Shipping USB4 CFe readers do the former, so we expect yes; verify at PoC with the dev-kit rig.
- Card left write-protected (D8 side effect): a card pulled during a Port C session with WPS = 1 set refuses camera writes until re-inserted in PoppyUp. Decide at PoC whether an OLED warning plus clear-on-re-insert is sufficient, or whether the fallback (mechanism 3) should be preferred for cards likely to be pulled hot. Add to
12-risks-open-questions.md. - Carrier form (D12): confirm RK3588
pcie3x4×2 +pcie3x2×2 bifurcation is exposed by the Firefly and Forlinx bootloaders and by the ODM's board-down BSP; if not on the chosen path, swap roles (CFe B onpcie3x2, switch onpcie3x4×2) — electrically identical. Decision at PoC exit. - Thermal safety sensors (D2): confirm the NTC allocation gives a skin estimate within ±3 K of the thermocouple map (rear-skin NTC vs SoM-boss NTC) and an internal-air reading not dominated by a nearby component;
06-thermal.md§6. - Validate tethering behaviour at PoC on the dev-kit rig: iPhone (
ipheth, trust prompt, USB 2.0-class link ~30–40 MB/s), Android 14+ (NCM default, RNDIS legacy), one MBIM and one QMI adapter; measure phone charge current at each Port B limit setting. Decide whether ModemManager ships at all or MBIM adapters are handled by a minimal in-house MBIM client.