العربية
📅 October 27, 2026 · ⏱ 9 min read

My laptop felt slow: the CPU was stuck at 800 MHz

▎ Five minutes of measurement turned “my laptop is slow” into a firmware bug pinning a 4.5 GHz processor to its slowest state.

Linux Hardware Performance Debugging

My laptop had become painful to use. Chrome was sluggish, switching tabs stuttered, and even opening a terminal felt heavy. So I did what I should have done on day one: I stopped guessing and measured. The answer had nothing to do with RAM, disk or Chrome.

800MHzactual clock under full load
4,500MHzwhat the CPU is rated for
5×faster after the fix
52°Ctemperature while it “throttled”

The symptom

The usual suspects were easy to check, and all of them were innocent:

Nothing was obviously broken. The one number I had not looked at was the one that explains everything: how fast the CPU is actually running.

Step 1: read the current clock

Two commands are enough on any Linux machine:

current clock
grep MHz /proc/cpuinfo
lscpu | grep -E 'Model name|MHz'

Mine answered 800 MHz, on all twelve threads. The processor is an Intel Core i7-9750H: a 2.6 GHz part that boosts to 4.5 GHz. It was running at about one third of its base clock.

An idle CPU sitting at 800 MHz is completely normal, that is power saving. The fault only becomes visible once you put it under load.

Step 2: find the ceiling the CPU is allowed to reach

Before blaming the hardware, rule out the operating system. Linux exposes both the hardware limits and the limits it is currently enforcing:

what the hardware allows
# what the CPU says it can do (kHz)
cat /sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq
# what the OS currently allows (kHz)
cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq
# Intel P-state limits, as a percentage of the maximum
cat /sys/devices/system/cpu/intel_pstate/{min_perf_pct,max_perf_pct,no_turbo}

Every one of those answered go ahead: a 4,500,000 kHz ceiling, max_perf_pct=100, turbo not disabled. On paper the CPU was free to run at full speed.

LayerReadingVerdict
CPU rated maximum4,500 MHzhardware is capable
OS frequency ceiling4,500 MHznot restricted
Actual clock, full load800 MHz🔴 the anomaly

Step 3: prove it under load

Put every core to work for a few seconds and read the clock again. This is the test that separates a power-saving illusion from a real fault:

saturate all cores, then read the clock
for i in $(seq 1 $(nproc)); do yes > /dev/null & done
sleep 5
grep MHz /proc/cpuinfo
pkill yes

Mine stayed at 800 MHz with 100% CPU utilisation and a load average of 11, while the package temperature was only 52 °C.

That combination is the whole diagnosis: maximum load, minimum clock, low temperature. A genuinely hot CPU throttles because it is approaching 100 °C. This one was cool and still refused to run. Something outside the scheduler was holding it down.

Clock speed over 60 s of full load, before vs. after
0150030004500015304560seconds under load
Before: pinned by PROCHOTAfter: free to boost

Same machine, same stress test. Before, the clock never moved. After, it tracks the load and pulls back only when it gets hot.

Step 4: ask the CPU why

Intel processors publish the reason they are being limited in a model-specific register (MSR). Install msr-tools and read them:

read the limiting reason straight from the CPU
sudo apt install msr-tools        # rdmsr / wrmsr
sudo modprobe msr

sudo rdmsr -p 0 0x64F   # PERF_LIMIT_REASONS: why am I being limited?
sudo rdmsr -p 0 0x1FC   # POWER_CTL:          do I listen to PROCHOT?
sudo rdmsr -p 0 0x198   # PERF_STATUS:        what ratio am I running at?
sudo rdmsr -p 0 0x199   # PERF_CTL:           what ratio was requested?

Mine answered:

RegisterValueMeaning
0x64F, PERF_LIMIT_REASONS0x00010001bit 0 set, PROCHOT is the active limiter
0x1FC, POWER_CTL0x002c005dbit 0 = 1, listening to the external PROCHOT pin
0x198, PERF_STATUS0x…0800current ratio 8, so 800 MHz
0x199, PERF_CTL0x…2d00requested ratio 45, so 4.5 GHz

Requested 4.5 GHz, delivered 0.8 GHz, and the CPU named the reason itself: PROCHOT.

What PROCHOT actually is

PROCHOT# (Processor Hot) is a physical pin on the CPU package. When it is pulled low, the processor drops to its minimum P-state immediately, with no negotiation and no involvement from the operating system. Intel also implements Bi-Directional PROCHOT (BD PROCHOT): the CPU listens to that pin not only for its own temperature, but for signals from other components, such as voltage regulators, the embedded controller and the battery.

