From: James Clark <jjc@jclark.com>
To: "David S. Miller" <davem@davemloft.net>,
"Eric Dumazet" <edumazet@kernel.org>,
"Jakub Kicinski" <kuba@kernel.org>,
"Paolo Abeni" <pabeni@redhat.com>,
"Andrew Lunn" <andrew+netdev@lunn.ch>,
"Heiner Kallweit" <hkallweit1@gmail.com>,
"Richard Cochran" <richardcochran@gmail.com>,
"Florian Fainelli" <florian.fainelli@broadcom.com>,
"Doug Berger" <opendmb@gmail.com>,
"Nicolai Buchwitz" <nb@tipi-net.de>,
"Théo Lebrun" <theo.lebrun@bootlin.com>
Cc: Russell King <linux@armlinux.org.uk>,
Conor Dooley <conor.dooley@microchip.com>,
Broadcom internal kernel review list
<bcm-kernel-feedback-list@broadcom.com>,
Thomas Gleixner <tglx@kernel.org>,
Miroslav Lichvar <mlichvar@redhat.com>,
netdev@vger.kernel.org, linux-kernel@vger.kernel.org
Subject: [PATCH net-next 5/5] net: mdio: bcm-unimac: implement timestamped MDIO writes
Date: Fri, 9 Oct 2026 21:35:06 +0700 [thread overview]
Message-ID: <20261009143506.2507607-6-jjc@jclark.com> (raw)
In-Reply-To: <20261009143506.2507607-1-jjc@jclark.com>
Implement write_sts to provide system timestamp bounds for completion
of an MDIO write. This enables tighter system timestamp bounds in PHY
implementations of gettimex64.
Take system timestamps around the command start, then add the time
from the command start until the MDC edge that clocks the last data
bit, calculated from the reference clock rate and the configured MDC
divider.
On BCM2711 that edge comes 64 MDC periods after the command start, on
average. This was measured on a Raspberry Pi CM4 with its BCM54210PE
PHY. The PHY's PHC was read with PTP_SYS_OFFSET_EXTENDED while the MDC
divider was switched between 9 and 39 through /dev/mem. Any error in
the number of periods would make the PHC offset jump at each switch.
After compensating for drift, the jump with 64 periods was under 10 ns.
The MDC divider appears to run freely: the PHC offsets spread evenly
over one MDC period at each divider. So use 63.5 and 64.5 periods as
the lower and upper bounds of the delay.
Use a 200 MHz reference rate for BCM2711 GENET, whose clock is not
described in DT.
Signed-off-by: James Clark <jjc@jclark.com>
Assisted-by: LLM
---
When there is no clock, unimac_mdio_clk_set() assumes a 250 MHz
reference rate, and no in-tree DT gives GENET or UniMAC a clock. For
BCM2711 I have no documentation of the reference rate or of the clock
that supplies it, but MDIO busy times measured at several MDC dividers
fit a rate of 200 MHz. This patch checks the parent's compatible string,
which is unsatisfactory. I would prefer to get the rate from DT, and
would welcome suggestions for the right DT description.
The delay of 63.5 to 64.5 MDC periods has been measured only on
BCM2711, but the code assumes it holds for any UniMAC.
drivers/net/mdio/mdio-bcm-unimac.c | 105 ++++++++++++++++++++++++++++-
1 file changed, 102 insertions(+), 3 deletions(-)
diff --git a/drivers/net/mdio/mdio-bcm-unimac.c b/drivers/net/mdio/mdio-bcm-unimac.c
index 31e396cc9fb..e1ae806d1a8 100644
--- a/drivers/net/mdio/mdio-bcm-unimac.c
+++ b/drivers/net/mdio/mdio-bcm-unimac.c
@@ -16,6 +16,7 @@
#include <linux/phy.h>
#include <linux/platform_data/mdio-bcm-unimac.h>
#include <linux/platform_device.h>
+#include <linux/ptp_clock_kernel.h>
#include <linux/sched.h>
#define MDIO_CMD 0x00
@@ -42,6 +43,7 @@ struct unimac_mdio_priv {
void *wait_func_data;
struct clk *clk;
u32 clk_freq;
+ unsigned long mdio_ref_rate;
};
static inline u32 unimac_mdio_readl(struct unimac_mdio_priv *priv, u32 offset)
@@ -73,6 +75,27 @@ static inline void unimac_mdio_start(struct unimac_mdio_priv *priv)
unimac_mdio_writel(priv, reg, MDIO_CMD);
}
+static void unimac_mdio_start_sts(struct unimac_mdio_priv *priv,
+ struct ptp_system_timestamp *sts)
+{
+ unsigned long flags;
+ u32 reg;
+
+ reg = unimac_mdio_readl(priv, MDIO_CMD);
+ reg |= MDIO_START_BUSY;
+ local_irq_save(flags);
+ ptp_read_system_prets(sts);
+ /* Order the timestamp before the relaxed command write. */
+ mb();
+ unimac_mdio_writel(priv, reg, MDIO_CMD);
+ /* Flush the posted write before taking the upper bound. */
+ unimac_mdio_readl(priv, MDIO_CMD);
+ /* Order the read-back before the system timestamp. */
+ rmb();
+ ptp_read_system_postts(sts);
+ local_irq_restore(flags);
+}
+
static int unimac_mdio_poll(void *wait_func_data)
{
struct unimac_mdio_priv *priv = wait_func_data;
@@ -127,10 +150,41 @@ static int unimac_mdio_read(struct mii_bus *bus, int phy_id, int reg)
return ret;
}
-static int unimac_mdio_write(struct mii_bus *bus, int phy_id,
- int reg, u16 val)
+static int unimac_mdio_sts_delays(struct unimac_mdio_priv *priv,
+ u64 *pre_ns, u64 *post_ns)
+{
+ u32 cmd, config, divisor;
+ int ret;
+
+ /* The delays assume the controller is idle. */
+ ret = read_poll_timeout(unimac_mdio_readl, cmd,
+ !(cmd & MDIO_START_BUSY),
+ 1, 1000, false, priv, MDIO_CMD);
+ if (ret)
+ return ret;
+
+ config = unimac_mdio_readl(priv, MDIO_CFG);
+ if (config & MDIO_SUPP_PREAMBLE)
+ return -EIO;
+ divisor = 2 * (((config >> MDIO_CLK_DIV_SHIFT) & MDIO_CLK_DIV_MASK) + 1);
+ /* On BCM2711 the MDC divider runs freely, so the MDC edge that
+ * clocks the last bit of a write comes 63.5 to 64.5 periods after
+ * the command start.
+ */
+ *pre_ns = div64_ul(127ULL * divisor * NSEC_PER_SEC,
+ 2 * priv->mdio_ref_rate);
+ *post_ns = div64_ul(129ULL * divisor * NSEC_PER_SEC +
+ 2 * priv->mdio_ref_rate - 1,
+ 2 * priv->mdio_ref_rate);
+
+ return 0;
+}
+
+static int unimac_mdio_write_sts(struct mii_bus *bus, int phy_id, int reg,
+ u16 val, struct ptp_system_timestamp *sts)
{
struct unimac_mdio_priv *priv = bus->priv;
+ u64 pre_ns, post_ns;
u32 cmd;
int ret;
@@ -138,19 +192,38 @@ static int unimac_mdio_write(struct mii_bus *bus, int phy_id,
if (ret)
return ret;
+ if (sts) {
+ ret = unimac_mdio_sts_delays(priv, &pre_ns, &post_ns);
+ if (ret)
+ goto out;
+ }
+
/* Prepare the write operation */
cmd = MDIO_WR | (phy_id << MDIO_PMD_SHIFT) |
(reg << MDIO_REG_SHIFT) | (0xffff & val);
unimac_mdio_writel(priv, cmd, MDIO_CMD);
- unimac_mdio_start(priv);
+ if (sts) {
+ unimac_mdio_start_sts(priv, sts);
+ ptp_adjust_system_prets(sts, pre_ns);
+ ptp_adjust_system_postts(sts, post_ns);
+ } else {
+ unimac_mdio_start(priv);
+ }
ret = priv->wait_func(priv->wait_func_data);
+out:
clk_disable_unprepare(priv->clk);
return ret;
}
+static int unimac_mdio_write(struct mii_bus *bus, int phy_id,
+ int reg, u16 val)
+{
+ return unimac_mdio_write_sts(bus, phy_id, reg, val, NULL);
+}
+
/* Workaround for integrated BCM7xxx Gigabit PHYs which have a problem with
* their internal MDIO management controller making them fail to successfully
* be read from or written to for the first transaction. We insert a dummy
@@ -234,6 +307,30 @@ static int unimac_mdio_clk_set(struct unimac_mdio_priv *priv)
return ret;
}
+static bool unimac_mdio_init_sts(struct unimac_mdio_priv *priv,
+ struct device *dev)
+{
+ u32 config;
+
+ /* The reference rate is fixed, so read it once. */
+ priv->mdio_ref_rate = clk_get_rate(priv->clk);
+ /* BCM2711's 200 MHz GENET reference clock is not described in DT. */
+ if (!priv->mdio_ref_rate && dev->parent &&
+ of_device_is_compatible(dev->parent->of_node,
+ "brcm,bcm2711-genet-v5"))
+ priv->mdio_ref_rate = 200000000;
+
+ if (!priv->mdio_ref_rate)
+ return false;
+
+ if (clk_prepare_enable(priv->clk))
+ return false;
+ config = unimac_mdio_readl(priv, MDIO_CFG);
+ clk_disable_unprepare(priv->clk);
+
+ return !(config & MDIO_SUPP_PREAMBLE);
+}
+
static int unimac_mdio_probe(struct platform_device *pdev)
{
struct unimac_mdio_pdata *pdata = pdev->dev.platform_data;
@@ -292,6 +389,8 @@ static int unimac_mdio_probe(struct platform_device *pdev)
bus->parent = &pdev->dev;
bus->read = unimac_mdio_read;
bus->write = unimac_mdio_write;
+ if (unimac_mdio_init_sts(priv, &pdev->dev))
+ bus->write_sts = unimac_mdio_write_sts;
bus->reset = unimac_mdio_reset;
snprintf(bus->id, MII_BUS_ID_SIZE, "%s-%d", pdev->name, pdev->id);
--
2.56.0
next prev parent reply other threads:[~2026-10-09 14:35 UTC|newest]
Thread overview: 17+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-09 14:35 [PATCH net-next 0/5] net: mdio: add timestamped MDIO writes for PHY gettimex64 James Clark
2026-10-09 14:35 ` [PATCH net-next 1/5] net: mdio: add timestamped write operation James Clark
2026-10-10 15:10 ` netdev-bot+sashiko
2026-10-09 14:35 ` [PATCH net-next 2/5] net: phy: broadcom: use timestamped MDIO writes in gettimex64 James Clark
2026-10-10 15:10 ` netdev-bot+sashiko
2026-10-09 14:35 ` [PATCH net-next 3/5] ptp: add functions to adjust system timestamps James Clark
2026-10-10 15:10 ` netdev-bot+sashiko
2026-10-09 14:35 ` [PATCH net-next 4/5] net: macb: implement timestamped MDIO writes James Clark
2026-10-10 15:10 ` netdev-bot+sashiko
[not found] ` <DM1WEUIF8V8V.2OZWRB5G232T4@bootlin.com>
2026-10-11 10:02 ` Théo Lebrun
2026-10-09 14:35 ` James Clark [this message]
2026-10-09 16:04 ` [PATCH net-next 5/5] net: mdio: bcm-unimac: " Florian Fainelli
2026-10-10 1:25 ` James Clark
2026-10-10 15:18 ` Nicolai Buchwitz
2026-10-11 7:20 ` James Clark
2026-10-10 15:11 ` netdev-bot+sashiko
2026-10-10 4:55 ` [PATCH net-next 0/5] net: mdio: add timestamped MDIO writes for PHY gettimex64 James Clark
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261009143506.2507607-6-jjc@jclark.com \
--to=jjc@jclark.com \
--cc=andrew+netdev@lunn.ch \
--cc=bcm-kernel-feedback-list@broadcom.com \
--cc=conor.dooley@microchip.com \
--cc=davem@davemloft.net \
--cc=edumazet@kernel.org \
--cc=florian.fainelli@broadcom.com \
--cc=hkallweit1@gmail.com \
--cc=kuba@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux@armlinux.org.uk \
--cc=mlichvar@redhat.com \
--cc=nb@tipi-net.de \
--cc=netdev@vger.kernel.org \
--cc=opendmb@gmail.com \
--cc=pabeni@redhat.com \
--cc=richardcochran@gmail.com \
--cc=tglx@kernel.org \
--cc=theo.lebrun@bootlin.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox