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
Open
Conversation
…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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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) → halmacpinmux_set_func_8822eforRFE_CTRL_3/5/7/8/9/11, transcribed entry-for-entry fromhalmac_gpio_8822e.c. GPIO13 becomesWL_DPDT_SELdriven 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:
0x4c[24]+[22]-clear (current)0x4c[24]only (halmacWL_DPDT_SELbit alone)[22]untouched revives chain B (and breaks TX — hence neither bit combination is sufficient; the pin needs its function claim).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.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).InitWrite— coexGPIO_MUXCFGwrites would mask it; same ordering lesson as the existing RFE pad block. ThePAD_CTRL1[29:28]post-coex re-assert is kept.DEVOURER_DPDT_MODE=legacy|bit24|skip(defaultefem).docs/8822e-quirks.mdupdated: 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