Hi All :)

This is a bug report, not a patch submission. Claude Code was used to
assist with the but triage and fix. I did patch a local build to
confirm the diagnosis and I describe it below, but I have only this one
board
and no view of how a real fix should be shaped, so I am not proposing one.

HARDWARE
NVIDIA GK107GLM [Quadro K1000M], 10de:0ffc rev a1, subsystem 103c:176b
(HP mobile workstation). Single GPU, no iGPU.
Kernel 7.2.5 (Arch), in-tree nouveau. Mesa 26.2.2.

SYMPTOM
Every reclock fails with -EINVAL; the card is pinned at core 405 MHz /
memory 810 MHz for the life of the boot. Each pstate asks for a different
voltage and gets the identical error:

nouveau 0000:01:00.0: volt: couldn't set 837500uv
nouveau 0000:01:00.0: clk: failed to raise voltage: -22
nouveau 0000:01:00.0: clk: error setting pstate 0: -22

nouveau 0000:01:00.0: volt: couldn't set 887500uv
nouveau 0000:01:00.0: clk: failed to raise voltage: -22
nouveau 0000:01:00.0: clk: error setting pstate 1: -22

Earlier in the boot log:

nouveau 0000:01:00.0: volt: Type mismatch between the voltage table type
and the GPIO table. Fallback to GPIO mode.

TO REPRODUCE
1. Boot the affected card on nouveau.
2. Confirm no VID table was built:
ls /sys/class/drm/card0/device/hwmon/hwmon*/in0_min
These files are created only when vid_nr > 0. Their absence is the
signature of this bug.
3. Force a real pstate transition (re-requesting the current state is
silent and looks like success):
sudo sh -c 'echo 0f > /sys/kernel/debug/dri/0000:01:00.0/pstate'
4. Read dmesg for the lines above.

Do not judge by the pstate file: it prints '*' against the target even
when the apply failed. dmesg is the only reliable indicator. Also note
"volt: VID" lines are nvkm_debug and invisible at the default loglevel.

ROOT CAUSE
The vBIOS voltage table (BIT 'P' +0x0c -> 0x85e6 here) is version 0x50 with
its flag byte at +0x04 set to 0x01, so nvbios_volt_parse() classifies it as
NVBIOS_VOLT_PWM and reads pwm_freq/pwm_range, leaving vidmask, step and
ranged at zero.

The board has no DCB_GPIO_VID_PWM (tag 0x81) entry at all. It drives the
regulator through three VID GPIO lines (VID_0 line 11, VID_1 line 5,
VID_2 line 6). gk104_volt_new() notices and falls back to GPIO mode - that
is the "Type mismatch" message - but the table has already been parsed as
PWM. This does two separate pieces of damage:

1. In nvkm_volt_parse_bios(), both VID-building branches are gated on
info.vidmask, which is 0, so neither runs and vid_nr stays 0.
nvkm_volt_set() can then satisfy no request at all - hence the identical
-22 whichever voltage is asked for.

2. The PWM branch also sets volt->max_uv = info.base + info.pwm_range,
which here is 306250 + 6250 = 312500 uV. nvkm_cstate_new() rejects any
cstep whose nvkm_volt_map_min() exceeds max_uv, and every cstep on this
card needs more than 0.3125 V, so all of them are dropped and
pstate->list is left empty. nvkm_cstate_prog() then falls back to
&pstate->base. That is why the two requests above are exactly the PERF
table's base voltages (pstate 07 -> vmap id 49 -> 837500 uV;
pstate 0f -> vmap id 56 -> 887500 uV) rather than any cstep's.

The data needed is present in the ROM but never read. The 8 entries start
at table+hdr = 0x8601:

00 43 00 80 / 01 44 80 80 / 02 45 00 81 / 03 46 80 81 /
04 47 00 82 / 05 48 80 82 / 06 49 00 83 / 07 4a 80 83

i.e. { vid:u8, voltage:u8 in 12.5 mV units, unk:u16 }, decoding to
837500..925000 uV for vid 0..7 - a monotonic 12.5 mV ladder, and exactly
the voltages the clock code asks for. nvbios_volt_entry_parse() has
"case 0x50: break;", a deliberate no-op, so those rows are skipped.
envytools' nvbios does not decode them either; it prints all four bytes of
each entry as a single "unk" u32.

Also ruled out: this is not missing headroom. BOOST v11 gives pstate f a
max of 1701 MHz shader (~850 MHz core, the card's rated clock) and CSTEP
v10 a 33-step 540..1690 MHz ladder. And there is no voltage-related
nouveau.config option in this build to work around it.

WHAT I CHANGED LOCALLY (to confirm the diagnosis, not as a proposed fix)
Three files:
- bios/volt.h: carry the GPIO-mode fields alongside a PWM-flagged table.
- bios/volt.c: populate them when parsing a v0x50 PWM table, and implement
case 0x50 in nvbios_volt_entry_parse() with the layout above, trusting
the decode only when the voltage lands inside the table's own min/max.
- volt/base.c: in nvkm_volt_parse_bios(), when the table is PWM-flagged
and nvkm_gpio_find() finds no DCB_GPIO_VID_PWM, re-interpret it as GPIO
before building the VID list.

The GPIO re-interpretation is a guess about intent and the min/max guard is
a heuristic. I can send the diff if it is useful.

RESULTS
An 8-entry VID table (837500..925000 uV) is built and reclocking works.
pstate 0f applies core 645 MHz / memory 1800 MHz at 925 mV.

glmark2 2023.01, Wayland, 800x600 windowed, same session and binary
throughout. pstate 07 is the clock state stock nouveau is stuck in, so this
measures what the bug costs:

pstate 07 (405/810 MHz, 862 mV) Score 944 peak 63 C
pstate 0f (645/1800 MHz, 925 mV) Score 1993 peak 73 C (+111%)

glmark2-es2 at pstate 0f scores 2061. Memory-bound tests gain most:
terrain 43 -> 86 FPS, refract 103 -> 243, desktop-blur 220 -> 492.

Clocks held pstate 0f for 100% of the run, peak 73 C against a 95 C limit.
Transitions 0f -> 07 -> 0f were clean, no GPU hang, compositor survived,
and no new nouveau kernel messages during any run.

Core tops out at 645 MHz rather than the 850 MHz the boost ladder implies,
and this is not a second bug: the ceiling is the top VID, 925000 uV.
nvkm_cstate_find_best() walks down from cstep 33 and stops at cstep 15
(1290 MHz shader = 645 MHz core), whose vmap id 34 evaluates to
887500 + 37500 = exactly 925000 uV. The next cstep up needs more than this
3-bit VID GPIO regulator has wired.

QUESTIONS
- How widespread is this? The combination is "v0x50 voltage table flagged
PWM + no DCB_GPIO_VID_PWM + VID GPIOs wired" - plausibly other Kepler
mobile parts, but I have only the one board.
- What is the right fix: re-interpreting the table as GPIO when the VID_PWM
GPIO is absent, implementing the entry-parse path alone, or catching the
contradiction somewhere earlier?
- Is the { vid, voltage*12.5mV, unk } entry layout documented anywhere?
- Would a warning when vid_nr ends at 0 with a voltage table present be
worth having? Today the only symptom is -EINVAL from every pstate, which
reads like a clock problem rather than a parsing one.

I can supply the vBIOS dump (93696 bytes) and full dmesg on request.


Regards,
Shane Ayerst

Reply via email to