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:
parent
61d9c1d0b2
commit
1ed8f720d6
@ -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
|
||||
|
||||
@ -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 10–15 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.
|
||||
:::
|
||||
|
||||
@ -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. |
|
||||
|
||||
@ -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">
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user