Skip to content

jaguar3(8822e): port kernel eFEM GPIO pinmux — RX path B was antenna-less under the static DPDT route#289

Open
gilankpam wants to merge 1 commit into
OpenIPC:masterfrom
gilankpam:fix/8822e-efem-pinmux
Open

jaguar3(8822e): port kernel eFEM GPIO pinmux — RX path B was antenna-less under the static DPDT route#289
gilankpam wants to merge 1 commit into
OpenIPC:masterfrom
gilankpam:fix/8822e-efem-pinmux

Conversation

@gilankpam

@gilankpam gilankpam commented Jul 13, 2026

Copy link
Copy Markdown

Summary

The 8822E front-end fix (#268 / b5a6df7) routes the DPDT antenna transfer switch with a static two-bit write: REG_LED_CFG 0x4c[24] set, [22] clear. That made MCS4+ TX work — but it parks the switch in a TX-favoring position that disconnects RX path B's antenna on every 8812EU: chain-B pwdb pins at the noise floor (~10 raw / −99 dBm) no matter which antenna is fitted.

Nobody saw it because every RX validation to date — including #268's full-duplex proof and the 0x41e8 desense recheck — counts total frames, and chain A alone carries the stream. The loss is real, though: a dead chain B forfeits MRC diversity (3–5 dB, more in NLOS multipath).

This PR replaces the static write with a port of the kernel's actual mechanism: _efem_pinmux_config (rtl8822e_halinit.c, rfe 21–24) → halmac pinmux_set_func_8822e for RFE_CTRL_3/5/7/8/9/11, transcribed entry-for-entry from halmac_gpio_8822e.c. GPIO13 becomes WL_DPDT_SEL driven by the RFE engine's RFE_CTRL_9, so the transfer switch follows TX/RX in hardware: PA on TX, both LNAs on RX — kernel parity.

Evidence

Two-adapter bench (BL-M8812EU2 ↔ 8812EU, ch149, 20 MHz, MCS5 at 6 Mbps offered unless noted, txagc 30 near-field, per-chain rssi/snr from the phystatus via a per-chain-reporting RX tool). Same card, back-to-back bring-ups, only the DPDT route changing:

DPDT route RX chain A RX chain B TX @ MCS7 (10M offered)
static 0x4c[24]+[22]-clear (current) −68 dBm / 27 dB −99 dBm / dead 9.9M goodput, clean
0x4c[24] only (halmac WL_DPDT_SEL bit alone) −68 / 27 −77 / 19 airs nothing
eFEM pinmux (this PR) −70 / 26 −77 / 19 9.7M goodput, clean
  • The bit-level bisect that found it: with the full merge in place, skipping only the DPDT block brings chain B back; skipping only the 1SS TX mapping does not. Within the block, keeping [22] untouched revives chain B (and breaks TX — hence neither bit combination is sufficient; the pin needs its function claim).
  • Read-back before the write shows 0x4c[22] SET from reset (0x0062e282), i.e. GPIO13 parked as BT_WAKE — which is also why [24]-only TX airs nothing: no one has claimed the pin for the RFE engine. The missing piece is the pin-function claim (0x42[1] on GPIO13 + 0x43[3]/[4] fixups), which only the full pinmux walk performs.
  • Validated with both link ends running this port simultaneously: TX MCS7 9.7M / MCS5 6.0M at 0.0% loss / MCS0 clean, while RX reports both chains live (−70/−77 dBm, snr 26/19) — the combination that was previously impossible.
  • 0x41e8 history (quirks doc updated): the register is exonerated by direct A/B; the "RX desense" it was blamed for was this antenna disconnection all along, and both prior rechecks were blind to it because they counted total frames.

Implementation notes

  • RtlJaguar3Device::efem_pinmux_8822e(): per-pin priority-list walk (halmac semantics — disable every claim above the target: field := ~val & msk; enable the target: field := val & msk; stop) + the WL/BT ownership fixups, as data tables. Kernel call order preserved (RFE_CTRL_3, 5, 7, 8, 9, 11).
  • Runs post-coex in InitWrite — coex GPIO_MUXCFG writes would mask it; same ordering lesson as the existing RFE pad block. The PAD_CTRL1[29:28] post-coex re-assert is kept.
  • The old routes remain selectable for board A/B: DEVOURER_DPDT_MODE=legacy|bit24|skip (default efem).
  • docs/8822e-quirks.md updated: the pin-mux section now describes the hardware-controlled route and its A/B matrix, and adds the honest moral — per-chain RSSI is the only trustworthy RX-health metric on a 2T2R part.

Suggested follow-up (not in this PR)

GetActiveRxPaths/per-chain health could gate CI-style validation so a single-chain regression can't hide behind total-frame counts again.

🤖 Generated with Claude Code

…less under the static DPDT route

The 8822E front-end fix (OpenIPC#268/b5a6df7) routed the DPDT antenna transfer
switch with a static two-bit write (REG_LED_CFG 0x4c[24] set, [22]
clear). That fixed MCS4+ TX — but parked the switch in a TX-favoring
position that disconnects RX path B's antenna on every 8812EU: chain-B
pwdb pins at the noise floor (~10 raw / -99 dBm) regardless of antenna.
Invisible to every total-frame-count validation (incl. the OpenIPC#268
full-duplex proof and the 0x41e8 desense recheck) because chain A
carries the stream; found with per-chain RSSI on a two-adapter bench
(2026-07-14).

Live A/B on the same card, seconds apart (8812EU <-> 8812EU, ch149,
MCS5 6 Mbps offered, per-chain rssi/snr from the phystatus):

  DPDT route            RX chain A   RX chain B    TX @ MCS7
  static (b5a6df7)      -68 / 27dB   -99 / dead    9.9M clean
  0x4c[24] only         -68 / 27dB   -77 / 19dB    airs nothing
  eFEM pinmux (this)    -70 / 26dB   -77 / 19dB    9.7M clean

Fix: port the kernel's _efem_pinmux_config (rtl8822e_halinit.c, rfe
21-24) -> halmac pinmux_set_func_8822e for RFE_CTRL_3/5/7/8/9/11:
per-pin priority-list walk (disable the claims above the target, enable
the target) + WL/BT ownership fixups, transcribed entry-for-entry from
halmac_gpio_8822e.c. GPIO13 becomes WL_DPDT_SEL driven by the RFE
engine's RFE_CTRL_9, so the transfer switch follows TX/RX in hardware:
PA on TX, BOTH LNAs on RX — kernel parity. Runs post-coex in InitWrite
(coex GPIO_MUXCFG writes would mask it; same ordering lesson as the
existing RFE pad block).

Validated with both ends on this port simultaneously: TX MCS7 9.7M
goodput / MCS5 6.0M 0.0% loss / MCS0 clean, while RX reports both
chains live (A/B -70/-77 dBm, snr 26/19). The old routes stay
selectable for board A/B via DEVOURER_DPDT_MODE=legacy|bit24|skip
(default: efem). docs/8822e-quirks.md updated — including the 0x41e8
history: the register is exonerated, the desense it was blamed for was
this antenna disconnection, and per-chain RSSI is the only honest RX
health metric on a 2T2R part.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QbMmK39gvTZUeWuAx8ujCH
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant