A few years ago, one of our customers came to us with what sounded like a reasonably straightforward request: adding support for the Precision Time Protocol to an existing Linux kernel Ethernet PHY driver, namely the Marvell PHY driver.
Not only this sounded reasonably easy, but someone had actually already done the work: Russell King, a famous Linux kernel developer, maintainer of the ARM 32-bit support and prolific contributor to the networking subsystem, had already sent to the netdev mailing list a patch series implementing exactly this. With this existing series, we initially expected that a few review rounds and some testing would be enough to get it merged. Task done, move on.
It turned out to be much more complicated.
We started this work in March 2023. Three years later, we only have posted a second revision of the Marvell PHY PTP support, and that happened only after a significant change to the PTP core API. Read on for a deep dive into this real-life story of Linux kernel development and contribution.
A quick primer on PTP
The Precision Time Protocol (PTP), defined by the IEEE 1588 standard, is a network protocol designed to synchronize clocks across distributed systems with higher precision than NTP, achieved through more frequent synchronization and finer-grained timestamp exchange. PTP is designed from the ground up to be offloadable to hardware: packets can be timestamped as close to the wire as possible — ideally inside the PHY or MAC. This matters because software timestamping is subject to OS scheduling jitter, which introduces latency variability that degrades clock accuracy; by pushing timestamping into hardware, that variability is largely eliminated, enabling sub-microsecond precision.
If you’d like a deeper introduction to timestamping and PTP in Linux, we recommend this Bootlin conference talk from ELCE 2020 (video), which covers the fundamentals in detail.
The discovery of the problems
The first cracks appeared during the discussion following Russell King’s initial Marvell PHY PTP patch. What had seemed like a clean implementation quickly revealed two thorny issues hiding beneath the surface.
Problem 1: PTP capabilities and configuration mismatch
To understand this issue, a little background helps. In a typical network interface, two hardware components are involved in sending and receiving data: the MAC (Media Access Controller), which handles the logical side of Ethernet communication, and the PHY (Physical Layer transceiver), which deals with the actual electrical signals on the wire. Both can potentially support hardware timestamping for PTP.
Linux exposes two IOCTLs, essentially special system calls, (SIOCGHWTSTAMP and SIOCSHWTSTAMP) that userspace applications use to read and change the active hardware timestamping configuration. When one of these calls is made, it reaches the MAC driver first, through a hook called ndo_eth_ioctl. The MAC driver can either handle it or forward it down to the PHY driver.
The problem is that a separate code path, __ethtool_get_ts_info, is responsible for reporting what timestamping capabilities the interface has, and it always looks at the PHY first. This means the system could end up in an inconsistent state: the MAC is actively handling timestamps, but the kernel is advertising the PHY’s capabilities to userspace, or vice versa. Two parts of the kernel were giving conflicting answers to the same question.
Problem 2: Risk of timestamping regression on MACCHIATObin
The second issue was equally tricky. On the MACCHIATObin board, PTP can be provided either by the Marvell PHY or by the MAC, which already has mainline support and importantly, the MAC delivers more precise PTP. But at the time, Linux defaulted to the PHY as the PTP source, since it sits closer to the link. Adding PTP support to the Marvell PHY would silently make the PHY the default, effectively replacing a more precise source with a less precise one. A quiet, hard-to-diagnose regression.
The solution accepted upstream
Solving these problems required fixing something deeper: Linux needed a proper mechanism for selecting between MAC and PHY timestamping sources.
As it turned out, this problem had already been identified. Back in 2022, Richard Cochran had submitted a patch series to add exactly this kind of selection support. His work built on prior efforts by Maxim Georgiev and Vladimir Oltean to introduce ndo_hwtstamp_get/set.
We reviewed that patch series, picked up where Richard had left off, and pushed it forward. What followed was a long, humbling process. Each version brought new reviewer feedback, new edge cases to handle, and sometimes fundamental design questions that sent us back to the drawing board. The patch series went through revision after revision, 21 in total, spanning months of back-and-forth on the mailing list, rethinking core design decisions, and carefully addressing every concern raised by maintainers. There were moments where it was unclear whether the work would ever reach a state everyone could agree on. But persistence paid off: we finally landed the support in Linux mainline. ethtool support followed a few months later.
This work deprecates the old SIOCGHWTSTAMP/SIOCSHWTSTAMP IOCTLs in favor of the new ETHTOOL_MSG_TSCONFIG_SET/GET netlink interface. Netlink is a modern socket-based communication mechanism between the Linux kernel and userspace programs. Unlike IOCTLs, which are essentially numbered commands crammed into a single system call with little room for structure or extensibility, Netlink messages are typed, versioned, and designed to carry structured data cleanly. This makes them far easier to extend over time without breaking existing tools, which is exactly what the kernel networking subsystem needs as features like PTP grow more complex.
With this work, you can now select which hardware timestamp source you want in your network topology, meaning you can choose either the PTP from the MAC (or an integrated MAC/PHY device) or from the external PHY.
The new netlink interfaces, implemented in netlink since version 6.14, allow to query and manipulate the PTP sources.
Usage
A good starting point is to query the capabilities of the available interfaces:
# ethtool -T "*" Time stamping parameters for lo: Capabilities: software-transmit software-receive software-system-clock PTP Hardware Clock: none Hardware Transmit Timestamp Modes: none Hardware Receive Filter Modes: none Time stamping parameters for eth0: Capabilities: hardware-transmit software-transmit hardware-receive software-receive software-system-clock hardware-raw-clock Hardware timestamp provider index: 1 Hardware timestamp provider qualifier: Precise (IEEE 1588 quality) Hardware timestamp source: MAC Hardware Transmit Timestamp Modes: off on onestep-sync Hardware Receive Filter Modes: none all Time stamping parameters for eth0: Capabilities: hardware-transmit hardware-receive software-receive software-system-clock hardware-raw-clock Hardware timestamp provider index: 0 Hardware timestamp provider qualifier: Precise (IEEE 1588 quality) Hardware timestamp source: PHY Hardware timestamp source PHY index: 1 Hardware Transmit Timestamp Modes: off on Hardware Receive Filter Modes: none some #
In this output, the eth0 Ethernet port reports two available hardware timestamp sources: the MAC at index 1 and the PHY at index 0. Picking the PHY as the active PTP source is now a one-liner:
# ethtool --set-hwtimestamp-cfg eth0 index 0 qualifier precise
You can also retrieve the current hardware timestamp configuration with the following command:
# ethtool --get-hwtimestamp-cfg eth0 Time stamping configuration for eth0: Hardware timestamp provider index: 1 Hardware timestamp provider qualifier: Precise (IEEE 1588 quality) Hardware Transmit Timestamp Mode: off Hardware Receive Filter Mode: none Hardware Flags: none
This feature is now supported for nearly all MAC drivers in the kernel. That breadth didn’t come for free: it required systematically converting a large number of Network Interface Controller (NIC) drivers to expose the hwtstamp NDOs, the driver-level hooks that make timestamp source selection possible. That conversion effort was carried out by Vadim Fedorenko and Vladimir Oltean, whose patient work across dozens of drivers turned a new kernel feature into something broadly usable from day one.
Where things stand today
As for PTP support in the Marvell driver itself, the original goal of all this work, the story isn’t finished yet. Our second revision didn’t align with Russell King’s expectations: he already had his own patch series in development in his own Linux tree. His series makes sense: the Marvell PTP implementation is also shared with Marvell mv88e6xxx switches, requiring a more generic approach. But Russell’s series has its own issues and doesn’t work cleanly on our customer’s hardware. For now, the Marvell PHY PTP support is stalled again. Moving forward will mean resuming his work, adapting it to our customer’s board, and validating it on a board with an mv88e6xxx switch as well.
Two years in, the original goal (adding PTP support to the Marvell PHY driver) is still not fully complete. But that’s only part of the story. In the process, this work exposed a real limitation in the Linux PTP stack and led to a proper upstream solution for selecting between multiple hardware timestamping sources. That improvement is now in mainline and benefits a wide range of drivers and platforms.
This kind of detour is common in kernel work. What looks like a contained feature often ends up touching deeper parts of the system, requiring design changes, coordination with other ongoing efforts, and multiple rounds of review before things converge. Getting code upstream is not just about making it work: it’s about making it fit, and getting buy-in from the people who maintain the subsystem.
At Bootlin, this is exactly the kind of work we help our customers with. Beyond implementing features, we take care of the upstreaming process itself: engaging with maintainers, iterating on feedback, aligning with existing designs, and sometimes helping shape the direction of the subsystem when needed. It can be a long and sometimes uncertain path, but it’s what ensures that the result is maintainable and usable in the long term.
In this case, the initial feature is still in progress, but the work already delivered value upstream. That’s often how these projects go: even when the end goal takes time, the intermediate steps improve the kernel and that benefits both our customers and the wider community.