In principle that is a good safety feature: if power delivery cannot sustain the CPU, something must slow it down. It becomes a bug when the signal is held down while nothing is hot.

How an external pin overrides the operating system
Embedded controller / VRMpulls the pinCPUforced to minimum800 MHzuntil the pin is releasedPROCHOT#

The OS asks for 4.5 GHz. The pin wins.

Step 5: rule out the cheap causes

Before reaching for a workaround, work out why the signal is being pulled. In rough order of likelihood:

  1. The power adapter or the batteryOn many laptops a failing battery or an underpowered adapter makes the embedded controller assert PROCHOT to protect the voltage regulators. Test it: repeat the load test with the charger unplugged.
  2. Real thermalsRun sensors. The package should sit well under its critical temperature. If it reads 95 to 100 °C, the CPU is genuinely hot, so clean the fans and repaste instead.
  3. Power profile or governorCheck powerprofilesctl or cpupower frequency-info. A powersave governor with a low max_perf_pct can look similar, but it will respond to load.
  4. A latched embedded controllerA full power drain resets it: shut down, unplug the charger, hold the power button for 30 to 60 seconds, plug back in and boot.
  5. FirmwareAn outdated BIOS/EC is a very common source of spurious PROCHOT on gaming laptops. Check your vendor support page for a BIOS newer than the one you run.

In my case the pin stayed asserted on both AC and battery, the battery reported 97% health with 83 charge cycles, and the BIOS was seven years old. That pointed at the embedded controller and firmware rather than a dying cell.

Step 6: the workaround that gave me the speed back

Once the real faults are ruled out and the signal is still firing, you can tell the CPU to stop listening to BD PROCHOT by clearing bit 0 of MSR 0x1FC. Every core keeps its own copy, so write all of them:

disable BD PROCHOT listening
sudo rdmsr -a 0x1FC              # read, usually ends in 5d or 5b
sudo wrmsr -a 0x1FC 0x2c005c    # clear bit 0, ...5d becomes ...5c

The change takes effect immediately but does not survive a reboot, so make it persistent with a small systemd unit:

/usr/local/bin/fix-bd-prochot.sh
sudo tee /usr/local/bin/fix-bd-prochot.sh >/dev/null <<'EOF'
#!/bin/bash
modprobe msr 2>/dev/null || true
for i in $(seq 0 $(( $(nproc) - 1 ))); do
  cur=$(rdmsr -p "$i" 0x1FC 2>/dev/null) || continue
  if (( 0x$cur & 1 )); then
    wrmsr -p "$i" 0x1FC "$(printf '0x%x' $(( 0x$cur & ~1 )))"
  fi
done
EOF
sudo chmod 755 /usr/local/bin/fix-bd-prochot.sh
/etc/systemd/system/bd-prochot-fix.service
[Unit]
Description=Disable BD PROCHOT (workaround: CPU pinned at 800MHz)
After=multi-user.target

[Service]
Type=oneshot
ExecStart=/usr/local/bin/fix-bd-prochot.sh
RemainAfterExit=yes

[Install]
WantedBy=multi-user.target
enable it
sudo systemctl daemon-reload
sudo systemctl enable --now bd-prochot-fix.service

The result

Immediately after clearing the bit, the same full-load test reported 3.1 GHz across all twelve threads at 94 °C, and normal desktop work now boosts to around 4 GHz. Chrome stopped stuttering. Idle still drops back to 800 MHz, so power saving is intact; only the artificial ceiling is gone.

Clock speed compared: what the CPU did vs. what it is rated for
0150030004500MHzLocked (before)800Base clock2600After fix (full load)3100Max turbo4500

If you are on Windows

TL;DR checklist

  1. Read the current clock: grep MHz /proc/cpuinfo.
  2. Stress every core for five seconds and read it again. Low at idle is normal; low under load is not.
  3. Check the ceiling the OS allows: cpuinfo_max_freq, scaling_max_freq, max_perf_pct.
  4. If the OS allows full speed but the clock will not move, read rdmsr 0x64F. The CPU will name the reason.
  5. If the reason is PROCHOT while temperatures are low: test the adapter and battery, reset the embedded controller, then update the BIOS.
  6. Only then consider clearing BD PROCHOT (bit 0 of 0x1FC) as a workaround.
back to blog →