[TRACKING] Battery flipping between charging and discharging / Draws from battery even on AC

On my FW 16 (RX 7700s) I’ve been having this issue where BF6 stutters due to my power target bouncing after about half an hour of gameplay. I recently tracked this down to performance issues occurring when the device decides to no longer pull extra juice from the battery (I’m using a 240W charger).

I’ve documented that issue here: [SOLVED] An Adventure In Mitigating Throttling - #21 by TechPriestNhyk

Does this sound like the same kind of problem being observed on this thread?

This is definitely the same problem.

Does anyone know why chg_input_current doesn’t seem to line up with the PSU rating? When I connect my 180W PSU it seems to equate to a power budget of 135W, and my 240W yields 183W when looking at both framework control and manually calculating wattage via the chg_voltage (17.6v) and input current.

$ sudo ectool chargestate show
ac = 1
chg_voltage = 16704mV
chg_current = 0mA
chg_input_current = 4500mA
batt_state_of_charge = 81%

and

$ sudo ectool chargestate param
base params:
  0   chg_voltage
  1   chg_current
  2   chg_input_current
  3   chg_status
  4   chg_option
  5   limit_power

The param 2 points to the chg_input_current which is always 0.9 times the PD rated current, regardless of the PD voltage. I personally recommend $ sudo ectool chargestate param 2 5000 for all >100W PD input to maximize utilizing the power adapter. It’ll only change back if you unplug and replug the adapter. only suitable for SPR charger, not suitable for EPR chargers

Not writing anything after the param 0~5 reads the value

$ sudo ectool chargestate param 4
42926080 (0x28f0000)       # chg_option
$ sudo ectool chargestate param 3
49168 (0xc010)       # chg_status

I can’t decipher what 3 and 4 mean.
0 and 1 is the battery’s constant voltage and constant current values, which changes according to the battery charge level and the EC charge limit setting. In this example, the current is zero because the state of charge is at the charge limit. Changing these two values is not recommended and it often change back immediately.
You can probably change 3 or 4 if you know what these hex values mean but I think you can also use $ sudo ectool chargecontrol

EDIT: wrong info

I’m really confused by how an input current of 5,000 is working for you, unless you’re using a lower wattage charger for the FW 13. The default value for me on my FW 16’s 240W charger is over 10,000 (I see now that I didn’t share any of those default values earlier). As far as I understand, it pulls about 4.5A at ~47V, but as far as I can tell this ectool only see’s the current after it’s been stepped down to 17.6V, which would be need a much higher amperage for the same watt load. This higher value is closer to what I see in the default value (i.e. >5000). When I did the 5000 as you suggested while gaming, my system started to freak out as if it couldn’t figure out whether it should be using the charger or using the battery.

On your charger, what is the default chg_input_current before you modify it?

If I understand what you’re saying correctly, and can safely assume that this also applies to the 180W and 240W chargers, the hardware is rated for 180W/240W, but because the measurements of current load are innaccurate, the software targets 180W x 0.9 and 240W x 0.9 because of the measurement error margins could rish pushing say, a 240W charger up to say 264W?

If I multiply 180 and 240 by 0.9, I get the “exact” values my current meter measures going into the framework under load. I.e. my 180W caps out around 160W, and my 240W caps out around 216W.

4500

For some reason, an over 10000 value pulls about 4500mA from the 240W charger. I guess I was wrong. I assumed EPR chargers work the same. On my FL13, the chg_input_current is always equal to the current drawn(in mA) from the PD charger at max load.

What’s the exact value of the “over 10000”? divide the value by 4.5 and multiply by 5 and write the result into param 2

Output of my unmodified ectool chargestate show with my 240W charger gives me

chg_voltage = 17800mV
chg_current = 0mA
chg_input_current = 10256mA
batt_state_of_charge = 100%

I calculated a 5A equivalent as 13483, and asked about it in another thread: Framework 16 7940HS w/ 7700s 240W Charger Drops BF6 FPS from 100 to 35 - Community Support - Framework Community

