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

With the current implementation yes but it also can’t really reach PD capacity as the charge controller is told to keep it at 90% and it is very good at that.

I did just update to the most recent git version of ryzenadj and it gives me a lot more infos that used to be nan. I will retest the lower P3T values and see if the mystery throttling is visible there somewhere.

Edit: Nope still find nothing obvious. Here is the readout of stress (16 thread) running with a 45W powerbank connected (P4T 40500) and it somehow power limits to a bit over 10W.

CPU Family: Phoenix Point
SMU BIOS Interface Version: 15
Version: v0.16.0
PM Table Version: 4c0009
|        Name         |   Value   |     Parameter      |
|---------------------|-----------|--------------------|
| STAPM LIMIT         |    53.000 | stapm-limit        |
| STAPM VALUE         |    10.531 |                    |
| PPT LIMIT FAST      |    53.000 | fast-limit         |
| PPT VALUE FAST      |    10.722 |                    |
| PPT LIMIT SLOW      |    35.000 | slow-limit         |
| PPT VALUE SLOW      |    10.536 |                    |
| StapmTimeConst      |     0.000 | stapm-time         |
| SlowPPTTimeConst    |     0.000 | slow-time          |
| PPT LIMIT APU       |    41.000 | apu-slow-limit     |
| PPT VALUE APU       |     0.000 |                    |
| TDC LIMIT VDD       |    54.000 | vrm-current        |
| TDC VALUE VDD       |     9.445 |                    |
| TDC LIMIT SOC       |    16.000 | vrmsoc-current     |
| TDC VALUE SOC       |     1.461 |                    |
| EDC LIMIT VDD       |    41.859 | vrmmax-current     |
| EDC VALUE VDD       |    23.330 |                    |
| EDC LIMIT SOC       |    23.000 | vrmsocmax-current  |
| EDC VALUE SOC       |     1.728 |                    |
| THM LIMIT CORE      |    95.000 | tctl-temp          |
| THM VALUE CORE      |    39.125 |                    |
| STT LIMIT APU       |    43.500 | apu-skin-temp      |
| STT VALUE APU       |    35.289 |                    |
| STT LIMIT dGPU      |     0.000 | dgpu-skin-temp     |
| STT VALUE dGPU      |     0.000 |                    |
| CCLK Boost SETPOINT |       nan | power-saving /     |
| CCLK BUSY VALUE     |       nan | max-performance    |

ryzenadj
On my FW16, from the
sudo ryzenadj --dump-table

Name REG
SPL 0x0000
SPPT 0x0010
fPPT 0x0008
p3T 0x0080
ao_sppt 0x0018

Note: The register names and REG index is done from observation/reverse-engineering and not from any documents.

Well I guess I gotta track down which register is the mystery throttling and then figure out what it is XD

I did dump msr tables for a different psus (45W, 65W and 100W) with P3T set to 90% adapter capacity and the main difference in the non moving ones are 0x0800 (duh that one is P3T) and 0x0040, 0x0048 and 0x0050 which are 100 for the 100W and 60 for the other two. Given that 45W and 60W throttle quite differently with the same value I don’t think it’s them.

My method was taking one dump idle and one running stress for each adapter then separating the non moving values by doing comm -12 on the idle and stressed file and then compare those to find changed settings. If anyone has a better idea how to isolate the relevant infos I have kept all the dumps.

Was looking at some of the other files referenced by cpupower.c.

I see the following console command to display CPU power limits in common_cpu_power.c.

DECLARE_CONSOLE_COMMAND(cpupower, cmd_cpupower,
			"cpupower spl fppt sppt p3t (unit mW)",
			"Set/Get the cpupower limit");

I also see the following console command to display CPU throttling status in throttle_ap.c.

DECLARE_CONSOLE_COMMAND(apthrottle, command_apthrottle, NULL,
			"Display the AP throttling state");

Are these commands accessable with ectool? Maybe they would give a hint as to whether the EC is trying to throttle the CPU with the 45W charger?

Not sure if they are available by ectool but they definitely are via the ccd, both were included when I said I did not found any obvious limits.

Also cpupower is kinda lying, it can only change spl, fppt and sppt, p3t is unshakeable XD. It also has a few more features not included in the help text like switching between the profiles in “update_os_power_slider” and some others I don’t quite understand (it can also crash the ec if it doesn’t like the params it gets XD).

I kinda gave up on this for now and just made sure the P3T is at least 89W. Not wo get onto more usefull ec changes like making making the power button less bright and more sensible keyboard backlight levels XD.

