Correct the PTP page: no PTP on a 4B, but the CM4 has it

Reader sent Geerling's CM4 PTP write-up, which shows two CM4s syncing to 10-15ns.
Our headline overclaimed ('impossible on a Pi 4') and our mechanism was wrong (we
blamed the bcmgenet MAC for not exposing a PHC).

The real story is better: the CM4 (BCM54210PE) and the 4B (BCM54213PE) report the
IDENTICAL PHY ID 0x600d84a2, so the kernel cannot tell them apart by asking the
chip. bcm-phy-ptp.c disambiguates by MDIO bus address instead — addr 0 = CM4,
proceed; addr 1 = 4B, return NULL. Ours is addr 1.

It is not a kernel config gap and not our RT patch: CONFIG_BCM_NET_PHYPTP=y and
CONFIG_NETWORK_PHY_TIMESTAMPING=y are both set. The driver loads, looks at the
board, and declines.

Correction is visible on the page rather than quietly patched. Credit to Geerling.
This commit is contained in:
Ryan Malloy 2026-07-14 14:50:27 -06:00
parent 61d9c1d0b2
commit 1ed8f720d6
4 changed files with 131 additions and 34 deletions

View File

@ -40,7 +40,7 @@ export default defineConfig({
description:
"What the Raspberry Pi time-server guides get wrong, and the numbers to prove it. " +
"GPS Stratum 1 on a Pi 4: PREEMPT_RT makes PPS jitter worse, the PPS interrupt " +
"cannot be pinned, PTP is impossible, and your dashboard is taxing your clock.",
"cannot be pinned, PTP needs a CM4 not a 4B, and your dashboard is taxing your clock.",
// The mark IS the word "cuckoo" — a rebus. replacesTitle stops Starlight
// rendering the title text beside it (which would read "…escapement The

View File

@ -1,14 +1,33 @@
---
title: Why PTP is off the table on a Pi 4
description: PTP's entire value is hardware timestamping. The Pi 4's NIC has no PTP hardware clock. Software PTP is a worse NTP.
title: No PTP on a Pi 4 Model B (but yes on a CM4)
description: The CM4 does hardware PTP. The Pi 4 Model B does not — and both report the identical PHY ID, so the kernel tells them apart by MDIO address and declines ours by name.
sidebar:
order: 3
---
The reference builds all reach for **PTP** (IEEE 1588), and they're right to: on
the right hardware it's dramatically better than NTP.
:::caution[Corrected — 2026-07-14]
This page used to be called *"Why PTP is off the table on a Pi 4"* and blamed the
`bcmgenet` MAC driver for not exposing a PHC. **The headline overclaimed and the
mechanism was wrong.** A reader sent us
[Jeff Geerling's CM4 PTP write-up](https://www.jeffgeerling.com/blog/2022/ptp-and-ieee-1588-hardware-timestamping-on-raspberry-pi-cm4/),
which shows the **Compute Module 4 doing hardware PTP** — two of them synced to
within 1015 nanoseconds.
The Pi 4 is not the right hardware. One command settles it:
Our measurement was real, but it is specific to the **Pi 4 Model B**, and the
actual reason is far better than "unsupported." Corrected version below. This is
the site working as intended.
:::
PTP's entire value is **hardware timestamping**: the network silicon stamps the
packet as it crosses the wire, outside the operating system. That's what takes
scheduling, driver latency and queueing out of the measurement, and it's why PTP
reaches nanoseconds where NTP reaches microseconds.
With no PHC (PTP Hardware Clock), PTP is just a protocol — packets stamped by the
*kernel*, on the *CPU*, subject to precisely the jitter you were trying to escape.
A more complicated NTP with worse tooling.
On our board:
```console
$ ethtool -T eth0
@ -17,36 +36,106 @@ Capabilities:
software-receive
software-system-clock
PTP Hardware Clock: none
Hardware Transmit Timestamp Modes: none
Hardware Receive Filter Modes: none
$ ls /dev/ptp*
ls: cannot access '/dev/ptp*': No such file or directory
```
**`PTP Hardware Clock: none`.** There isn't one. There's no `/dev/ptp0` to open.
On a CM4 that same command reports `PTP Hardware Clock: 0`, and
`/sys/class/ptp/ptp0/clock_name` says `bcm_phy_ptp`. Same family, same era,
different answer.
## Why that's fatal rather than inconvenient
## Two different chips — that report the same ID
PTP's whole advantage is **hardware timestamping**: the network card itself
stamps the packet as it crosses the wire, in silicon, outside the operating
system. That's what removes kernel scheduling, driver latency, and queueing from
the measurement, and it's why PTP reaches nanoseconds where NTP reaches
microseconds.
| board | PHY | hardware PTP |
|---|---|---|
| Compute Module 4 | BCM54210PE | **yes** |
| Compute Module 5 | — | **yes**, out of the box |
| Pi 4 Model B | BCM54213PE | no |
Take the hardware clock away and PTP is just... a protocol. Software-timestamped
PTP has the packets stamped by the *kernel*, on the *CPU*, subject to exactly the
scheduling jitter you were trying to escape. It is a more complicated NTP with
worse tooling.
Here's the part that makes this worth a page. **Both PHYs report an identical ID:**
## But the guides say the Pi 4's PHY supports PTP
```console
$ cat /sys/class/mdio_bus/unimac-mdio--19/unimac-mdio--19:01/phy_id
0x600d84a2
```
They do, and the *chip* does — the BCM54213PE PHY has PTP capability on paper.
It doesn't matter. The Pi 4's `bcmgenet` MAC driver doesn't expose a PHC, so
Linux has nothing to give you. And the reference builds that make PTP work feed
the PPS into the NIC through a **SYNC pin that only the CM4/CM5 break out** — a
regular Pi 4 board doesn't route it anywhere you can reach.
That value is `PHY_ID_BCM54213PE` in `include/linux/brcmphy.h` — and a CM4
reports it too. The silicon genuinely differs; the identifier does not. So the
kernel *cannot* tell these boards apart by asking the chip who it is.
## So don't chase it
## So it tells them apart by where they're plugged in
We spent real time on this before running `ethtool -T`, which we should have run
first. If your board reports `PTP Hardware Clock: none`, close the tab. Put the
effort into the PPS path instead — that's where the nanoseconds actually are, and
From the Raspberry Pi kernel, `drivers/net/phy/bcm-phy-ptp.c`, comments and all:
```c
struct bcm_ptp_private *bcm_ptp_probe(struct phy_device *phydev)
{
switch (BRCM_PHY_MODEL(phydev)) {
case PHY_ID_BCM54210E:
break;
#ifdef PHY_ID_BCM54213PE
case PHY_ID_BCM54213PE:
switch (phydev->mdio.addr) {
case 0: // CM4 - this is a BCM54210PE which supports PTP
break;
case 1: // 4B - this is a BCM54213PE which doesn't
return NULL;
default: // Unknown - assume it's BCM54210PE
break;
}
break;
```
The disambiguator is the **MDIO bus address**. A CM4's PHY sits at address 0; a
Pi 4 Model B's sits at address 1. Ours:
```console
$ ls -d /sys/class/mdio_bus/unimac-mdio--19/unimac-mdio--19:*
/sys/class/mdio_bus/unimac-mdio--19/unimac-mdio--19:01
^^ address 1 — the 4B branch
```
Address 1 → `return NULL` → no PHC is ever registered. That is the whole story.
## It is not your kernel, and there is no option to flip
This is the part worth internalising, because it's a trap that looks exactly like
a misconfiguration. All the support is *present*:
```console
$ grep -E "CONFIG_NETWORK_PHY_TIMESTAMPING|CONFIG_BCM_NET_PHYPTP" .config
CONFIG_NETWORK_PHY_TIMESTAMPING=y
CONFIG_BCM_NET_PHYPTP=y
```
The driver is compiled in. The PHY ID is a recognised constant. The code path
exists, and it runs. **It runs, looks at your board, and declines.** No kernel
option changes that, no patch, no `rpi-update` — the refusal is a deliberate and
correct statement about the silicon in front of it.
It also means our [`IRQF_NO_THREAD` kernel](/reference/the-patch/) is not the
culprit. That was the obvious suspicion when we re-opened this, so we checked it
first. It isn't.
## What to actually do
**On a Pi 4 Model B:** stop. Spend the effort on the PPS path, which is where the
nanoseconds actually live — and
[it needs the help](/explanation/preempt-rt-made-it-worse/).
**If you want PTP on a Pi:** buy a **CM4 or CM5**, not a 4B. You get `/dev/ptp0`,
`ethtool -T` reports hardware transmit and receive timestamping, and on the CM4 IO
board you can drive a PPS out of pin 9. Geerling's post is the guide; we're not
going to rewrite it.
**Either way, run `ethtool -T` first.** We didn't, and burned real time on a
question that one command answers.
:::note[Credit]
The correction, and every CM4 figure here, comes from
[Jeff Geerling's write-up](https://www.jeffgeerling.com/blog/2022/ptp-and-ieee-1588-hardware-timestamping-on-raspberry-pi-cm4/)
and the kernel patches it links to. We had the right conclusion for the wrong
reason, and stated it too broadly. Checking beats believing — including when the
thing being checked is us.
:::

View File

@ -12,11 +12,18 @@ A GPS-disciplined Stratum 1 NTP server: **Raspberry Pi 4** + **BerryGPS-IMU v4**
199 ns**, root delay ~1 ns, survives a cold power cut unattended. About $130 of
parts, replacing an appliance that costs $1,500$10,000.
## What the guides get wrong on a Pi 4
## What the guides get wrong on a Pi 4 Model B
:::note[Model B, specifically]
These are properties of the **Pi 4 Model B**, not the whole Pi 4 family. The
**CM4 and CM5 do hardware PTP** — [we got that one wrong at first, and corrected
it](/explanation/no-ptp-on-a-pi-4/). If a claim here matters to you, check it on
*your* board. That's the entire point of this site.
:::
| Claim | Reality |
|---|---|
| "Use PTP for real precision" | **Impossible.** `ethtool -T eth0``PTP Hardware Clock: none`. No hardware timestamping exists on this NIC. |
| "Use PTP for real precision" | **Not on a 4B — but yes on a CM4.** Both report the *same* PHY ID, so the kernel disambiguates by MDIO address and hands the 4B a `return NULL`. `ethtool -T``PTP Hardware Clock: none`. |
| "Isolate the PPS IRQ on a dedicated core" | **Not permitted.** GPIO IRQs demux through `pinctrl-bcm2835` and reject `smp_affinity`. |
| "Install PREEMPT_RT" | **Made jitter 3× worse** until patched — it threads the handler that takes the timestamp. |
| "Raise the GPS baud rate" | **Irrelevant.** PPS offset measured 1 ns at 9600 vs 115200. Identical. NMEA only *labels* the second. |

View File

@ -76,9 +76,10 @@ board rather than take our word for it. That's the whole point.
costs you the accuracy.
[→ Why](/explanation/the-interrupt-you-cannot-move/)
</Card>
<Card title="PTP is impossible on a Pi 4" icon="error">
`ethtool -T eth0` → `PTP Hardware Clock: none`. There is no hardware
timestamping. Software PTP is just a worse NTP. Don't chase it.
<Card title="No PTP on a 4B — but yes on a CM4" icon="error">
The CM4 and the 4B report the **same PHY ID**, so the kernel tells them apart
by MDIO address and hands the 4B a `return NULL`. The driver is compiled in;
it looks at your board and declines. Buy a CM4 if you want PTP.
[→ Why](/explanation/no-ptp-on-a-pi-4/)
</Card>
<Card title="Your dashboard is taxing your clock" icon="rocket">