I’ve since tried setting 13484, but have yet to see my PSU supply 240W. It should be fine on modern kernels to leave secure boot on, right? Some things on ectool seem to work, and some don’t (at least in the way I anticipated).

On the FW16 AMD 7840HS, I have:
sudo ectool chargestate param 2
interfaces:0xffffffff
comm_init_dev being used /dev/cros_ec
7680 (0x1e00) # chg_input_current
I.e. 7680mA.
On the FW16, the 36V or 48V from the power adapter is stepped down to 20V, so the 7680mA is at 20V, so == 153.6W while a 180W FW charger is plugged in.

240W / 20V = 12000mA
200W / 20V = 10000mA
85% = 204W / 20V = 10200mA

The EC code fixes it at about 85% of the power adapter.

On the FW13, there is no step down to 20V. The FW13 tries to get 20V from the power adapter if the power adapter can do it, otherwise is boosts the power adapter up to 20V.
So, a FW13 60W PSU might be 60W / 20V = 3000mA
100W PSU might be 100W / 20V = 5000mA
100W * 85% = 85W / 20 V = 4250mA
100W * 90% = 90W / 20 V = 4500mA

The EC sets it lower than the 100% because the charger input current detection is not well calibrated, thus leaving space for calibration errors.

To properly calibrate it, one would need to somehow get the CPU to draw a constant current and then compare the EC measured value with an external calibrated PSU.

Ah ok, thanks for the clarification. Using 20V in the calculation fixes the error delta between my math and what the watt meter was seeing! My math was estimating that I should be pulling 182W when I was actually pulling 205W… which is exactly the adjustment that happens at 17.8V → 20V calculations :slight_smile:

BF6 is one of the anti-consumer-rights game to force secure boot (and bricking desktop mainboards due to messing with the bios settings) on gamers. I highly recommend to use other games to test max performance.

What’s the actual current from the charger? and what’s the result of ectool chargestate show after setting chg_input_current = 13484?

Then test again, this time setchg_input_current = 11396, what’s the result?

I hear you, BF6 is the only reason I even have an installation of windows on an extra drive right now and I’m not happy about it. Trouble is, I haven’t found a game that is capable of hitting the cpu and gpu as hard as it can. Both CPU and GPU sit at 100% steadily. I’ve tried synthetically increasing cpu load with something like stress -c X but that hurts gaming performance and suddenly I can’t get enough frames to keep the GPU clocked up.

When I set it to 13484 it’ll apply, but it’ll knock a couple off. I forget exactly what, but a chargestate show will return something like 13482. I now know that I’m calculating that wrong, and the 13484 is actually equivalent to ~270W (and would explain why my device did not seem to like that setting). I’ll try 12k and 11k as you and James suggest tonight after work.

I use Furmark (available on both Linux and Windows), and stress -c at the same time to achieve this

1 Like

I’ll give that a shot next time then :slight_smile:

I tested 11k and 12k input currents while running furmark and stress on 16 threads. I didn’t pull 240W, but I did pull a stead 215W or so… and pull nothing from my battery. That’s honestly all I needed, was just a teensy bit more juice, and ectool got me there! Thanks for the help guys!

1 Like

Framework Universal Smart Power Controller

“Elastic V-Curve” Implementation Guide (FW13 & FW16)

Target Hardware:

  • Framework Laptop 13 (AMD 7040 / AI 300)
  • Framework Laptop 16 (AMD 7040 / AI 300)

Target Controller: ISL9241 Buck-Boost Charger
Firmware Base: Chromium EC (cros-ec)

1. Executive Summary

This firmware modification addresses the “battery flipping,” “input throttling,” and “400MHz lock” issues observed on AMD-based Framework laptops during high-load scenarios.

It replaces the stock reactive logic with a Proactive Hybrid Controller that implements an Universal Elastic V-Curve. This allows the battery to act as a high-performance energy buffer without triggering false-positive safety trips.