Those “DECLARE_CONSOLE_COMMAND…” are EC CCD commands. You need the EC CCD adapter to view them.
As you might have noticed, some of the commands I have been adding to my custom EC firmware has been making some of the EC CCD commands also work with ectool.
The “cpupower …” settings can all be set with “ryzenadj”, so probably not much need to make an ectool command from those.
The information/output from the apthrottle command is also available from the “ectool console” so probably don’t need to make that an ectool command either.

I think the most useful command that I surfaced as an ectool command has been the “ectool chargegetregs 0 filename.csv” command.
The output of that has let us all understand why it is “flipping”.

On the other thread:

Someone managed to get the CPU/dGPU on a FW16 to draw total peak of 369.70 Watts!!!
That was 202W from 240W PSU and the rest from the battery.

1 Like

Including p3t? I mean it doesn’t work with cpupower either but being able to set that at runtime would make testing a lot easier.

For how long?

Guys, I broke my own record

I think my laptop will need a second charger :laughing:

I mean technically if we could somehow get multiple of the charge port select fets to open at the same time… XD

Seriously though how long do those spikes last?

I don’t imagine it was pulling it for too long. The GPU spikes regularly to above the power limit while gaming. The floor for it was a bit elevated as well, because I had everything at max brightness/volume

I’ll try to get more data later for the CPU values

I wanted to document some of my observations on how CPU power limits are controlled by cpu_power.c for the benefit of anyone reviewing the code from scratch in the future.

It looks like the SPL, SPPT, FPPT, and P3T limits are set by the following for loop in the update_soc_power_limit function. I cleaned up the code below to make it more readable.

