Repository navigation
Conversation
During EPR entry/exit the EC intentionally puts the system into a transitional clamp state: charger input current is collapsed to 0mA (learn mode on) and a hard AP throttle is asserted via update_pmf_events(BIT(PD_PROGRESS_ENTER/EXIT_EPR_MODE), 1). On a healthy transition this lasts ~1.1s and both are released after the new contract lands. When the transition fails, two pieces of that transitional state currently persist beyond the transition: 1. Neither failure handler releases the PMF transition event. The normal release path, clear_prochot(), only clears the ENTER event when cypd_get_ac_power() > 100W, which is never true after a failed EPR entry (the standing SPR contract is at most 100W), so the hard throttle outlives the transition it was meant to cover and is left to the 3s force_clear_pmf_prochot backstop at best. Release the ENTER/EXIT transition events at both points where the flow is abandoned: the EPR_MODE_ENTER_FAILED/default branch of cypd_update_epr_state(), and the retry-exhausted branch of epr_flow_pending_deferred() (the path taken when the EPR event from the PD controller never arrives). The failure handler now leaves the system in the plain-SPR state (input limit is already restored by the existing cypd_update_port_state() call on both paths) instead of the mid-transition state. 2. The "EPR fail, do not retry" latch (epr_active = 0xff) is only cleared by clear_port_state() on a PORT_DISCONNECT/hard-reset response. cypd_update_port_state() deliberately preserves it, and a CCG_RESPONSE_PORT_CONNECT does not reset it, so if the disconnect response is missed (the same event-loss class the deferred retry exists for) or EC RAM outlives a PD controller reset, the latch outlives the connection that caused it and the port can never enter EPR again. Clear the latch on PORT_CONNECT: a fresh attach is a new connection, so the do-not-retry intent (no EPR retry on the contract that failed) is preserved while the latch can no longer outlive that connection. Field evidence (FW16 Ryzen AI 300 "tulip", RTX 5070, 240W EPR, EC tulip-4.0.1): instrumented captures show the system latching into the transitional state - charger input stuck at ~0/500-800mA, hard throttle asserted (BIOS runs slow, survives OS reboot), and a port stuck attach-without-contract where EC_CMD_GET_PD_PORT_STATE returns zeros and the EC reports "AC not connected". Resets of either or both PD controllers do not clear it (EC RAM state re-imposes it after every reset); only a physical AC removal recovers, because only the PD_PROGRESS_DISCONNECTED path clears the progress/event state. A healthy reference trace of the same transition (throttle on, input 500mA, ~1.1s, then release) was captured for comparison. Issue: tyeth/framework-system#1 Telemetry/data: https://github.com/tyeth/fw16-gpu-watchdog Related: FrameworkComputer/SoftwareFirmwareIssueTracker #160 #193 #241 #243 Compile-untested: no Zephyr build environment was available; changes were limited to call sites and symbols already used in these files and verified by read-through. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…oned On boards with the RAA489300 3-level buck (tulip), enter_epr_mode() / exit_epr_mode() first collapse the charger input current to 0mA (learn mode on) and assert the PMF transition throttle, then hand off to buck_ready_for_epr() to sequence the buck mode change before the CCG EPR command is sent. If the buck readiness check exhausts its retries, the handler reverts the buck mode and goes idle - but the CCG command is never sent, so no EPR event ever arrives, and because epr_flow_pending_deferred() is only scheduled from handle_pd_epr_mode() (after buck ready), the event-loss backstop never runs either. The pd_epr_in_progress port bits, the collapsed input current and the transition throttle are all left standing: a second independent route into the latched slow state that otherwise only a physical AC removal clears. Give the abandonment branch the same cleanup the retry-exhausted branch of epr_flow_pending_deferred() already performs: clear the in-progress port bits (keeping the process flags so the existing restore block in cypd_update_port_state() runs), release the ENTER/EXIT transition throttle events, and refresh the active charge port so the input current limit is restored from the standing contract. The buck mode revert, retry count and timing are unchanged; on an abandoned exit the standing contract is the still-active EPR contract and epr_active stays 1, so a later exit attempt remains possible. This stacks on "fwk: epr: make a failed EPR transition recoverable without AC removal" (the two compose: that change covers the failure paths after the CCG command is sent, this one covers the flow being abandoned before it is sent). Issue: tyeth/framework-system#1 Telemetry/data: https://github.com/tyeth/fw16-gpu-watchdog Compile-untested: no Zephyr build environment was available; the change uses only symbols already used in this file and was verified by read-through. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This was referenced Oct 20, 2026
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.
Stacked on #90 (this branch includes that commit; diff relative to it is the single buck_ready_for_epr() change). Investigation + telemetry: tyeth/framework-system#1, https://github.com/tyeth/fw16-gpu-watchdog. Fork-side review copies: tyeth#2, issue tyeth#4.
Stacks on #1 (base branch is
fix/epr-fail-recovery; the two compose and this diff shows only the buck change).Problem
On boards with the RAA489300 3-level buck (tulip, i.e. FW16 Ryzen AI 300), the EPR flow has a second, independent route into the latched slow state that #1 does not cover — abandonment before the CCG command is ever sent:
enter_epr_mode()/exit_epr_mode()collapse the charger input current to 0mA (learn mode on), assert the PMF transition throttle, set thepd_epr_in_progressport bits, and hand off tobuck_ready_for_epr()to sequence the buck mode change.buck_ready_for_epr()retries up to 4 times; on exhaustion it reverts the buck mode (LEVEL_BUCK_SPR/LEVEL_BUCK_EPR) and goes idle — and returns.INITIATE_EPR_ENTRY/EXITcommand is never sent, so no EPR event will ever arrive; and becauseepr_flow_pending_deferred()is only scheduled fromhandle_pd_epr_mode()(i.e. after buck-ready succeeds), the event-loss backstop that hx20: CHARGE_LIMIT_CONTROL min_percentage is unused #1 fixed never runs either.Result:
pd_epr_in_progressport bits, the 0mA input limit, learn mode, and the hard transition throttle are all left standing indefinitely — the same end state documented with full telemetry in tyeth/framework-system#1 (data: https://github.com/tyeth/fw16-gpu-watchdog), reachable without any PD message loss.Why exactly these lines
The abandonment branch now performs the same cleanup the retry-exhausted branch of
epr_flow_pending_deferred()already performs, in the same order:pd_epr_in_progress &= EPR_PROCESS_MASK— clears the port bits while keeping the ENTER/EXIT process flags, which is exactly the precondition for the existing restore block incypd_update_port_state()(restore input limit from the standing contract PDO,board_discharge_on_ac(0),level_buck_set_input_current_limit(), thenclear_erp_progress_mask()). No new restore code is introduced.update_pmf_events(BIT(PD_PROGRESS_ENTER/EXIT_EPR_MODE), 0)— releases the transition throttle the same way hx20: CHARGE_LIMIT_CONTROL min_percentage is unused #1 does on the post-command failure paths (idempotent; the ENTER clear also restores the GPU ACDC GPIO; if other PMF events are set the throttle correctly stays).cypd_update_port_state()on the active charge port — triggers that restore block immediately, mirroring the give-up branch ofepr_flow_pending_deferred()line for line.What this deliberately does not touch
WAIT_TRANSITION_TIMEare unchanged.epr_support/epr_activeare not latched here: a buck readiness failure is a power-stage sequencing problem, not a charger EPR rejection, so a later transition attempt (next contract event or resume) stays possible. On an abandoned exit, the standing contract is the still-active EPR contract andepr_activestays 1, so exit can be retried.buck_ready_for_epr()("Enter EPR aborted: not in S0 or no power") is unchanged: the no-power case is cleaned up by the existing disconnect path, and the not-in-S0 case is re-driven by the resume hook; widening this patch to cover it would touch suspend/resume ordering and is left for a separate discussion.Risk analysis
charge_port == -1(adapter gone mid-flow), the input-limit refresh is skipped exactly as in the existing give-up branch; the disconnect path (clear_erp_progress()+PD_PROGRESS_DISCONNECTED) already owns that cleanup, and the process flags are cleared by the nextcypd_update_port_state()on any port.phase, so there is no concurrent buck flow to disturb.How a FW16 owner would test
board_confirm_buck_transition_ready()to report not-ready in a test build, or interrupt input power timing during entry so the buck cannot settle). Confirm the console shows the five3Level-Buck enter epr retrylines then3Level-Buck enter epr failed, reverting to SPR mode, and that immediately afterwards: input current returns to the SPR contract value (not 0mA), learn mode is off (battery not discharging on AC), and the hard throttle (h_prochot_l/ 0x3E22 hard mask) deasserts without waiting forpmf update timeout.🤖 Generated with Claude Code