Key Findings from Community Engineering

  1. The “Crying Wolf” Prochot: The stock firmware enables “Input Current PROCHOT” (Bit 9 of Control2). This triggers CPU throttling immediately when an input spike occurs, even if the battery is healthy and capable of assisting. Fix: We disable this specific bit and manage thermal safety manually.
  2. The BGATE Danger: Warning from Framework Staff: The BGATE MOSFETs must never be manually disabled under load. Their body diodes are rated for only ~2A. Fix: This implementation manages power exclusively via current limits, ensuring the ISL9241 hardware state machine keeps BGATE safely engaged in NVDC mode.
  3. FW16 Power Topology: The FW16 uses a pre-buck converter that steps 36V/48V inputs down to a fixed 20V before reaching the charger. Fix: FW16 input limits must be calculated relative to this 20V rail, not the external PD voltage.

2. Power Source Logic Categories

The controller automatically classifies the power source into one of four modes to optimize behavior. Crucially, all modes allow for eventual recovery to the target charge limit (e.g., 100%), though the timeframe varies significantly.

Source Class Wattage Logic Strategy Goal Recovery Profile
Desktop > 85W Passthrough Max Performance. No Boost needed. Instant. Charges during load.
Hybrid 20W - 85W Elastic V-Curve Bridge the deficit. Maintain system stability. Managed. Throttles to force charge when low.
Range Extender 8W - 20W Input Saturation Slow the drain. Input < Load. Opportunistic. Charges when user Idles/Types.
Scavenger < 8W Sleep Only Ignore active usage. Charge when sleeping. Overnight. Charges only in s2idle or Off.

Eventual Recovery Assurance

Even on the weakest power sources (e.g., a 15W phone charger), the logic ensures the battery will eventually reach the user-defined limit (e.g., 90% or 100%) provided the laptop remains plugged in.

  • Mechanism: When the system is idle or sleeping, power consumption drops to ~0.8W - 5W. The controller directs the remaining 10W+ from the phone charger entirely into the battery. Over a long duration (e.g., leaving it plugged in overnight), this accumulates to a full charge, resetting the “Boost Budget” for the next day’s high-performance tasks.

Adaptive Floor (“Snooze” Functionality)

The system enforces a Safety Floor (Default: 20%) where “Boost” is disabled to protect the battery and ensure user runtime if unplugged. However, power users may need to finish a critical task (e.g., a ranked match or a render) and are willing to sacrifice that safety buffer.

  • The Trigger: Unplugging and Replugging the USB-C charger while at the floor.
  • The Logic:
    1. System detects AC insertion.
    2. Checks if Battery SoC is near the current floor (e.g., 20%).
    3. Action: Lowers the floor by 5% (e.g., new floor = 15%).
    4. Result: Turbo Boost is immediately unlocked again.
  • Safety Stop: This “Snooze” can be repeated until the Absolute Floor (5%) is reached, at which point the system enforces hard throttling to prevent brownouts.

3. Comprehensive Behavior Matrices

Battery Assumptions:

  • FW16: 85Wh Capacity (Recovery to 90% requires ~61Wh).
  • FW13: 61Wh Capacity (Recovery to 90% requires ~43Wh).

Scenario A: The “Desktop” (FW16 on 240W)

Load: 220W (Gaming) | Adapter: 240W (Effective ~215W) | Deficit: ~5W

Time Battery State Performance Behavior
0h 100% Turbo 100% Battery covers 200W+ transients.
2h 50% Turbo 100% High voltage maintains efficiency.
3h 20% Hit Floor 95% Throttled to 215W. Battery protected.
Action Replug Reset $\downarrow$ Floor → 15%. Full boost restored.
Recovery Active Charging ~34 Hours Time to Full while Gaming (2W trickle).
Recovery Idle Fast Charge ~50 Mins Time to Full if Gaming stops.

Scenario B: The “Standard” (FW13 on 60W)