for (int item = 0 ; item < 4; item++) {
	/* use slider as default */
	target_func[item] = 0;
	for (int func = 0; func < 5; func++) {
		if (power_limit[func].mwatt[item] < 1)
			continue;
		if (power_limit[target_func[item]].mwatt[item]
			> power_limit[func].mwatt[item])
			target_func[item] = func;

The power limits set in the target_func array for each power limit type (SPL, SPPT, etc.) are the CPU power limits the system ends up using. Each “item” number represents a particular power limit type as defined in the power_limit_type enum from common_cpu_power.h.

Each “func” number represents a group of power limits. The group names are defined by the power_limit_function enum in common_cpu_power.h.

  • The FUNCTION_SLIDER group of CPU power limits are set by the update_os_power_slider function. This function runs whenever I change my Power Mode in Fedora, so I think it runs whenever a recognized Power Mode is in effect.
  • The FUNCTION_POWER group power limits are set by the update_adapter_power_limit function. I think this function runs every time a charger is plugged in.
  • The FUNCTION_SAFETY group power limits are set by the update_dc_safety_power_limit function. I think this function runs every time the system is on battery power only.
  • I could not find where the FUNCTION_THERMAL_PMF and FUNCTION_THERMAL limits are defined, outside of one instance where the SPPT is capped at 15W when a high CPU temperature event of some kind occurs.
  • If any of these groups does not apply to the current system state, their power limits are set to 0 and ignored.

The for loop iterates over each power group and selects the lowest CPU power limit per type amongst all active groups to use as the system CPU power limit for that type. Any CPU power limits that were set to 0 in the code are ignored in this comparison. The minimum value per power limit type is stored in the target_func[item]array.

For some reason, the line of code immediately after the for loop overwrites the target_func P3T value with the P3T value stored in the FUNCTION_POWER group. This means that the P3T limit is always set at 89W or higher no matter what state the system is in. For example, with a 100W charger plugged in on the FW13, the P3T power limit is always set at 176W no matter what Power Mode is used.

You can use ectool console to determine when the CPU power limits are changed by cpu_power.c. The active CPU setpoints can also be viewed in ryzenadj by looking at the values corresponding to the following addresses.

As an aside, I found out P3 stands for Peak Package Power per common_cpu_power.c, though I couldn’t find documentation on how changing this limit alters how the CPU runs.

Here is a video showing how the CPU power limits change on Fedora when the Power Mode is changed. Note that the SPL, SPPT, and FPPT values match the values defined in update_os_power_slider. Also note how P3T stays constant at 176W with a 100W charger plugged in and 89W when on battery.

This could have been added after discovering the mystery throttling at lower levels. Pretty annoying that you can’t really change it at runtime because of this which makes testing a bit of a pain cause you have to reflash for every level.

Well 100+89 is more than 176 and the calculated value is capped to 176.

I can also switch between the profiles using cpupower mode [profile index] over the ccd though that function should probably be avoided as it crashes the ec if you forget to provide the index.

Power limiting to absolutely never hit battery while using a 60W psu is probably a bit unreasonable because you also have to reserve power for the ssd which can also take quick peaks over 10W and the usb ports and the display and the speakers and pretty quickly you end up with nothing left.

Setting vsys to battery max when the charge limit has been reached seems to completely take care of the single digit mA charge discharge loops and reducing P3T to 89W seems to have significantly reduced the quick 100+mA discharge blips while not obviously kneecapping performance on 65W and 100W adapters.

I ran a few more tests with two different EC firmware versions and my 100W charger. One version used the stock P3T logic (P3T = 176W with a 100W charger). The other version used code revisions made by Adrian_Joachim that capped P3T at 90W.

I created a script to log select amdgpu_top and ryzenadj outputs every 0.1 seconds in a CSV file. I then ran the following test.

  • Begin recording a video and start data logging.
  • Wait ~10 seconds to get baseline power readings.
  • Open and close Firefox for ~10 seconds.
  • Leave the system idle while continuing recording for ~10 seconds.
  • Stop video recording and log for another ~10 seconds.

I plotted the STAPM, SPPT, and FPPT outputs for different power modes at both P3T power limits. I also logged CPU power usage from amdgpu_top, but I omitted showing it in the graphs since it tracked FPPT fairly closely. See below images for these outputs.

With the reduction in P3T from 176W to 90W, I see about a 10W reduction in peak FPPT and peak SPPT in the balanced and performance power modes. I suspect the P3T parameter acts as a low-pass power filter of sorts by placing an upper limit on how high instantaneous CPU power is allowed to get.

The code changes by Adrian_Joachim also increased VSysMax to the battery desired voltage when the charge status is IDLE. From what I can tell, the combination of the higher VSysMax and lower P3T eliminates battery flipping under my test conditions.

At the minimum, I think reducing P3T to 90W on the FW13 is a low risk way to decrease CPU power draw without a noticeable performance hit in normal use. This reduced power draw should in turn lessen how often the system has to dip into battery, at least with a 100W charger. There’s probably some optimal P3T setpoint that balances CPU peak performance with power usage in a gaming context, but that’s not how I plan to use the laptop.

I am less certain about whether increasing VSysMax to the battery desired voltage is the right long-term solution, at least without further testing.

I still get the impression that my idle CPU temps are higher with the VSysMax increase compared to what they would be otherwise. Further data logging would prove if that is actually the case. On the other hand, the change definitely has the desired effect of preventing dipping into battery unless the available charger supply current is exceeded.






2 Likes

I guess the next thing to do is modify amdgpu_top to output csv file.
Is anyone doing that? That would give the cpu/gpu temps.
The using vsysmax = battery desired is not actually raising vsys by much if you look at it.
It is vsys that is the actual voltage output, not vsysmax.

Running amdgpu_top -gm outputs the same temperature data shown in the GUI program. So it should just be a matter of filtering that output in a Bash script on a loop to print the data you want and redirecting the output to a csv file.

Am posting the short Bash script I wrote to log CPU data into a CSV file. The graphs in my last post were made using data generated by this script. It requires amdgpu_top and ryzenadj to work properly.

I just added logging for Edge and CPU TCtrl temperatures from amdgpu_top. Feel free to modify and add whatever other data logging you want.

#!/bin/bash
OUTPUT_FILE="output.csv"
printf "Logging data to %s.  Press Ctrl+C to stop.\n" "$OUTPUT_FILE"
get_timestamp() {
  date +%Y-%m-%d_%H:%M:%S
}

printf "Datetime,Edge T,CPU T,Power AVG,Power In," > "$OUTPUT_FILE"
printf "STAPM,FPPT,SPPT\n" >> "$OUTPUT_FILE"

while true; do
    printf "$(get_timestamp),"
    amdgpu_top -d |
    awk '/(Edge T|CPU T)/ || /^Power/ {
        sub(/Input[ ]+/, "Input", $0)
        printf("%s,", $4)
    }'
    ryzenadj -i |
    awk '/(STAPM|PPT) VALUE/ && !/APU/ {
        printf("%s%s", (NF == 7) ? $5 : $6, ($0 ~ /SLOW/) ? "\n" : ",")
    }'
    sleep 0.1
done >> "$OUTPUT_FILE"

For amdgpu_top, the json. -j option is probably good enough to capture the temps over time.

Having it flapping around like the current stock implementation does certainly isn’t either.

That would be quite interesting. It is possible the amd boosting algo somehow takes that into account and higher vsys could make the vrms slightly less efficient but we are talking very slightly here, especially at idle when currents are low and the voltage difference is not that big.

Theoretically I could try just a bigger offset on the stock implementation that may still prevent it from dipping into battery and would give you a slightly lower but constantly moving vsys.

Most importantly the idle behavior on battery is unchanged and there idle power matters most.