Load: 70W | Adapter: 60W (Effective 57W) | Deficit: 12W

Time Battery State Performance Behavior
0h 100% Turbo 100% Battery bridges 12W deficit.
4h 22% Gliding 94% Boost limit gently reduces.
4.5h 20% Hit Floor 81% (57W) Throttled. System stable at wall limit.
Recovery Active Charging ~24.5 Hours Time to Full while Gaming (2W trickle).
Recovery Idle Fast Charge ~1 Hour Time to Full if Gaming stops.

Scenario C: The “Traveler” (FW13 on 45W Charger)

Load: 70W | Adapter: 45W (Effective 42W) | Deficit: 28W

Time Battery State Performance Behavior
0h 100% Turbo 100% Massive battery assist (-28W).
1h 54% Gliding 98% Curve begins to limit peak power.
1.5h 20% Floor Hit 57% (40W) Hard Throttle. Performance crash.
Recovery Active Charging ~24.5 Hours Time to Full while Gaming (2W trickle).
Recovery Idle Charge ~1.5 Hours Time to Full if Gaming stops.

Scenario D: The “Emergency” (FW13 on 15W Phone Charger)

Load: Variable | Adapter: 15W (5V/3A) | Behavior: Survival

Usage Battery Performance Behavior
Gaming Draining Unplayable System cannot sustain high load.
Office Steady 100% Range Extender. Battery covers spikes.
Recovery Active N/A Infinite
Recovery Office Slow ~16 Hours
Recovery Sleep Scavenge ~3.5 Hours

Scenario E: The “Trickle” (FW13 on Legacy USB-A / < 5W)

Load: Variable | Adapter: 2.5W - 4.5W

Usage State Behavior
Active Draining Slow Death. Source cannot power screen + CPU. Discharges slowly.
Recovery Active Impossible
Recovery Sleep ~15 Hours

4. Implementation Guide

A. Configuration Header (smart_power_config.h)

Create board/yourboard/smart_power_config.h. Uncomment the correct definition for your target board.

#ifndef __SMART_POWER_CONFIG_H
#define __SMART_POWER_CONFIG_H

// ==========================================
// SELECT TARGET BOARD (Uncomment ONE)
// ==========================================

// #define BOARD_FRAMEWORK_13_AMD
// #define BOARD_FRAMEWORK_16_AMD

// ==========================================
// TUNING PARAMETERS
// ==========================================

#ifdef BOARD_FRAMEWORK_16_AMD
    /* Framework 16 Specifics */
    // FW16 uses a pre-buck converter to 20V.
    // Max Transient Load ~450W observed in lab (James3).
    // Battery supports massive discharge bursts.
    #define MAX_BOOST_MW          180000  // Extreme Boost allowed
    #define HIGH_POWER_SOURCE_MW  241000  // Even 240W needs logic
    #define HAS_INPUT_BUCK_CONV   1       // 20V Step-down exists
    #define FIXED_INPUT_VOLTAGE   20000   // Charger sees 20V
#else
    /* Framework 13 Specifics */
    // FW13 is Direct VBUS.
    // Battery limited to ~1C discharge recommended.
    #define MAX_BOOST_MW          25000   // 25W Max Boost
    #define HIGH_POWER_SOURCE_MW  85000   // 85W+ is Desktop Mode
    #define HAS_INPUT_BUCK_CONV   0       // Direct VBUS
    #define FIXED_INPUT_VOLTAGE   0       // Dynamic
#endif

// Universal Constants
// Safety Margin: Stock FW uses 90%. We use 95% for better performance.
// Warning: 100% may trip OCP on older chargers due to measurement tolerance.
#define INPUT_SAFETY_MARGIN_PERCENT 95

#define LOW_POWER_SOURCE_MW   15000   // Threshold for Survival Mode
#define DEFAULT_FLOOR_SOC     20
#define ABSOLUTE_MIN_SOC      5
#define CEILING_BUFFER        2
#define CHARGE_PENALTY_MW     2000    // 2W forced charge
#define S2IDLE_DRAIN_MW       1500
#define BROWNOUT_VOLTAGE_MV   4600

#endif

B. Core Logic Module (smart_power.c)

This logic implements the survival hierarchy for weak sources and the critical architecture handling for FW16.

/*
 * Framework Universal Smart Power Controller
 * Implements Elastic V-Curve with Universal Source Handling
 */

#include "smart_power_config.h"
#include "driver/charger/isl9241.h"
#include "charge_state.h"
#include "usb_pd.h"
#include "math_util.h" 
#include "chipset.h"
#include "thermal.h"
#include "console.h"

// INTERNAL STATE
enum power_mode {
    MODE_TURBO_DESCENT,
    MODE_FORCED_RECOVERY
};

static enum power_mode current_mode = MODE_TURBO_DESCENT;
static int adaptive_floor = DEFAULT_FLOOR_SOC;

/*
 * HELPER: Calculate Battery Boost
 */
static int32_t calculate_boost_limit(int soc) {
    if (soc <= adaptive_floor) return 0;
    int range = 100 - adaptive_floor;
    int val = soc - adaptive_floor;
    
    // Normalized Sqrt Curve
    uint32_t ratio_scaled = (val * 10000) / range;
    uint32_t curve_factor = integer_sqrt(ratio_scaled);
    
    // Returns mW allowed from battery
    return (MAX_BOOST_MW * curve_factor) / 100;
}

/*
 * HELPER: Get User Charge Limit
 */
static int get_effective_ceiling(void) {
    #ifdef CONFIG_CHARGER_PROFILE_OVERRIDE
    int user_limit = charge_get_display_charge();
    if (user_limit > 0 && user_limit <= 100) return user_limit;
    #endif
    return 100;
}

/*
 * EVENT: Replug ("Snooze")
 */
void smart_power_on_ac_change(void) {
    if (extpower_is_present()) {
        int soc = charge_get_percent();
        // Drop floor if user replugs near the limit
        if (soc <= (DEFAULT_FLOOR_SOC + 5) && soc > ABSOLUTE_MIN_SOC) {
            adaptive_floor = (soc > 5) ? (soc - 5) : ABSOLUTE_MIN_SOC;
            current_state = MODE_TURBO_DESCENT;
            cprintf(CC_CHARGER, "SmartPower: Turbo Reset! Floor: %d%%\n", adaptive_floor);
        } else if (soc > DEFAULT_FLOOR_SOC) {
            adaptive_floor = DEFAULT_FLOOR_SOC;
            current_state = MODE_TURBO_DESCENT;
        }
    }
}

/*
 * MAIN LOOP (1Hz)
 */
void smart_power_update(void) {
    if (!extpower_is_present()) return;
    
    int soc = charge_get_percent();
    int active_port = charge_manager_get_active_charge_port();
    if (active_port < 0) return;

    // 1. ANALYZE SOURCE & EFFECTIVE POWER
    int pd_volts_mv = pd_get_negotiated_voltage(active_port);
    int pd_amps_ma = pd_get_negotiated_current(active_port);
    int32_t wall_mw = (pd_volts_mv * pd_amps_ma) / 1000;
    
    // Safety Derating: Configurable margin
    wall_mw = (wall_mw * INPUT_SAFETY_MARGIN_PERCENT) / 100;

    int32_t input_limit_mw = wall_mw;
    int32_t discharge_limit_mw = 0;

    // 2. STATE MACHINE
    int exit_threshold = get_effective_ceiling() - CEILING_BUFFER;
    if (exit_threshold < DEFAULT_FLOOR_SOC + 5) exit_threshold = DEFAULT_FLOOR_SOC + 5;

    switch (current_state) {
        case MODE_TURBO_DESCENT:
            if (soc <= adaptive_floor) current_mode = MODE_FORCED_RECOVERY;
            break;
        case MODE_FORCED_RECOVERY:
            if (soc >= exit_threshold) {
                current_state = MODE_TURBO_DESCENT;
                adaptive_floor = DEFAULT_FLOOR_SOC;
            }
            break;
    }

    // 3. LOGIC BRANCHING
    
    // S2IDLE (Scavenger)
    if (chipset_in_state(CHIPSET_STATE_STANDBY)) {
         if (wall_mw > S2IDLE_DRAIN_MW + 500) {
             input_limit_mw = wall_mw;
             discharge_limit_mw = 0;
             // Ensure Charge Path Open
             i2c_write16(I2C_PORT_CHARGER, ISL9241_ADDR_CHARGE, 
                         ISL9241_REG_CHARGE_CURRENT_LIMIT, 4000); 
         } else {
             // Protect weak source from brownout loop
             i2c_write16(I2C_PORT_CHARGER, ISL9241_ADDR_CHARGE, 
                         ISL9241_REG_CHARGE_CURRENT_LIMIT, 0);
             return;
         }
         goto apply_limits;
    }

    // A. DESKTOP MODE
    if (wall_mw > HIGH_POWER_SOURCE_MW) {
        current_mode = MODE_TURBO_DESCENT;
        input_limit_mw = wall_mw;
        discharge_limit_mw = 0;
    }
    // B. SURVIVAL MODE (Weak Charger)
    else if (wall_mw < LOW_POWER_SOURCE_MW) {
        // Source is too weak to throttle (Throttle target would be < Idle).
        // Strategy: Max Input, Max Discharge. 
        // We let the battery drain naturally to prevent voltage collapse.
        input_limit_mw = wall_mw;
        discharge_limit_mw = 0xFFFF; // Max value (No throttling)
    }
    // C. HYBRID MODE (Normal)
    else {
        if (current_mode == MODE_FORCED_RECOVERY) {
            // Force charge reservation
            if (wall_mw > CHARGE_PENALTY_MW + 8000) {
                input_limit_mw = wall_mw - CHARGE_PENALTY_MW;
            } else {
                input_limit_mw = wall_mw;
            }
            discharge_limit_mw = 0;
        } else {
            // Turbo
            discharge_limit_mw = calculate_boost_limit(soc);
        }
    }

apply_limits:
    // 4. HARDWARE TRANSLATION (CRITICAL FW13 vs FW16 LOGIC)
    
    uint16_t reg_input_ma = 0;
    
    #if HAS_INPUT_BUCK_CONV
        // FW16 Architecture:
        // Adapter(36/48V) -> Buck(20V) -> ISL9241 -> Vsys.
        // The ISL9241 measures input current at its own input pin (20V rail).
        // We want to limit Power. P = I * V.
        // I_reg = Power_Limit / 20V.
        reg_input_ma = input_limit_mw / 20; 
    #else
        // FW13 Architecture:
        // Adapter(Vbus) -> ISL9241 -> Vsys.
        // The ISL9241 measures input current at Vbus.
        // I_reg = Power_Limit / Vbus.
        int effective_v = (pd_volts_mv > 0) ? pd_volts_mv : 5000;
        reg_input_ma = input_limit_mw / (effective_v / 1000);
    #endif

    // Discharge is usually relative to Battery Voltage (~15-16V)
    uint16_t reg_discharge_ma = discharge_limit_mw / 15;

    // 5. WRITE REGISTERS
    i2c_write16(I2C_PORT_CHARGER, ISL9241_ADDR_CHARGE, 
                ISL9241_REG_ADAPTER_CURRENT_LIMIT1, reg_input_ma);
                
    i2c_write16(I2C_PORT_CHARGER, ISL9241_ADDR_CHARGE, 
                ISL9241_REG_DISCHARGE_CURRENT_LIMIT, reg_discharge_ma);

    // 6. PROCHOT CONFIG
    // CRITICAL: Disable "Input Current Prochot" (Bit 9)
    // This bit is the cause of the "Crying Wolf" throttling issue.
    // We replace it with our calculated "Discharge Limit Prochot" (Bit 11).
    uint16_t c2;
    i2c_read16(I2C_PORT_CHARGER, ISL9241_ADDR_CHARGE, ISL9241_REG_CONTROL2, &c2);
    
    c2 &= ~ISL9241_CONTROL2_INPUT_CURRENT_PROCHOT; // DISABLE panic throttling

    if (discharge_limit_mw > 0 && discharge_limit_mw < 0xFFFF) {
        c2 |= ISL9241_CONTROL2_BAT_DISCHARGE_CURRENT_PROCHOT; // ENABLE smart throttling
    } else {
        c2 &= ~ISL9241_CONTROL2_BAT_DISCHARGE_CURRENT_PROCHOT;
    }
    
    i2c_write16(I2C_PORT_CHARGER, ISL9241_ADDR_CHARGE, ISL9241_REG_CONTROL2, c2);
}

C. Integration Hooks

Modify board/yourboard/board.c.

#include "smart_power.h"
#include "hooks.h"

// Register 1Hz loop
DECLARE_HOOK(HOOK_SECOND, smart_power_update, HOOK_PRIO_DEFAULT);
// Register AC Change for Replug/Snooze Logic
DECLARE_HOOK(HOOK_AC_CHANGE, smart_power_on_ac_change, HOOK_PRIO_DEFAULT);

static void board_init(void) {
    smart_power_init();
}
DECLARE_HOOK(HOOK_INIT, board_init, HOOK_PRIO_DEFAULT);

5. References

  1. Renesas ISL9241 Datasheet:
    https://www.renesas.com/us/en/document/dst/isl9241-datasheet
    Defines registers 0x39 (Discharge Limit), 0x3F (Adapter Limit), and 0x3D (Control 2).

  2. Chromium EC Source Code (Framework EC):
    https://github.com/FrameworkComputer/EmbeddedController

  3. Framework Community Thread (FW13 Investigation):
    https://community.frame.work/t/tracking-battery-flipping-between-charging-and-discharging-draws-from-battery-even-on-ac/22484
    Primary source for battery flipping behavior and input throttling analysis.

  4. Framework 16 Power Architecture:
    Forum thread analysis confirms 3-stage power train (Adapter → Buck → Charger → VRM), necessitating the fixed 20V input calculation logic.

Is this AI? FL13 doesn’t draw 60W at constant load, and for both AMD FL13 and FL16 a 5V charger is not supported

It’s bounced through several local models - and validated, yes, but the logic is sound. I am aware the FW13 doesn’t draw that under NORMAL conditions it’s a potential hypothetical worst case load. Strix can have it’s TLP fiddled relatively easily i.e via smokeless. Matrices are there to illustrate behaviour.

Regards the 5V ‘not supported’ there is nothing inherent other than EC policy stopping 5V sources from charging the F13, and it’s fully supported by the ISL9241. The FW16 is a question mark on this front and will depend on if UVLO is enabled on the Buck Boost IC or not, information I don’t have. For the FW13 tho it’s pure policy.

@jwp
You will actually need to test your theory. I.e. make your changes to the firmware and see if they actually fix a problem for you.
I have no idea if it will help or not.

Last night I tested using 11000 and 12000 input current values while gaming on my FW 16, and a miraculous thing happened… my device just worked! It would pull between 0-2W from the battery every once in a while to top up, but on average it was running entirely on AC power and pulling a steady 215W. Both 11k and 12k input currents seemed to cap out at about 215W draw, but I’m pretty sure that’s just because that’s how much power it needed.

If I didn’t also have a 180W PSU that I used sometimes, I would configure KDE to auto execute the ectool command on plug-in, but oh well. I’ll just run it manually as I don’t want to forget about a 240W override when I’m plugging in a 180W adapter, or some other random charger.

Thank you so much for the help guys! I’ve spent the last 2 months tinkering with and trying to get my Framework to work the way I want, and now that I have… I gotta remember what it was I was doing before that started lol.