* [PATCH RFC net-next 0/5] Marvell PTP support
@ 2025-04-11 21:26 Russell King (Oracle)
2025-04-11 21:26 ` [PATCH RFC net-next 1/5] net: mvpp2: add support for hardware timestamps Russell King
` (6 more replies)
0 siblings, 7 replies; 28+ messages in thread
From: Russell King (Oracle) @ 2025-04-11 21:26 UTC (permalink / raw)
To: Andrew Lunn, Heiner Kallweit
Cc: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Kory Maincent, Marcin Wojtas, netdev, Paolo Abeni,
Richard Cochran, Vladimir Oltean
Hi,
This series is a work in progress, and represents the current state of
things, superseding Kory's patches which were based in a very old
version of my patches - and my patches were subsequently refactored
and further developed about five years ago. Due to them breaking
mvpp2 if merged, there was no point in posting them until such time
that the underlying issues with PTP were resolved - and they now have
been.
Marvell re-uses their PTP IP in several of their products - PHYs,
switches and even some ethernet MACs contain the same IP. It really
doesn't make sense to duplicate the code in each of these use cases.
Therefore, this series introduces a Marvell PTP core that can be
re-used - a TAI module, which handles the global parts of the PTP
core, and the TS module, which handles the per-port timestamping.
I will note at this point that although the Armada 388 TRM states that
NETA contains the same IP, attempts to access the registers returns
zero, and it is not known if that is due to the board missing something
or whether it isn't actually implemented. I do have some early work
re-using this, but when I discovered that the TAI registers read as
zero and wouldn't accept writes, I haven't progressed that.
Today, I have converted the mv88e6xxx DSA code to use the Marvell TAI
module from patch 1, and for the sake of getting the code out there,
I have included the "hacky" patches in this series - with the issues
with DSA VLANs that I reported this evening and subsequently
investigated, I've not had any spare time to properly prepare that
part of this series. (Being usurped from phylink by stmmac - for which
I have a big stack of patches that I can't get out because of being
usurped, and then again by Marvell PTP, and then again by DSA VLAN
stuff... yea, I'm feeling like I have zero time to do anything right
now.) The mv88e6xxx DSA code still needs to be converted to use the
Marvell TS part of patch 1, but I won't be able to test that after
Sunday, and I'm certainly not working on this over this weekend.
Anyway, this is what it is - and this is likely the state of it for
a while yet, because I won't be able to sensibly access the hardware
for testing for an undefined period of time.
The PHY parts seem to work, although not 100% reliably, with the
occasional overrun, particularly on the receive side. I'm not sure
whether this is down to a hardware bug or not, or MDIO driver bug,
because we certainly aren't missing timestamping a SKB. This has been
tested at L2 and L4.
I'm not sure which packets we should be timestamping (remembering
that this is global config across all ports.)
https://chronos.uk/wordpress/wp-content/uploads/TechnicalBrief-IEEE1588v2PTP.pdf
suggests Sync, Delay_req and Delay_resp need to be timestamped,
possibly PDelay_req and PDelay_resp as well, but I haven't seen
those produced by PTPDv2 nor ptp4l.
There's probably other stuff I should mention, but as I've been at
this into the evening for almost every day this week, I'm mentally
exhausted.
Sorry also if this isn't coherent.
drivers/net/dsa/mv88e6xxx/Kconfig | 1 +
drivers/net/dsa/mv88e6xxx/chip.h | 26 +-
drivers/net/dsa/mv88e6xxx/hwtstamp.c | 17 +-
drivers/net/dsa/mv88e6xxx/hwtstamp.h | 1 +
drivers/net/dsa/mv88e6xxx/ptp.c | 519 ++++++++-------------
drivers/net/dsa/mv88e6xxx/ptp.h | 1 -
drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c | 2 +
drivers/net/phy/Kconfig | 13 +
drivers/net/phy/Makefile | 1 +
drivers/net/phy/marvell.c | 21 +-
drivers/net/phy/marvell_ptp.c | 307 ++++++++++++
drivers/net/phy/marvell_ptp.h | 21 +
drivers/ptp/Kconfig | 4 +
drivers/ptp/Makefile | 2 +
drivers/ptp/ptp_marvell_tai.c | 449 ++++++++++++++++++
drivers/ptp/ptp_marvell_ts.c | 593 ++++++++++++++++++++++++
include/linux/marvell_ptp.h | 129 ++++++
17 files changed, 1764 insertions(+), 343 deletions(-)
--
RMK's Patch system: https://www.armlinux.org.uk/developer/patches/
FTTP is here! 80Mbps down 10Mbps up. Decent connectivity at last!
^ permalink raw reply [flat|nested] 28+ messages in thread
* [PATCH RFC net-next 1/5] net: mvpp2: add support for hardware timestamps
2025-04-11 21:26 [PATCH RFC net-next 0/5] Marvell PTP support Russell King (Oracle)
@ 2025-04-11 21:26 ` Russell King
2025-04-11 21:29 ` Russell King (Oracle)
2025-04-14 12:51 ` Kory Maincent
2025-04-11 21:26 ` [PATCH RFC net-next 2/5] ptp: marvell: add core support for Marvell PTP v2.1 Russell King
` (5 subsequent siblings)
6 siblings, 2 replies; 28+ messages in thread
From: Russell King @ 2025-04-11 21:26 UTC (permalink / raw)
To: Andrew Lunn, Heiner Kallweit
Cc: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Kory Maincent, Marcin Wojtas, netdev, Paolo Abeni
Add support for hardware timestamps in (e.g.) the PHY by calling
skb_tx_timestamp() as close as reasonably possible to the point that
the hardware is instructed to send the queued packets.
Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
---
drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c
index 416a926a8281..e3f8aa139d1e 100644
--- a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c
+++ b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c
@@ -4439,6 +4439,8 @@ static netdev_tx_t mvpp2_tx(struct sk_buff *skb, struct net_device *dev)
txq_pcpu->count += frags;
aggr_txq->count += frags;
+ skb_tx_timestamp(skb);
+
/* Enable transmit */
wmb();
mvpp2_aggr_txq_pend_desc_add(port, frags);
--
2.30.2
^ permalink raw reply related [flat|nested] 28+ messages in thread
* [PATCH RFC net-next 2/5] ptp: marvell: add core support for Marvell PTP v2.1
2025-04-11 21:26 [PATCH RFC net-next 0/5] Marvell PTP support Russell King (Oracle)
2025-04-11 21:26 ` [PATCH RFC net-next 1/5] net: mvpp2: add support for hardware timestamps Russell King
@ 2025-04-11 21:26 ` Russell King
2025-04-11 21:30 ` Russell King (Oracle)
2025-04-16 8:48 ` Kory Maincent
2025-04-11 21:26 ` [PATCH RFC net-next 3/5] net: phy: add Marvell PHY PTP support Russell King
` (4 subsequent siblings)
6 siblings, 2 replies; 28+ messages in thread
From: Russell King @ 2025-04-11 21:26 UTC (permalink / raw)
To: Andrew Lunn, Heiner Kallweit
Cc: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Kory Maincent, netdev, Paolo Abeni, Richard Cochran
Provide core support for the Marvell PTP v2.1 implementations, which
consist of a TAI (time application interface) and timestamping blocks.
This hardware can be found in Marvell 88E151x PHYs, Armada 38x and
Armada 37xx (mvneta), as well as Marvell DSA devices.
Support for both arrival timestamps is supported, we use arrival 1 for
PTP peer delay messages, and arrival 0 for all other messages.
External event capture is also supported.
PPS output and trigger generation is not supported.
This core takes inspiration from the existing Marvell 88E6xxx DSA PTP
code and DP83640 drivers. Like the original 88E6xxx DSA code, we
use a delayed work to keep the cycle counter updated, and a separate
delayed work for event capture.
We expose the ptp clock aux work to allow users to support single and
multi-port designs - where there is one Marvell TAI instance and a
number of Marvell TS instances.
Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
---
drivers/ptp/Kconfig | 4 +
drivers/ptp/Makefile | 2 +
drivers/ptp/ptp_marvell_tai.c | 449 +++++++++++++++++++++++++
drivers/ptp/ptp_marvell_ts.c | 593 ++++++++++++++++++++++++++++++++++
include/linux/marvell_ptp.h | 129 ++++++++
5 files changed, 1177 insertions(+)
create mode 100644 drivers/ptp/ptp_marvell_tai.c
create mode 100644 drivers/ptp/ptp_marvell_ts.c
create mode 100644 include/linux/marvell_ptp.h
diff --git a/drivers/ptp/Kconfig b/drivers/ptp/Kconfig
index 07bf7f9aae01..27b54f37b9ab 100644
--- a/drivers/ptp/Kconfig
+++ b/drivers/ptp/Kconfig
@@ -184,6 +184,10 @@ config PTP_1588_CLOCK_FC3W
To compile this driver as a module, choose M here: the module
will be called ptp_fc3.
+config PTP_1588_CLOCK_MARVELL
+ tristate
+ depends on PTP_1588_CLOCK
+
config PTP_1588_CLOCK_MOCK
tristate "Mock-up PTP clock"
depends on PTP_1588_CLOCK
diff --git a/drivers/ptp/Makefile b/drivers/ptp/Makefile
index 25f846fe48c9..6248f75d9335 100644
--- a/drivers/ptp/Makefile
+++ b/drivers/ptp/Makefile
@@ -12,6 +12,8 @@ obj-$(CONFIG_PTP_1588_CLOCK_INES) += ptp_ines.o
obj-$(CONFIG_PTP_1588_CLOCK_PCH) += ptp_pch.o
obj-$(CONFIG_PTP_1588_CLOCK_KVM) += ptp_kvm.o
obj-$(CONFIG_PTP_1588_CLOCK_VMCLOCK) += ptp_vmclock.o
+obj-$(CONFIG_PTP_1588_CLOCK_MARVELL) += ptp-marvell.o
+ptp-marvell-y := ptp_marvell_tai.o ptp_marvell_ts.o
obj-$(CONFIG_PTP_1588_CLOCK_QORIQ) += ptp-qoriq.o
ptp-qoriq-y += ptp_qoriq.o
ptp-qoriq-$(CONFIG_DEBUG_FS) += ptp_qoriq_debugfs.o
diff --git a/drivers/ptp/ptp_marvell_tai.c b/drivers/ptp/ptp_marvell_tai.c
new file mode 100644
index 000000000000..eea7ccdce729
--- /dev/null
+++ b/drivers/ptp/ptp_marvell_tai.c
@@ -0,0 +1,449 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * TAI (time application interface) driver for Marvell PHYs and Marvell NETA.
+ *
+ * This file implements TAI support as a PTP clock. Timecounter/cyclecounter
+ * representation taken from Marvell 88E6xxx DSA driver. We may need to share
+ * the TAI between multiple PHYs in a multiport PHY.
+ */
+#include <linux/if_ether.h>
+#include <linux/ktime.h>
+#include <linux/slab.h>
+#include <linux/marvell_ptp.h>
+
+#define TAI_CONFIG_0 0
+#define TAI_CONFIG_0_EVENTCAPOV BIT(15)
+#define TAI_CONFIG_0_EVENTCTRSTART BIT(14)
+#define TAI_CONFIG_0_EVENTPHASE BIT(13)
+#define TAI_CONFIG_0_TRIGGENINTEN BIT(9)
+#define TAI_CONFIG_0_EVENTCAPINTEN BIT(8)
+
+#define TAI_CONFIG_9 9
+#define TAI_CONFIG_9_EVENTCAPERR BIT(9)
+#define TAI_CONFIG_9_EVENTCAPVALID BIT(8)
+
+#define TAI_EVENT_CAPTURE_TIME_LO 10
+#define TAI_EVENT_CAPTURE_TIME_HI 11
+
+#define PTPG_CONFIG_0 0
+#define PTPG_CONFIG_1 1
+#define PTPG_CONFIG_2 2
+#define PTPG_CONFIG_3 3
+#define PTPG_CONFIG_3_TSATSFD BIT(0)
+#define PTPG_STATUS 8
+
+#define TAI_EVENT_POLL_INTERVAL msecs_to_jiffies(100)
+
+struct marvell_tai {
+ const struct marvell_ptp_ops *ops;
+ struct device *dev;
+
+ struct ptp_clock_info caps;
+ struct ptp_clock *ptp_clock;
+
+ u32 cc_mult_num;
+ u32 cc_mult_den;
+ u32 cc_mult;
+
+ struct mutex mutex;
+ struct timecounter timecounter;
+ struct cyclecounter cyclecounter;
+
+ long half_overflow_period;
+ struct delayed_work overflow_work;
+
+ bool defunct;
+ bool extts_poll;
+ struct delayed_work event_work;
+
+ /* Used while reading the TAI */
+ struct ptp_system_timestamp *sts;
+};
+
+static struct marvell_tai *cc_to_tai(const struct cyclecounter *cc)
+{
+ return container_of(cc, struct marvell_tai, cyclecounter);
+}
+
+/* Read the global time registers using the readplus command */
+static u64 marvell_tai_clock_read(const struct cyclecounter *cc)
+{
+ struct marvell_tai *tai = cc_to_tai(cc);
+
+ return tai->ops->tai_clock_read(tai->dev, tai->sts);
+}
+
+u64 marvell_tai_cyc2time(struct marvell_tai *tai, u32 cyc)
+{
+ u64 ns;
+
+ mutex_lock(&tai->mutex);
+ ns = timecounter_cyc2time(&tai->timecounter, cyc);
+ mutex_unlock(&tai->mutex);
+
+ return ns;
+}
+EXPORT_SYMBOL_GPL(marvell_tai_cyc2time);
+
+static struct marvell_tai *ptp_to_tai(struct ptp_clock_info *ptp)
+{
+ return container_of(ptp, struct marvell_tai, caps);
+}
+
+static int marvell_tai_adjfine(struct ptp_clock_info *ptp, long scaled_ppm)
+{
+ struct marvell_tai *tai = ptp_to_tai(ptp);
+ bool neg;
+ u32 diff;
+ u64 adj;
+
+ neg = scaled_ppm < 0;
+ if (neg)
+ scaled_ppm = -scaled_ppm;
+
+ adj = tai->cc_mult_num;
+ adj *= scaled_ppm;
+ diff = div_u64(adj, tai->cc_mult_den);
+
+ mutex_lock(&tai->mutex);
+ timecounter_read(&tai->timecounter);
+ tai->cyclecounter.mult = neg ? tai->cc_mult - diff :
+ tai->cc_mult + diff;
+ mutex_unlock(&tai->mutex);
+
+ return 0;
+}
+
+static int marvell_tai_adjtime(struct ptp_clock_info *ptp, s64 delta)
+{
+ struct marvell_tai *tai = ptp_to_tai(ptp);
+
+ mutex_lock(&tai->mutex);
+ timecounter_adjtime(&tai->timecounter, delta);
+ mutex_unlock(&tai->mutex);
+
+ return 0;
+}
+
+static int marvell_tai_gettimex64(struct ptp_clock_info *ptp,
+ struct timespec64 *ts,
+ struct ptp_system_timestamp *sts)
+{
+ struct marvell_tai *tai = ptp_to_tai(ptp);
+ u64 ns;
+
+ mutex_lock(&tai->mutex);
+ tai->sts = sts;
+ ns = timecounter_read(&tai->timecounter);
+ tai->sts = NULL;
+ mutex_unlock(&tai->mutex);
+
+ *ts = ns_to_timespec64(ns);
+
+ return 0;
+}
+
+static int marvell_tai_settime64(struct ptp_clock_info *ptp,
+ const struct timespec64 *ts)
+{
+ struct marvell_tai *tai = ptp_to_tai(ptp);
+ u64 ns = timespec64_to_ns(ts);
+
+ mutex_lock(&tai->mutex);
+ timecounter_init(&tai->timecounter, &tai->cyclecounter, ns);
+ mutex_unlock(&tai->mutex);
+
+ return 0;
+}
+
+static void marvell_tai_extts(struct marvell_tai *tai)
+{
+ struct marvell_extts extts;
+ struct ptp_clock_event ev;
+ int err;
+
+ err = tai->ops->tai_extts_read(tai->dev, TAI_CONFIG_9, &extts);
+ if (err <= 0)
+ return;
+
+ if (extts.status & TAI_CONFIG_9_EVENTCAPERR) {
+ dev_warn(tai->dev, "extts timestamp overrun (%x)\n",
+ extts.status);
+ return;
+ }
+
+ ev.type = PTP_CLOCK_EXTTS;
+ ev.index = 0;
+ ev.timestamp = marvell_tai_cyc2time(tai, extts.time);
+
+ ptp_clock_event(tai->ptp_clock, &ev);
+}
+
+static int marvell_tai_enable_extts(struct marvell_tai *tai,
+ struct ptp_extts_request *req, int enable)
+{
+ int err, pin;
+ u16 cfg0;
+
+ if (req->flags & ~(PTP_ENABLE_FEATURE | PTP_RISING_EDGE |
+ PTP_FALLING_EDGE | PTP_STRICT_FLAGS))
+ return -EINVAL;
+
+ pin = ptp_find_pin(tai->ptp_clock, PTP_PF_EXTTS, req->index);
+ if (pin < 0)
+ return -EBUSY;
+
+ /* Setup this pin, validating flags as appropriate */
+ err = tai->ops->tai_pin_setup(tai->dev, pin, req->flags, enable);
+ if (err < 0)
+ return err;
+
+ if (enable) {
+ /* Clear the status */
+ err = tai->ops->tai_write(tai->dev, TAI_CONFIG_9, 0);
+ if (err < 0)
+ return err;
+
+ cfg0 = TAI_CONFIG_0_EVENTCAPINTEN |
+ TAI_CONFIG_0_EVENTCTRSTART;
+
+ /*
+ * For compatibility with DSA, we test for !rising rather
+ * than for falling.
+ */
+ if (!(req->flags & PTP_RISING_EDGE))
+ cfg0 |= TAI_CONFIG_0_EVENTPHASE;
+
+ /* Enable the event interrupt and counter */
+ err = tai->ops->tai_modify(tai->dev, TAI_CONFIG_0,
+ TAI_CONFIG_0_EVENTCAPOV |
+ TAI_CONFIG_0_EVENTCTRSTART |
+ TAI_CONFIG_0_EVENTCAPINTEN, cfg0);
+ if (err < 0)
+ return err;
+
+ schedule_delayed_work(&tai->event_work,
+ TAI_EVENT_POLL_INTERVAL);
+ } else {
+ /* Disable the event interrupt and counter */
+ err = tai->ops->tai_modify(tai->dev, TAI_CONFIG_0,
+ TAI_CONFIG_0_EVENTCTRSTART |
+ TAI_CONFIG_0_EVENTCAPINTEN, 0);
+ if (err < 0)
+ return err;
+
+ cancel_delayed_work_sync(&tai->event_work);
+ }
+
+ return 0;
+}
+
+static int marvell_tai_enable(struct ptp_clock_info *ptp,
+ struct ptp_clock_request *req, int enable)
+{
+ struct marvell_tai *tai = ptp_to_tai(ptp);
+ int err;
+
+ switch (req->type) {
+ case PTP_PF_EXTTS:
+ err = marvell_tai_enable_extts(tai, &req->extts, enable);
+ break;
+
+ default:
+ err = -EOPNOTSUPP;
+ break;
+ }
+
+ return err;
+}
+
+static int marvell_tai_verify(struct ptp_clock_info *ptp, unsigned int pin,
+ enum ptp_pin_function func, unsigned int chan)
+{
+ struct marvell_tai *tai = ptp_to_tai(ptp);
+
+ /* Always allow a pin to be set to no function */
+ if (func == PTP_PF_NONE)
+ return 0;
+
+ if (!tai->ops->tai_pin_verify)
+ return -EOPNOTSUPP;
+
+ return tai->ops->tai_pin_verify(tai->dev, pin, func, chan);
+}
+
+/* Periodically read the timecounter to keep the time refreshed. */
+static long marvell_tai_aux_work(struct ptp_clock_info *ptp)
+{
+ struct marvell_tai *tai = ptp_to_tai(ptp);
+ long ret = -1;
+
+ if (tai->ops->ptp_aux_work)
+ ret = tai->ops->ptp_aux_work(tai->dev);
+
+ return ret;
+}
+
+#define event_work_to_tai(w) \
+ container_of(to_delayed_work(w), struct marvell_tai, event_work)
+static void marvell_tai_event_work(struct work_struct *w)
+{
+ struct marvell_tai *tai = event_work_to_tai(w);
+
+ if (tai->defunct)
+ return;
+
+ marvell_tai_extts(tai);
+
+ schedule_delayed_work(&tai->event_work, TAI_EVENT_POLL_INTERVAL);
+}
+
+#define overflow_work_to_tai(w) \
+ container_of(to_delayed_work(w), struct marvell_tai, overflow_work)
+static void marvell_tai_overflow_work(struct work_struct *w)
+{
+ struct marvell_tai *tai = overflow_work_to_tai(w);
+
+ /* Read the timecounter to update */
+ mutex_lock(&tai->mutex);
+ timecounter_read(&tai->timecounter);
+ mutex_unlock(&tai->mutex);
+
+ schedule_delayed_work(&tai->overflow_work, tai->half_overflow_period);
+}
+
+/* Configure the global (shared between ports) configuration for the PHY. */
+static int marvell_tai_global_config(struct marvell_tai *tai)
+{
+ int err;
+
+ /* Enable TAI */
+ err = tai->ops->tai_enable(tai->dev);
+ if (err)
+ return err;
+
+ /* Set ether-type for IEEE1588 packets */
+ err = tai->ops->ptp_global_write(tai->dev, PTPG_CONFIG_0, ETH_P_1588);
+ if (err < 0)
+ return err;
+
+ /* MsdIDTSEn - Enable timestamping on all PTP MessageIDs */
+ err = tai->ops->ptp_global_write(tai->dev, PTPG_CONFIG_1,
+ MV_PTP_MSD_ID_TS_EN);
+ if (err < 0)
+ return err;
+
+ /* TSArrPtr - Point to Arr0 registers */
+ err = tai->ops->ptp_global_write(tai->dev, PTPG_CONFIG_2,
+ MV_PTP_TS_ARR_PTR);
+ if (err < 0)
+ return err;
+
+ /* TSAtSFD - timestamp at SFD */
+ err = tai->ops->ptp_global_write(tai->dev, PTPG_CONFIG_3,
+ PTPG_CONFIG_3_TSATSFD);
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+int marvell_tai_ptp_clock_index(struct marvell_tai *tai)
+{
+ return ptp_clock_index(tai->ptp_clock);
+}
+EXPORT_SYMBOL_GPL(marvell_tai_ptp_clock_index);
+
+int marvell_tai_schedule(struct marvell_tai *tai, unsigned long delay)
+{
+ return ptp_schedule_worker(tai->ptp_clock, delay);
+}
+EXPORT_SYMBOL_GPL(marvell_tai_schedule);
+
+int marvell_tai_probe(struct marvell_tai **taip,
+ const struct marvell_ptp_ops *ops,
+ const struct marvell_tai_param *param,
+ struct ptp_pin_desc *pin_config, int n_pins,
+ const char *name, struct device *dev)
+{
+ struct marvell_tai *tai;
+ u64 overflow_ns;
+ int err;
+
+ tai = devm_kzalloc(dev, sizeof(*tai), GFP_KERNEL);
+ if (!tai)
+ return -ENOMEM;
+
+ mutex_init(&tai->mutex);
+
+ tai->dev = dev;
+ tai->ops = ops;
+ tai->cc_mult_num = param->cc_mult_num;
+ tai->cc_mult_den = param->cc_mult_den;
+ tai->cc_mult = param->cc_mult;
+
+ err = marvell_tai_global_config(tai);
+ if (err < 0)
+ return err;
+
+ tai->cyclecounter.read = marvell_tai_clock_read;
+ tai->cyclecounter.mask = CYCLECOUNTER_MASK(32);
+ tai->cyclecounter.mult = param->cc_mult;
+ tai->cyclecounter.shift = param->cc_shift;
+
+ overflow_ns = BIT_ULL(32) * param->cc_mult;
+ overflow_ns >>= param->cc_shift;
+ tai->half_overflow_period = nsecs_to_jiffies64(overflow_ns / 2);
+
+ timecounter_init(&tai->timecounter, &tai->cyclecounter,
+ ktime_to_ns(ktime_get_real()));
+
+ tai->caps.owner = THIS_MODULE;
+ strscpy(tai->caps.name, name, sizeof(tai->caps.name));
+ /* max_adj of 1000000 is what MV88E6xxx DSA uses */
+ tai->caps.max_adj = 1000000;
+ tai->caps.n_ext_ts = param->n_ext_ts;
+ tai->caps.n_pins = n_pins;
+ tai->caps.pin_config = pin_config;
+ tai->caps.adjfine = marvell_tai_adjfine;
+ tai->caps.adjtime = marvell_tai_adjtime;
+ tai->caps.gettimex64 = marvell_tai_gettimex64;
+ tai->caps.settime64 = marvell_tai_settime64;
+ tai->caps.enable = marvell_tai_enable;
+ tai->caps.verify = marvell_tai_verify;
+ tai->caps.do_aux_work = marvell_tai_aux_work;
+
+ INIT_DELAYED_WORK(&tai->overflow_work, marvell_tai_overflow_work);
+ INIT_DELAYED_WORK(&tai->event_work, marvell_tai_event_work);
+
+ tai->ptp_clock = ptp_clock_register(&tai->caps, dev);
+ if (IS_ERR(tai->ptp_clock)) {
+ kfree(tai);
+ return PTR_ERR(tai->ptp_clock);
+ }
+
+ /*
+ * Kick off the auxiliary worker to run once every half-overflow
+ * period to keep the timecounter properly updated.
+ */
+ schedule_delayed_work(&tai->overflow_work, tai->half_overflow_period);
+
+ *taip = tai;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(marvell_tai_probe);
+
+void marvell_tai_remove(struct marvell_tai *tai)
+{
+ /* Avoid races with the event work - mark defunct before
+ * unregistering, which goes against "unpublish then tear down"
+ */
+ tai->defunct = true;
+ cancel_delayed_work_sync(&tai->event_work);
+
+ ptp_clock_unregister(tai->ptp_clock);
+
+ cancel_delayed_work_sync(&tai->overflow_work);
+}
+EXPORT_SYMBOL_GPL(marvell_tai_remove);
diff --git a/drivers/ptp/ptp_marvell_ts.c b/drivers/ptp/ptp_marvell_ts.c
new file mode 100644
index 000000000000..a2e1ae9e4acc
--- /dev/null
+++ b/drivers/ptp/ptp_marvell_ts.c
@@ -0,0 +1,593 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Marvell PTP driver for 88E1510, 88E1512, 88E1514 and 88E1518 PHYs
+ *
+ * Ideas taken from 88E6xxx DSA and DP83640 drivers. This file
+ * implements the packet timestamping support only (PTP). TAI
+ * support is separate.
+ */
+#include <linux/ethtool.h>
+#include <linux/if_vlan.h>
+#include <linux/interrupt.h>
+#include <linux/marvell_ptp.h>
+#include <linux/netdevice.h>
+#include <linux/ptp_classify.h>
+#include <linux/ptp_clock_kernel.h>
+#include <linux/uaccess.h>
+
+#define TX_TIMEOUT_MS 40
+#define RX_TIMEOUT_MS 40
+
+#define PTP_PORT_CONFIG_0 0
+#define PTP_PORT_CONFIG_0_DISTSPECCHECK BIT(11)
+#define PTP_PORT_CONFIG_0_DISTSOVERWRITE BIT(1)
+#define PTP_PORT_CONFIG_0_DISPTP BIT(0)
+#define PTP_PORT_CONFIG_1 1
+#define PTP_PORT_CONFIG_1_IPJUMP(x) (((x) & 0x3f) << 8)
+#define PTP_PORT_CONFIG_1_ETJUMP(x) ((x) & 0x1f)
+#define PTP_PORT_CONFIG_2 2
+#define PTP_PORT_CONFIG_2_DEPINTEN BIT(1)
+#define PTP_PORT_CONFIG_2_ARRINTEN BIT(0)
+#define PTP_ARR_STATUS0 8
+#define PTP_ARR_STATUS1 12
+#define PTP_DEP_STATUS 16
+
+struct marvell_ptp_cb {
+ unsigned long timeout;
+ u16 seq;
+};
+#define MARVELL_PTP_CB(skb) ((struct marvell_ptp_cb *)(skb)->cb)
+
+/* RX queue support */
+
+/* Deliver a skb with its timestamp back to the networking core */
+static void marvell_rxq_rx(struct sk_buff *skb, u64 ns)
+{
+ struct skb_shared_hwtstamps *shhwtstamps = skb_hwtstamps(skb);
+
+ memset(shhwtstamps, 0, sizeof(*shhwtstamps));
+ shhwtstamps->hwtstamp = ns_to_ktime(ns);
+ netif_rx(skb);
+}
+
+/* Get a rx timestamp entry. Try the free list, and if that fails,
+ * steal the oldest off the pending list.
+ */
+static struct marvell_rxts *marvell_rxq_get_rxts(struct marvell_rxq *rxq)
+{
+ if (!list_empty(&rxq->rx_free))
+ return list_first_entry(&rxq->rx_free, struct marvell_rxts,
+ node);
+
+ return list_last_entry(&rxq->rx_pend, struct marvell_rxts, node);
+}
+
+static void marvell_rxq_init(struct marvell_rxq *rxq)
+{
+ int i;
+
+ mutex_init(&rxq->rx_mutex);
+ INIT_LIST_HEAD(&rxq->rx_free);
+ INIT_LIST_HEAD(&rxq->rx_pend);
+ skb_queue_head_init(&rxq->rx_queue);
+
+ for (i = 0; i < ARRAY_SIZE(rxq->rx_ts); i++)
+ list_add_tail(&rxq->rx_ts[i].node, &rxq->rx_free);
+}
+
+static void marvell_rxq_purge(struct marvell_rxq *rxq)
+{
+ skb_queue_purge(&rxq->rx_queue);
+}
+
+static void marvell_rxq_rx_ts(struct marvell_rxq *rxq, u16 seq, u64 ns)
+{
+ struct marvell_rxts *rxts;
+ struct sk_buff *skb;
+ bool found = false;
+
+ mutex_lock(&rxq->rx_mutex);
+
+ /* Search the rx queue for a matching skb */
+ skb_queue_walk(&rxq->rx_queue, skb) {
+ if (MARVELL_PTP_CB(skb)->seq == seq) {
+ __skb_unlink(skb, &rxq->rx_queue);
+ found = true;
+ break;
+ }
+ }
+
+ if (!found) {
+ rxts = marvell_rxq_get_rxts(rxq);
+ rxts->ns = ns;
+ rxts->seq = seq;
+ list_move(&rxts->node, &rxq->rx_pend);
+ }
+
+ mutex_unlock(&rxq->rx_mutex);
+
+ if (found)
+ marvell_rxq_rx(skb, ns);
+}
+
+static bool marvell_rxq_rxtstamp(struct marvell_rxq *rxq, struct sk_buff *skb,
+ u16 seq)
+{
+ struct marvell_rxts *rxts;
+ bool found = false;
+ u64 ns;
+
+ mutex_lock(&rxq->rx_mutex);
+
+ /* Search the pending receive timestamps for a matching seqid */
+ list_for_each_entry(rxts, &rxq->rx_pend, node) {
+ if (rxts->seq == seq) {
+ found = true;
+ ns = rxts->ns;
+ /* Move this timestamp entry to the free list */
+ list_move_tail(&rxts->node, &rxq->rx_free);
+ break;
+ }
+ }
+
+ if (!found) {
+ /* Store the seqid and queue the skb. Do this under the lock
+ * to ensure we don't miss any timestamps appended to the
+ * rx_pend list.
+ */
+ MARVELL_PTP_CB(skb)->seq = seq;
+ MARVELL_PTP_CB(skb)->timeout = jiffies +
+ msecs_to_jiffies(RX_TIMEOUT_MS);
+ __skb_queue_tail(&rxq->rx_queue, skb);
+ }
+
+ mutex_unlock(&rxq->rx_mutex);
+
+ if (found)
+ /* We found the corresponding timestamp. If we can add the
+ * timestamp, do we need to go through the netif_rx_ni()
+ * path, or would it be more efficient to add the timestamp
+ * and return "false" from marvell_ptp_rxtstamp() instead?
+ */
+ marvell_rxq_rx(skb, ns);
+
+ return found;
+}
+
+static void marvell_rxq_expire(struct marvell_rxq *rxq,
+ struct sk_buff_head *list)
+{
+ struct sk_buff *skb;
+
+ mutex_lock(&rxq->rx_mutex);
+ while ((skb = skb_dequeue(&rxq->rx_queue)) != NULL) {
+ if (!time_is_before_jiffies(MARVELL_PTP_CB(skb)->timeout)) {
+ __skb_queue_head(&rxq->rx_queue, skb);
+ break;
+ }
+ __skb_queue_tail(list, skb);
+ }
+ mutex_unlock(&rxq->rx_mutex);
+}
+
+/* Extract the sequence ID */
+static u16 ptp_seqid(const struct ptp_header *ptp_hdr)
+{
+ const __be16 *seqp = &ptp_hdr->sequence_id;
+
+ return be16_to_cpup(seqp);
+}
+
+static u8 ptp_msgid(const struct ptp_header *ptp_hdr)
+{
+ return ptp_hdr->tsmt & 15;
+}
+
+static void marvell_ptp_schedule(struct marvell_ptp *ptp)
+{
+ marvell_tai_schedule(ptp->tai, 0);
+}
+
+/* Check for a rx timestamp entry, try to find the corresponding skb and
+ * deliver it, otherwise add the rx timestamp to the queue of pending
+ * timestamps.
+ */
+static int marvell_ptp_rx_ts(struct marvell_ptp *ptp, int q)
+{
+ struct marvell_ts ts;
+ u16 reg;
+ int err;
+ u64 ns;
+
+ if (q)
+ reg = PTP_ARR_STATUS1;
+ else
+ reg = PTP_ARR_STATUS0;
+
+ err = ptp->ops->ptp_port_read_ts(ptp->dev, &ts, reg);
+ if (err <= 0)
+ return 0;
+
+ if ((ts.stat & MV_STATUS_INTSTATUS_MASK) !=
+ MV_STATUS_INTSTATUS_NORMAL)
+ dev_warn(ptp->dev,
+ "rx timestamp overrun (q=%u stat=0x%x seq=%u)\n",
+ q, ts.stat, ts.seq);
+
+ ns = marvell_tai_cyc2time(ptp->tai, ts.time);
+
+ marvell_rxq_rx_ts(&ptp->rxq[q], ts.seq, ns);
+
+ return 1;
+}
+
+/* Check whether the packet is suitable for timestamping, and if so,
+ * try to find a pending timestamp for it. If no timestamp is found,
+ * queue the packet with a timeout.
+ */
+bool marvell_ptp_rxtstamp(struct marvell_ptp *ptp, struct sk_buff *skb,
+ int type)
+{
+ const struct ptp_header *ptp_hdr;
+ u16 msgidvec, seq;
+ u8 msgid;
+ int q;
+
+ if (ptp->rx_filter == HWTSTAMP_FILTER_NONE)
+ return false;
+
+ ptp_hdr = ptp_parse_header(skb, type);
+ if (!ptp_hdr)
+ return false;
+
+ msgid = ptp_msgid(ptp_hdr);
+ seq = ptp_seqid(ptp_hdr);
+
+ /* Only check for timestamps for PTP packets whose message ID value
+ * is one that we are capturing timestamps for. This is part of the
+ * global configuration and is therefore fixed.
+ */
+ msgidvec = BIT(msgid);
+ if (msgidvec & ~MV_PTP_MSD_ID_TS_EN) {
+ dev_dbg(ptp->dev, "not timestamping rx msgid %u seq %u\n",
+ msgid, seq);
+ return false;
+ }
+
+ /* Determine the queue which the timestamp for this message ID will
+ * appear. This is part of the global configuration and is therefore
+ * fixed.
+ */
+ q = !!(msgidvec & MV_PTP_TS_ARR_PTR);
+
+ if (!marvell_rxq_rxtstamp(&ptp->rxq[q], skb, seq))
+ marvell_ptp_schedule(ptp);
+
+ return true;
+}
+EXPORT_SYMBOL_GPL(marvell_ptp_rxtstamp);
+
+/* Move any expired skbs on to our own list, and then hand the contents of
+ * our list to netif_rx() - this avoids calling netif_rx() with our
+ * mutex held.
+ */
+static void marvell_ptp_rx_expire(struct marvell_ptp *ptp)
+{
+ struct sk_buff_head list;
+ struct sk_buff *skb;
+ int i;
+
+ __skb_queue_head_init(&list);
+
+ for (i = 0; i < ARRAY_SIZE(ptp->rxq); i++)
+ marvell_rxq_expire(&ptp->rxq[i], &list);
+
+ while ((skb = __skb_dequeue(&list)) != NULL)
+ netif_rx(skb);
+}
+
+/* Complete the transmit timestamping; this is called to read the transmit
+ * timestamp from the PHY, and report back the transmitted timestamp.
+ */
+static int marvell_ptp_txtstamp_complete(struct marvell_ptp *ptp)
+{
+ struct skb_shared_hwtstamps shhwtstamps;
+ struct sk_buff *skb = ptp->tx_skb;
+ struct marvell_ts ts;
+ int err;
+ u64 ns;
+
+ err = ptp->ops->ptp_port_read_ts(ptp->dev, &ts, PTP_DEP_STATUS);
+ if (err < 0)
+ goto fail;
+
+ if (err == 0) {
+ if (time_is_before_jiffies(MARVELL_PTP_CB(skb)->timeout)) {
+ dev_warn(ptp->dev, "tx timestamp timeout\n");
+ goto free;
+ }
+ return 0;
+ }
+
+ /* Check the status */
+ if ((ts.stat & MV_STATUS_INTSTATUS_MASK) !=
+ MV_STATUS_INTSTATUS_NORMAL) {
+ dev_warn(ptp->dev, "tx timestamp overrun (stat=0x%x seq=%u)\n",
+ ts.stat, ts.seq);
+ goto free;
+ }
+
+ /* Reject if the sequence number doesn't match */
+ if (ts.seq != MARVELL_PTP_CB(skb)->seq) {
+ dev_warn(ptp->dev, "tx timestamp unexpected sequence id\n");
+ goto free;
+ }
+
+ ptp->tx_skb = NULL;
+
+ /* Set the timestamp */
+ ns = marvell_tai_cyc2time(ptp->tai, ts.time);
+ memset(&shhwtstamps, 0, sizeof(shhwtstamps));
+ shhwtstamps.hwtstamp = ns_to_ktime(ns);
+ skb_complete_tx_timestamp(skb, &shhwtstamps);
+ return 1;
+
+fail:
+ dev_err_ratelimited(ptp->dev, "failed reading PTP: %pe\n",
+ ERR_PTR(err));
+free:
+ dev_kfree_skb_any(skb);
+ ptp->tx_skb = NULL;
+ return -1;
+}
+
+/* Check whether the skb will be timestamped on transmit; we only support
+ * a single outstanding skb. Add it if the slot is available.
+ */
+static bool marvell_ptp_do_txtstamp(struct marvell_ptp *ptp,
+ struct sk_buff *skb, int type)
+{
+ const struct ptp_header *ptp_hdr;
+ u8 msgid;
+
+ if (ptp->tx_type != HWTSTAMP_TX_ON)
+ return false;
+
+ if (!(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP))
+ return false;
+
+ ptp_hdr = ptp_parse_header(skb, type);
+ if (!ptp_hdr)
+ return false;
+
+ msgid = ptp_msgid(ptp_hdr);
+ if (BIT(msgid) & ~MV_PTP_MSD_ID_TS_EN) {
+ dev_dbg(ptp->dev, "not timestamping tx msgid %u seq %u\n",
+ msgid, ptp_seqid(ptp_hdr));
+ return false;
+ }
+
+ MARVELL_PTP_CB(skb)->seq = ptp_seqid(ptp_hdr);
+ MARVELL_PTP_CB(skb)->timeout = jiffies +
+ msecs_to_jiffies(TX_TIMEOUT_MS);
+
+ if (cmpxchg(&ptp->tx_skb, NULL, skb) != NULL)
+ return false;
+
+ /* DP83640 marks the skb for hw timestamping. Since the MAC driver
+ * may call skb_tx_timestamp() but may not support timestamping
+ * itself, it may not set this flag. So, we need to do this here.
+ */
+ skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS;
+ marvell_ptp_schedule(ptp);
+
+ return true;
+}
+
+void marvell_ptp_txtstamp(struct marvell_ptp *ptp, struct sk_buff *skb,
+ int type)
+{
+ if (!marvell_ptp_do_txtstamp(ptp, skb, type))
+ kfree_skb(skb);
+}
+EXPORT_SYMBOL_GPL(marvell_ptp_txtstamp);
+
+int marvell_ptp_hwtstamp(struct marvell_ptp *ptp,
+ struct kernel_hwtstamp_config *kcfg,
+ struct netlink_ext_ack *ack)
+{
+ u16 cfg0 = PTP_PORT_CONFIG_0_DISPTP;
+ u16 cfg2 = 0;
+ int err;
+
+ if (kcfg->flags)
+ return -EINVAL;
+
+ switch (kcfg->tx_type) {
+ case HWTSTAMP_TX_OFF:
+ break;
+
+ case HWTSTAMP_TX_ON:
+ cfg0 = 0;
+ cfg2 |= PTP_PORT_CONFIG_2_DEPINTEN;
+ break;
+
+ default:
+ return -ERANGE;
+ }
+
+ switch (kcfg->rx_filter) {
+ case HWTSTAMP_FILTER_NONE:
+ break;
+
+ case HWTSTAMP_FILTER_PTP_V1_L4_EVENT:
+ case HWTSTAMP_FILTER_PTP_V1_L4_SYNC:
+ case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ:
+ case HWTSTAMP_FILTER_PTP_V2_L4_EVENT:
+ case HWTSTAMP_FILTER_PTP_V2_L4_SYNC:
+ case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ:
+ case HWTSTAMP_FILTER_PTP_V2_L2_EVENT:
+ case HWTSTAMP_FILTER_PTP_V2_L2_SYNC:
+ case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ:
+ case HWTSTAMP_FILTER_PTP_V2_EVENT:
+ case HWTSTAMP_FILTER_PTP_V2_SYNC:
+ case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ:
+ /* We accept 802.1AS, IEEE 1588v1 and IEEE 1588v2. We could
+ * filter on 802.1AS using the transportSpecific field, but
+ * that affects the transmit path too.
+ */
+ kcfg->rx_filter = HWTSTAMP_FILTER_SOME;
+ cfg0 = 0;
+ cfg2 |= PTP_PORT_CONFIG_2_ARRINTEN;
+ break;
+
+ default:
+ return -ERANGE;
+ }
+
+ err = ptp->ops->ptp_port_modify(ptp->dev, PTP_PORT_CONFIG_0,
+ PTP_PORT_CONFIG_0_DISPTP, cfg0);
+ if (err)
+ return err;
+
+ err = ptp->ops->ptp_port_write(ptp->dev, PTP_PORT_CONFIG_2, cfg2);
+ if (err)
+ return err;
+
+ ptp->tx_type = kcfg->tx_type;
+ ptp->rx_filter = kcfg->rx_filter;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(marvell_ptp_hwtstamp);
+
+int marvell_ptp_ts_info(struct marvell_ptp *ptp,
+ struct kernel_ethtool_ts_info *ts_info)
+{
+ ts_info->so_timestamping = SOF_TIMESTAMPING_TX_HARDWARE |
+ SOF_TIMESTAMPING_RX_HARDWARE |
+ SOF_TIMESTAMPING_RAW_HARDWARE;
+ ts_info->phc_index = marvell_tai_ptp_clock_index(ptp->tai);
+ ts_info->tx_types = BIT(HWTSTAMP_TX_OFF) |
+ BIT(HWTSTAMP_TX_ON);
+ ts_info->rx_filters = BIT(HWTSTAMP_FILTER_NONE) |
+ BIT(HWTSTAMP_FILTER_SOME);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(marvell_ptp_ts_info);
+
+static int marvell_ptp_port_config(struct marvell_ptp *ptp)
+{
+ int err;
+
+ /* Disable transport specific check (if the PTP common header)
+ * Disable timestamp overwriting (so we can read a stable entry.)
+ * Disable PTP
+ */
+ err = ptp->ops->ptp_port_write(ptp->dev, PTP_PORT_CONFIG_0,
+ PTP_PORT_CONFIG_0_DISTSPECCHECK |
+ PTP_PORT_CONFIG_0_DISTSOVERWRITE |
+ PTP_PORT_CONFIG_0_DISPTP);
+ if (err < 0)
+ return err;
+
+ /* Set ether-type jump to 12 (to ether protocol)
+ * Set IP jump to 2 (to skip over ether protocol)
+ * Does this mean it won't pick up on VLAN packets?
+ */
+ err = ptp->ops->ptp_port_write(ptp->dev, PTP_PORT_CONFIG_1,
+ PTP_PORT_CONFIG_1_ETJUMP(12) |
+ PTP_PORT_CONFIG_1_IPJUMP(2));
+ if (err < 0)
+ return err;
+
+ /* Disable all interrupts */
+ ptp->ops->ptp_port_write(ptp->dev, PTP_PORT_CONFIG_2, 0);
+
+ return 0;
+}
+
+static void marvell_ptp_port_disable(struct marvell_ptp *ptp)
+{
+ /* Disable PTP */
+ ptp->ops->ptp_port_write(ptp->dev, PTP_PORT_CONFIG_0,
+ PTP_PORT_CONFIG_0_DISPTP);
+
+ /* Disable interrupts */
+ ptp->ops->ptp_port_write(ptp->dev, PTP_PORT_CONFIG_2, 0);
+}
+
+long marvell_ptp_aux_work(struct marvell_ptp *ptp)
+{
+ if (ptp->tx_skb)
+ marvell_ptp_txtstamp_complete(ptp);
+
+ marvell_ptp_rx_ts(ptp, 0);
+ marvell_ptp_rx_ts(ptp, 1);
+ marvell_ptp_rx_expire(ptp);
+
+ if (ptp->tx_skb)
+ return 0;
+ else if (!skb_queue_empty(&ptp->rxq[0].rx_queue) ||
+ !skb_queue_empty(&ptp->rxq[1].rx_queue))
+ return 1;
+
+ return -1;
+}
+EXPORT_SYMBOL_GPL(marvell_ptp_aux_work);
+
+irqreturn_t marvell_ptp_irq(struct marvell_ptp *ptp)
+{
+ irqreturn_t ret = IRQ_NONE;
+
+ if (marvell_ptp_rx_ts(ptp, 0))
+ ret = IRQ_HANDLED;
+
+ if (marvell_ptp_rx_ts(ptp, 1))
+ ret = IRQ_HANDLED;
+
+ if (ptp->tx_skb && marvell_ptp_txtstamp_complete(ptp))
+ ret = IRQ_HANDLED;
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(marvell_ptp_irq);
+
+int marvell_ptp_probe(struct marvell_ptp *ptp, struct device *dev,
+ struct marvell_tai *tai,
+ const struct marvell_ptp_ops *ops)
+{
+ int i;
+
+ ptp->ops = ops;
+ ptp->dev = dev;
+ ptp->tai = tai;
+
+ for (i = 0; i < ARRAY_SIZE(ptp->rxq); i++)
+ marvell_rxq_init(&ptp->rxq[i]);
+
+ /* Configure this PTP port */
+ return marvell_ptp_port_config(ptp);
+}
+EXPORT_SYMBOL_GPL(marvell_ptp_probe);
+
+void marvell_ptp_remove(struct marvell_ptp *ptp)
+{
+ int i;
+
+ /* Free or dequeue all pending skbs */
+ if (ptp->tx_skb)
+ kfree_skb(ptp->tx_skb);
+
+ for (i = 0; i < ARRAY_SIZE(ptp->rxq); i++)
+ marvell_rxq_purge(&ptp->rxq[i]);
+
+ /* Ensure that the port is disabled */
+ marvell_ptp_port_disable(ptp);
+}
+EXPORT_SYMBOL_GPL(marvell_ptp_remove);
+
+MODULE_AUTHOR("Russell King");
+MODULE_DESCRIPTION("Marvell PTP library");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/marvell_ptp.h b/include/linux/marvell_ptp.h
new file mode 100644
index 000000000000..6e515648abaa
--- /dev/null
+++ b/include/linux/marvell_ptp.h
@@ -0,0 +1,129 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef LINUX_MARVELL_PTP_H
+#define LINUX_MARVELL_PTP_H
+
+#include <linux/irqreturn.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/net_tstamp.h>
+#include <linux/ptp_classify.h>
+#include <linux/ptp_clock_kernel.h>
+#include <linux/skbuff.h>
+#include <linux/timecounter.h>
+
+struct device;
+struct ifreq;
+struct kernel_ethtool_ts_info;
+struct marvell_tai;
+struct netlink_ext_ack;
+
+#define MV_PTP_MSGTYPE_DELAY_RESP 9
+
+/* This defines which incoming or outgoing PTP frames are timestampped */
+#define MV_PTP_MSD_ID_TS_EN (BIT(PTP_MSGTYPE_SYNC) | \
+ BIT(PTP_MSGTYPE_DELAY_REQ) | \
+ BIT(MV_PTP_MSGTYPE_DELAY_RESP))
+/* Direct Sync messages to Arr0 and delay messages to Arr1 */
+#define MV_PTP_TS_ARR_PTR (BIT(PTP_MSGTYPE_DELAY_REQ) | \
+ BIT(MV_PTP_MSGTYPE_DELAY_RESP))
+
+struct marvell_extts {
+ u32 time;
+ u8 status;
+#define MV_STATUS_EVENTCAPVALID BIT(8)
+};
+
+struct marvell_ts {
+ u32 time;
+ u16 stat;
+#define MV_STATUS_INTSTATUS_MASK 0x0006
+#define MV_STATUS_INTSTATUS_NORMAL 0x0000
+#define MV_STATUS_VALID BIT(0)
+ u16 seq;
+};
+
+struct marvell_ptp_ops {
+ int (*tai_enable)(struct device *dev);
+ u64 (*tai_clock_read)(struct device *dev,
+ struct ptp_system_timestamp *sts);
+ int (*tai_extts_read)(struct device *dev, int reg,
+ struct marvell_extts *extts);
+ int (*tai_pin_verify)(struct device *dev, int pin,
+ enum ptp_pin_function func, unsigned int chan);
+ int (*tai_pin_setup)(struct device *dev, int pin, unsigned int flags,
+ int enable);
+ int (*tai_write)(struct device *dev, u8 reg, u16 val);
+ int (*tai_modify)(struct device *dev, u8 reg, u16 mask, u16 val);
+ int (*ptp_global_write)(struct device *dev, u8 reg, u16 val);
+ int (*ptp_port_read_ts)(struct device *dev, struct marvell_ts *ts,
+ u8 reg);
+ int (*ptp_port_write)(struct device *dev, u8 reg, u16 val);
+ int (*ptp_port_modify)(struct device *dev, u8 reg, u16 mask, u16 val);
+ long (*ptp_aux_work)(struct device *dev);
+};
+
+/* TAI module */
+struct marvell_tai_param {
+ u32 cc_mult_num;
+ u32 cc_mult_den;
+ u32 cc_mult;
+ int cc_shift;
+
+ int n_ext_ts;
+};
+
+u64 marvell_tai_cyc2time(struct marvell_tai *tai, u32 cyc);
+int marvell_tai_ptp_clock_index(struct marvell_tai *tai);
+int marvell_tai_schedule(struct marvell_tai *tai, unsigned long delay);
+int marvell_tai_probe(struct marvell_tai **taip,
+ const struct marvell_ptp_ops *ops,
+ const struct marvell_tai_param *param,
+ struct ptp_pin_desc *pin_config, int n_pins,
+ const char *name, struct device *dev);
+void marvell_tai_remove(struct marvell_tai *tai);
+
+/* Timestamping module */
+struct marvell_rxts {
+ struct list_head node;
+ u64 ns;
+ u16 seq;
+};
+
+struct marvell_rxq {
+ struct mutex rx_mutex;
+ struct list_head rx_free;
+ struct list_head rx_pend;
+ struct sk_buff_head rx_queue;
+ struct marvell_rxts rx_ts[64];
+};
+
+struct marvell_ptp {
+ struct marvell_tai *tai;
+ const struct marvell_ptp_ops *ops;
+ struct device *dev;
+
+ /* We only support one outstanding transmit skb */
+ struct sk_buff *tx_skb;
+ enum hwtstamp_tx_types tx_type;
+
+ struct marvell_rxq rxq[2];
+ enum hwtstamp_rx_filters rx_filter;
+};
+
+bool marvell_ptp_rxtstamp(struct marvell_ptp *ptp, struct sk_buff *skb,
+ int type);
+void marvell_ptp_txtstamp(struct marvell_ptp *ptp, struct sk_buff *skb,
+ int type);
+int marvell_ptp_hwtstamp(struct marvell_ptp *ptp,
+ struct kernel_hwtstamp_config *kcfg,
+ struct netlink_ext_ack *ack);
+int marvell_ptp_ts_info(struct marvell_ptp *ptp,
+ struct kernel_ethtool_ts_info *ts_info);
+long marvell_ptp_aux_work(struct marvell_ptp *ptp);
+irqreturn_t marvell_ptp_irq(struct marvell_ptp *ptp);
+int marvell_ptp_probe(struct marvell_ptp *ptp, struct device *dev,
+ struct marvell_tai *tai,
+ const struct marvell_ptp_ops *ops);
+void marvell_ptp_remove(struct marvell_ptp *ptp);
+
+#endif
--
2.30.2
^ permalink raw reply related [flat|nested] 28+ messages in thread
* [PATCH RFC net-next 3/5] net: phy: add Marvell PHY PTP support
2025-04-11 21:26 [PATCH RFC net-next 0/5] Marvell PTP support Russell King (Oracle)
2025-04-11 21:26 ` [PATCH RFC net-next 1/5] net: mvpp2: add support for hardware timestamps Russell King
2025-04-11 21:26 ` [PATCH RFC net-next 2/5] ptp: marvell: add core support for Marvell PTP v2.1 Russell King
@ 2025-04-11 21:26 ` Russell King
2025-04-14 12:33 ` Kory Maincent
2025-04-14 14:43 ` Kory Maincent
2025-04-11 21:26 ` [PATCH RFC net-next 4/5] mv88e6xxx: convert to marvell TAI Russell King (Oracle)
` (3 subsequent siblings)
6 siblings, 2 replies; 28+ messages in thread
From: Russell King @ 2025-04-11 21:26 UTC (permalink / raw)
To: Andrew Lunn, Heiner Kallweit
Cc: David S. Miller, Eric Dumazet, Jakub Kicinski, Kory Maincent,
netdev, Paolo Abeni, Richard Cochran
Add PTP basic support for Marvell 88E151x single port PHYs. These
PHYs support timestamping the egress and ingress of packets, but does
not support any packet modification, nor do we support any filtering
beyond selecting packets that the hardware recognises as PTP/802.1AS.
The PHYs support hardware pins for providing an external clock for the
TAI counter, and a separate pin that can be used for event capture or
generation of a trigger (either a pulse or periodic). Only event
capture is supported.
We currently use a delayed work to poll for the timestamps which is
far from ideal, but we also provide a function that can be called from
an interrupt handler - which would be good to tie into the main Marvell
PHY driver.
The driver takes inspiration from the Marvell 88E6xxx DSA and DP83640
drivers. The hardware is very similar to the implementation found in
the 88E6xxx DSA driver, but the access methods are very different,
although it may be possible to create a library that both can use
along with accessor functions.
Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
---
drivers/net/phy/Kconfig | 13 ++
drivers/net/phy/Makefile | 1 +
drivers/net/phy/marvell.c | 21 ++-
drivers/net/phy/marvell_ptp.c | 307 ++++++++++++++++++++++++++++++++++
drivers/net/phy/marvell_ptp.h | 21 +++
5 files changed, 362 insertions(+), 1 deletion(-)
create mode 100644 drivers/net/phy/marvell_ptp.c
create mode 100644 drivers/net/phy/marvell_ptp.h
diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig
index d29f9f7fd2e1..fb8b326f5c7e 100644
--- a/drivers/net/phy/Kconfig
+++ b/drivers/net/phy/Kconfig
@@ -240,6 +240,19 @@ config MARVELL_PHY
help
Currently has a driver for the 88E1XXX
+config MARVELL_PHY_PTP
+ bool "Marvell PHY PTP support"
+ depends on NETWORK_PHY_TIMESTAMPING
+ depends on (PTP_1588_CLOCK = y && MARVELL_PHY = y) || \
+ (PTP_1588_CLOCK && MARVELL_PHY = m)
+ select PTP_1588_CLOCK_MARVELL
+ help
+ Support PHY timestamping on Marvell 88E1510, 88E1512, 88E1514
+ and 88E1518 PHYs.
+
+ N.B. In order for this to be fully functional, your MAC driver
+ must call the skb_tx_timestamp() function.
+
config MARVELL_10G_PHY
tristate "Marvell Alaska 10Gbit PHYs"
help
diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile
index 23ce205ae91d..9d513a18afb6 100644
--- a/drivers/net/phy/Makefile
+++ b/drivers/net/phy/Makefile
@@ -70,6 +70,7 @@ obj-$(CONFIG_ICPLUS_PHY) += icplus.o
obj-$(CONFIG_INTEL_XWAY_PHY) += intel-xway.o
obj-$(CONFIG_LSI_ET1011C_PHY) += et1011c.o
obj-$(CONFIG_LXT_PHY) += lxt.o
+obj-$(CONFIG_MARVELL_PHY_PTP) += marvell_ptp.o
obj-$(CONFIG_MARVELL_10G_PHY) += marvell10g.o
obj-$(CONFIG_MARVELL_PHY) += marvell.o
obj-$(CONFIG_MARVELL_88Q2XXX_PHY) += marvell-88q2xxx.o
diff --git a/drivers/net/phy/marvell.c b/drivers/net/phy/marvell.c
index 623292948fa7..1e9a4b300216 100644
--- a/drivers/net/phy/marvell.c
+++ b/drivers/net/phy/marvell.c
@@ -38,6 +38,8 @@
#include <asm/irq.h>
#include <linux/uaccess.h>
+#include "marvell_ptp.h"
+
#define MII_MARVELL_PHY_PAGE 22
#define MII_MARVELL_COPPER_PAGE 0x00
#define MII_MARVELL_FIBER_PAGE 0x01
@@ -3647,7 +3649,7 @@ static const struct sfp_upstream_ops m88e1510_sfp_ops = {
.disconnect_phy = phy_sfp_disconnect_phy,
};
-static int m88e1510_probe(struct phy_device *phydev)
+static int m88e15xx_probe(struct phy_device *phydev)
{
int err;
@@ -3658,6 +3660,22 @@ static int m88e1510_probe(struct phy_device *phydev)
return phy_sfp_probe(phydev, &m88e1510_sfp_ops);
}
+static int m88e1510_probe(struct phy_device *phydev)
+{
+ int err;
+
+ err = m88e15xx_probe(phydev);
+ if (err)
+ return err;
+
+ return marvell_phy_ptp_probe(phydev);
+}
+
+static void m88e1510_remove(struct phy_device *phydev)
+{
+ marvell_phy_ptp_remove(phydev);
+}
+
static struct phy_driver marvell_drivers[] = {
{
.phy_id = MARVELL_PHY_ID_88E1101,
@@ -3916,6 +3934,7 @@ static struct phy_driver marvell_drivers[] = {
.features = PHY_GBIT_FIBRE_FEATURES,
.flags = PHY_POLL_CABLE_TEST,
.probe = m88e1510_probe,
+ .remove = m88e1510_remove,
.config_init = m88e1510_config_init,
.config_aneg = m88e1510_config_aneg,
.read_status = marvell_read_status,
diff --git a/drivers/net/phy/marvell_ptp.c b/drivers/net/phy/marvell_ptp.c
new file mode 100644
index 000000000000..3ba71c44ffb0
--- /dev/null
+++ b/drivers/net/phy/marvell_ptp.c
@@ -0,0 +1,307 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Marvell PTP driver for 88E1510, 88E1512, 88E1514 and 88E1518 PHYs
+ *
+ * Ideas taken from 88E6xxx DSA and DP83640 drivers. This file
+ * implements the packet timestamping support only (PTP). TAI
+ * support is separate.
+ */
+#include <linux/marvell_ptp.h>
+#include <linux/netdevice.h>
+#include <linux/phy.h>
+
+#include "marvell_ptp.h"
+
+#define MARVELL_PAGE_MISC 6
+#define GCR 20
+#define GCR_PTP_POWER_DOWN BIT(9)
+#define GCR_PTP_REF_CLOCK_SOURCE BIT(8)
+#define GCR_PTP_INPUT_SOURCE BIT(7)
+#define GCR_PTP_OUTPUT BIT(6)
+
+#define MARVELL_PAGE_PTP_PORT_1 8
+
+#define MARVELL_PAGE_TAI_GLOBAL 12
+#define MARVELL_PAGE_PTP_GLOBAL 14
+#define PTPG_READPLUS_COMMAND 14
+#define PTPG_READPLUS_DATA 15
+
+struct marvell_phy_ptp {
+ struct marvell_ptp ptp;
+ struct mii_timestamper mii_ts;
+};
+
+static struct marvell_phy_ptp *mii_ts_to_phy_ptp(struct mii_timestamper *mii_ts)
+{
+ return container_of(mii_ts, struct marvell_phy_ptp, mii_ts);
+}
+
+static bool marvell_phy_ptp_rxtstamp(struct mii_timestamper *mii_ts,
+ struct sk_buff *skb, int type)
+{
+ struct marvell_phy_ptp *phy_ptp = mii_ts_to_phy_ptp(mii_ts);
+
+ return marvell_ptp_rxtstamp(&phy_ptp->ptp, skb, type);
+}
+
+static void marvell_phy_ptp_txtstamp(struct mii_timestamper *mii_ts,
+ struct sk_buff *skb, int type)
+{
+ struct marvell_phy_ptp *phy_ptp = mii_ts_to_phy_ptp(mii_ts);
+
+ return marvell_ptp_txtstamp(&phy_ptp->ptp, skb, type);
+}
+
+static int marvell_phy_ptp_hwtstamp(struct mii_timestamper *mii_ts,
+ struct kernel_hwtstamp_config *kcfg,
+ struct netlink_ext_ack *ack)
+{
+ struct marvell_phy_ptp *phy_ptp = mii_ts_to_phy_ptp(mii_ts);
+
+ return marvell_ptp_hwtstamp(&phy_ptp->ptp, kcfg, ack);
+}
+
+static int marvell_phy_ptp_ts_info(struct mii_timestamper *mii_ts,
+ struct kernel_ethtool_ts_info *ts_info)
+{
+ struct marvell_phy_ptp *phy_ptp = mii_ts_to_phy_ptp(mii_ts);
+
+ return marvell_ptp_ts_info(&phy_ptp->ptp, ts_info);
+}
+
+/* TAI accessor functions */
+static int marvell_phy_tai_enable(struct device *dev)
+{
+ struct phy_device *phydev = to_phy_device(dev);
+
+ return phy_modify_paged(phydev, MARVELL_PAGE_MISC, GCR,
+ GCR_PTP_POWER_DOWN, 0);
+}
+
+static u64 marvell_phy_tai_clock_read(struct device *dev,
+ struct ptp_system_timestamp *sts)
+{
+ struct phy_device *phydev = to_phy_device(dev);
+ int err, oldpage, lo, hi;
+
+ oldpage = phy_select_page(phydev, MARVELL_PAGE_PTP_GLOBAL);
+ if (oldpage >= 0) {
+ /* 88e151x says to write 0x8e0e */
+ ptp_read_system_prets(sts);
+ err = __phy_write(phydev, PTPG_READPLUS_COMMAND, 0x8e0e);
+ ptp_read_system_postts(sts);
+ lo = __phy_read(phydev, PTPG_READPLUS_DATA);
+ hi = __phy_read(phydev, PTPG_READPLUS_DATA);
+ }
+ err = phy_restore_page(phydev, oldpage, err);
+
+ if (err || lo < 0 || hi < 0)
+ return 0;
+
+ return lo | hi << 16;
+}
+
+static int marvell_phy_tai_write(struct device *dev, u8 reg, u16 val)
+{
+ struct phy_device *phydev = to_phy_device(dev);
+
+ return phy_write_paged(phydev, MARVELL_PAGE_TAI_GLOBAL, reg, val);
+}
+
+static int marvell_phy_tai_modify(struct device *dev, u8 reg, u16 mask, u16 val)
+{
+ struct phy_device *phydev = to_phy_device(dev);
+
+ return phy_modify_paged(phydev, MARVELL_PAGE_TAI_GLOBAL,
+ reg, mask, val);
+}
+
+static int marvell_phy_ptp_global_write(struct device *dev, u8 reg, u16 val)
+{
+ struct phy_device *phydev = to_phy_device(dev);
+
+ return phy_write_paged(phydev, MARVELL_PAGE_PTP_GLOBAL, reg, val);
+}
+
+/* Read the status, timestamp and PTP common header sequence from the PHY.
+ * Apparently, reading these are atomic, but there is no mention how the
+ * PHY treats this access as atomic. So, we set the DisTSOverwrite bit
+ * when configuring the PHY.
+ */
+static int marvell_phy_ptp_port_read_ts(struct device *dev,
+ struct marvell_ts *ts, u8 reg)
+{
+ struct phy_device *phydev = to_phy_device(dev);
+ int oldpage, page = MARVELL_PAGE_PTP_PORT_1 + (reg >> 4);
+ int ret;
+
+ reg &= 15;
+
+ /* Read status register */
+ oldpage = phy_select_page(phydev, page);
+ if (oldpage >= 0) {
+ ret = __phy_read(phydev, reg);
+ if (ret < 0)
+ goto restore;
+
+ ts->stat = ret;
+ if (!(ts->stat & MV_STATUS_VALID)) {
+ ret = 0;
+ goto restore;
+ }
+
+ /* Read low timestamp */
+ ret = __phy_read(phydev, reg + 1);
+ if (ret < 0)
+ goto restore;
+
+ ts->time = ret;
+
+ /* Read high timestamp */
+ ret = __phy_read(phydev, reg + 2);
+ if (ret < 0)
+ goto restore;
+
+ ts->time |= ret << 16;
+
+ /* Read sequence */
+ ret = __phy_read(phydev, reg + 3);
+ if (ret < 0)
+ goto restore;
+
+ ts->seq = ret;
+
+ /* Clear valid */
+ __phy_write(phydev, reg, 0);
+
+ ret = 1;
+ }
+restore:
+ return phy_restore_page(phydev, oldpage, ret);
+}
+
+static int marvell_phy_ptp_port_write(struct device *dev, u8 reg, u16 val)
+{
+ struct phy_device *phydev = to_phy_device(dev);
+
+ return phy_write_paged(phydev, MARVELL_PAGE_PTP_PORT_1 + (reg >> 4),
+ reg & 15, val);
+}
+
+static int marvell_phy_ptp_port_modify(struct device *dev, u8 reg, u16 mask,
+ u16 val)
+{
+ struct phy_device *phydev = to_phy_device(dev);
+
+ return phy_modify_paged(phydev, MARVELL_PAGE_PTP_PORT_1 + (reg >> 4),
+ reg & 15, mask, val);
+}
+
+static long marvell_phy_ptp_aux_work(struct device *dev)
+{
+ struct phy_device *phydev = to_phy_device(dev);
+ struct marvell_phy_ptp *phy_ptp;
+
+ phy_ptp = mii_ts_to_phy_ptp(phydev->mii_ts);
+
+ return marvell_ptp_aux_work(&phy_ptp->ptp);
+}
+
+static const struct marvell_ptp_ops marvell_phy_ptp_ops = {
+ .tai_enable = marvell_phy_tai_enable,
+ .tai_clock_read = marvell_phy_tai_clock_read,
+ .tai_write = marvell_phy_tai_write,
+ .tai_modify = marvell_phy_tai_modify,
+ .ptp_global_write = marvell_phy_ptp_global_write,
+ .ptp_port_read_ts = marvell_phy_ptp_port_read_ts,
+ .ptp_port_write = marvell_phy_ptp_port_write,
+ .ptp_port_modify = marvell_phy_ptp_port_modify,
+ .ptp_aux_work = marvell_phy_ptp_aux_work,
+};
+
+static const struct marvell_tai_param marvell_phy_tai_param = {
+ /* This assumes a 125MHz clock */
+ .cc_mult_num = 1 << 9,
+ .cc_mult_den = 15625U,
+ .cc_mult = 8 << 28,
+ .cc_shift = 28,
+};
+
+/* This function should be called from the PHY threaded interrupt
+ * handler to process any stored timestamps in a timely manner.
+ * The presence of an interrupt has an effect on how quickly a
+ * timestamp requiring received packet will be processed.
+ */
+irqreturn_t marvell_phy_ptp_irq(struct phy_device *phydev)
+{
+ struct marvell_phy_ptp *phy_ptp;
+
+ if (!phydev->mii_ts)
+ return IRQ_NONE;
+
+ phy_ptp = mii_ts_to_phy_ptp(phydev->mii_ts);
+
+ return marvell_ptp_irq(&phy_ptp->ptp);
+}
+EXPORT_SYMBOL_GPL(marvell_phy_ptp_irq);
+
+int marvell_phy_ptp_probe(struct phy_device *phydev)
+{
+ struct marvell_phy_ptp *phy_ptp;
+ struct marvell_tai *tai;
+ struct device *dev;
+ int err;
+
+ dev = &phydev->mdio.dev;
+
+ phy_ptp = devm_kzalloc(dev, sizeof(*phy_ptp), GFP_KERNEL);
+ if (!phy_ptp)
+ return -ENOMEM;
+
+ phy_ptp->mii_ts.rxtstamp = marvell_phy_ptp_rxtstamp;
+ phy_ptp->mii_ts.txtstamp = marvell_phy_ptp_txtstamp;
+ phy_ptp->mii_ts.hwtstamp = marvell_phy_ptp_hwtstamp;
+ phy_ptp->mii_ts.ts_info = marvell_phy_ptp_ts_info;
+
+ /* Get the TAI for this PHY. */
+ err = marvell_tai_probe(&tai, &marvell_phy_ptp_ops,
+ &marvell_phy_tai_param,
+ "Marvell PHY", dev);
+ if (err)
+ return err;
+
+ err = marvell_ptp_probe(&phy_ptp->ptp, dev, tai,
+ &marvell_phy_ptp_ops);
+ if (err) {
+ marvell_tai_remove(tai);
+ return err;
+ }
+
+ phydev->mii_ts = &phy_ptp->mii_ts;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(marvell_phy_ptp_probe);
+
+void marvell_phy_ptp_remove(struct phy_device *phydev)
+{
+ struct marvell_phy_ptp *phy_ptp;
+ struct mii_timestamper *mii_ts;
+
+ /* Disconnect from the net subsystem - we assume there is no
+ * packet activity at this point.
+ */
+ mii_ts = phydev->mii_ts;
+ phydev->mii_ts = NULL;
+
+ if (mii_ts) {
+ phy_ptp = mii_ts_to_phy_ptp(mii_ts);
+ marvell_ptp_remove(&phy_ptp->ptp);
+ marvell_tai_remove(phy_ptp->ptp.tai);
+ }
+}
+EXPORT_SYMBOL_GPL(marvell_phy_ptp_remove);
+
+MODULE_AUTHOR("Russell King");
+MODULE_DESCRIPTION("Marvell PHY PTP library");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/net/phy/marvell_ptp.h b/drivers/net/phy/marvell_ptp.h
new file mode 100644
index 000000000000..7d009fe4fd23
--- /dev/null
+++ b/drivers/net/phy/marvell_ptp.h
@@ -0,0 +1,21 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef MARVELL_PTP_H
+#define MARVELL_PTP_H
+
+#if IS_ENABLED(CONFIG_MARVELL_PHY_PTP)
+irqreturn_t marvell_phy_ptp_irq(struct phy_device *phydev);
+int marvell_phy_ptp_probe(struct phy_device *phydev);
+void marvell_phy_ptp_remove(struct phy_device *phydev);
+#else
+static inline int marvell_phy_ptp_dummy_probe(void)
+{
+ return 0;
+}
+#define marvell_phy_ptp_probe(x...) marvell_phy_ptp_dummy_probe()
+
+static inline void marvell_phy_ptp_remove(struct phy_device *phydev)
+{
+}
+#endif
+
+#endif
--
2.30.2
^ permalink raw reply related [flat|nested] 28+ messages in thread
* [PATCH RFC net-next 4/5] mv88e6xxx: convert to marvell TAI
2025-04-11 21:26 [PATCH RFC net-next 0/5] Marvell PTP support Russell King (Oracle)
` (2 preceding siblings ...)
2025-04-11 21:26 ` [PATCH RFC net-next 3/5] net: phy: add Marvell PHY PTP support Russell King
@ 2025-04-11 21:26 ` Russell King (Oracle)
2025-04-11 21:26 ` [PATCH RFC net-next 5/5] mv88e6xxx: cleanup ptp tai Russell King (Oracle)
` (2 subsequent siblings)
6 siblings, 0 replies; 28+ messages in thread
From: Russell King (Oracle) @ 2025-04-11 21:26 UTC (permalink / raw)
To: Andrew Lunn, Heiner Kallweit
Cc: David S. Miller, Eric Dumazet, Jakub Kicinski, Kory Maincent,
netdev, Paolo Abeni, Richard Cochran, Vladimir Oltean
---
drivers/net/dsa/mv88e6xxx/Kconfig | 1 +
drivers/net/dsa/mv88e6xxx/chip.h | 22 +-
drivers/net/dsa/mv88e6xxx/hwtstamp.c | 23 +-
drivers/net/dsa/mv88e6xxx/hwtstamp.h | 1 +
drivers/net/dsa/mv88e6xxx/ptp.c | 354 ++++++++++++++++++++++++---
drivers/net/dsa/mv88e6xxx/ptp.h | 1 -
6 files changed, 361 insertions(+), 41 deletions(-)
diff --git a/drivers/net/dsa/mv88e6xxx/Kconfig b/drivers/net/dsa/mv88e6xxx/Kconfig
index 64ae3882d17c..595ca6cd6075 100644
--- a/drivers/net/dsa/mv88e6xxx/Kconfig
+++ b/drivers/net/dsa/mv88e6xxx/Kconfig
@@ -14,6 +14,7 @@ config NET_DSA_MV88E6XXX_PTP
default n
depends on (NET_DSA_MV88E6XXX = y && PTP_1588_CLOCK = y) || \
(NET_DSA_MV88E6XXX = m && PTP_1588_CLOCK)
+ select PTP_1588_CLOCK_MARVELL
help
Say Y to enable PTP hardware timestamping on Marvell 88E6xxx switch
chips that support it.
diff --git a/drivers/net/dsa/mv88e6xxx/chip.h b/drivers/net/dsa/mv88e6xxx/chip.h
index 86bf113c9bfa..6fb7d0fa0180 100644
--- a/drivers/net/dsa/mv88e6xxx/chip.h
+++ b/drivers/net/dsa/mv88e6xxx/chip.h
@@ -7,13 +7,14 @@
#ifndef _MV88E6XXX_CHIP_H
#define _MV88E6XXX_CHIP_H
-
+#define USE_MARVELL_TAI
#include <linux/idr.h>
#include <linux/if_vlan.h>
#include <linux/irq.h>
#include <linux/gpio/consumer.h>
#include <linux/kthread.h>
#include <linux/leds.h>
+#include <linux/marvell_ptp.h>
#include <linux/phy.h>
#include <linux/property.h>
#include <linux/ptp_clock_kernel.h>
@@ -412,20 +413,29 @@ struct mv88e6xxx_chip {
/* GPIO resources */
u8 gpio_data[2];
+#ifdef USE_MARVELL_TAI
+ struct marvell_tai *tai;
+#else
+
/* This cyclecounter abstracts the switch PTP time.
* reg_lock must be held for any operation that read()s.
*/
struct cyclecounter tstamp_cc;
struct timecounter tstamp_tc;
struct delayed_work overflow_work;
+#endif
const struct mv88e6xxx_cc_coeffs *cc_coeffs;
+#ifndef USE_MARVELL_TAI
struct ptp_clock *ptp_clock;
struct ptp_clock_info ptp_clock_info;
struct delayed_work tai_event_work;
+#endif
struct ptp_pin_desc pin_config[MV88E6XXX_MAX_GPIO];
+#ifndef USE_MARVELL_TAI
u16 trig_config;
u16 evcap_config;
+#endif
u16 enable_count;
/* Current ingress and egress monitor ports */
@@ -732,10 +742,12 @@ struct mv88e6xxx_avb_ops {
};
struct mv88e6xxx_ptp_ops {
- u64 (*clock_read)(const struct cyclecounter *cc);
- int (*ptp_enable)(struct ptp_clock_info *ptp,
- struct ptp_clock_request *rq, int on);
- int (*ptp_verify)(struct ptp_clock_info *ptp, unsigned int pin,
+ u64 (*clock_read)(struct mv88e6xxx_chip *chip);
+ int (*ptp_enable_extts)(struct mv88e6xxx_chip *chip,
+ struct ptp_clock_request *rq, int on);
+ int (*ptp_pin_setup)(struct mv88e6xxx_chip *chip, int pin,
+ unsigned int flags, int enable);
+ int (*ptp_verify)(struct mv88e6xxx_chip *chip, unsigned int pin,
enum ptp_pin_function func, unsigned int chan);
void (*event_work)(struct work_struct *ugly);
int (*port_enable)(struct mv88e6xxx_chip *chip, int port);
diff --git a/drivers/net/dsa/mv88e6xxx/hwtstamp.c b/drivers/net/dsa/mv88e6xxx/hwtstamp.c
index 49e6e1355142..232deff1b2ba 100644
--- a/drivers/net/dsa/mv88e6xxx/hwtstamp.c
+++ b/drivers/net/dsa/mv88e6xxx/hwtstamp.c
@@ -79,7 +79,11 @@ int mv88e6xxx_get_ts_info(struct dsa_switch *ds, int port,
SOF_TIMESTAMPING_TX_HARDWARE |
SOF_TIMESTAMPING_RX_HARDWARE |
SOF_TIMESTAMPING_RAW_HARDWARE;
+#ifdef USE_MARVELL_TAI
+ info->phc_index = marvell_tai_ptp_clock_index(chip->tai);
+#else
info->phc_index = ptp_clock_index(chip->ptp_clock);
+#endif
info->tx_types =
(1 << HWTSTAMP_TX_OFF) |
(1 << HWTSTAMP_TX_ON);
@@ -293,9 +297,13 @@ static void mv88e6xxx_get_rxts(struct mv88e6xxx_chip *chip,
if (mv88e6xxx_ts_valid(status) && seq_match(skb, seq_id)) {
ns = timehi << 16 | timelo;
+#ifdef USE_MARVELL_TAI
+ ns = marvell_tai_cyc2time(chip->tai, ns);
+#else
mv88e6xxx_reg_lock(chip);
ns = timecounter_cyc2time(&chip->tstamp_tc, ns);
mv88e6xxx_reg_unlock(chip);
+#endif
shwt = skb_hwtstamps(skb);
memset(shwt, 0, sizeof(*shwt));
shwt->hwtstamp = ns_to_ktime(ns);
@@ -352,7 +360,11 @@ bool mv88e6xxx_port_rxtstamp(struct dsa_switch *ds, int port,
else
skb_queue_tail(&ps->rx_queue, skb);
+#ifdef USE_MARVELL_TAI
+ marvell_tai_schedule(chip->tai, 0);
+#else
ptp_schedule_worker(chip->ptp_clock, 0);
+#endif
return true;
}
@@ -413,9 +425,13 @@ static int mv88e6xxx_txtstamp_work(struct mv88e6xxx_chip *chip,
memset(&shhwtstamps, 0, sizeof(shhwtstamps));
time_raw = ((u32)departure_block[2] << 16) | departure_block[1];
+#ifdef USE_MARVELL_TAI
+ ns = marvell_tai_cyc2time(chip->tai, time_raw);
+#else
mv88e6xxx_reg_lock(chip);
ns = timecounter_cyc2time(&chip->tstamp_tc, time_raw);
mv88e6xxx_reg_unlock(chip);
+#endif
shhwtstamps.hwtstamp = ns_to_ktime(ns);
dev_dbg(chip->dev,
@@ -443,9 +459,8 @@ static int mv88e6xxx_txtstamp_work(struct mv88e6xxx_chip *chip,
return 0;
}
-long mv88e6xxx_hwtstamp_work(struct ptp_clock_info *ptp)
+long mv88e6xxx_hwtstamp_work(struct mv88e6xxx_chip *chip)
{
- struct mv88e6xxx_chip *chip = ptp_to_chip(ptp);
struct dsa_switch *ds = chip->ds;
struct mv88e6xxx_port_hwtstamp *ps;
int i, restart = 0;
@@ -495,7 +510,11 @@ void mv88e6xxx_port_txtstamp(struct dsa_switch *ds, int port,
ps->tx_tstamp_start = jiffies;
ps->tx_seq_id = be16_to_cpu(hdr->sequence_id);
+#ifdef USE_MARVELL_TAI
+ marvell_tai_schedule(chip->tai, 0);
+#else
ptp_schedule_worker(chip->ptp_clock, 0);
+#endif
}
int mv88e6165_global_disable(struct mv88e6xxx_chip *chip)
diff --git a/drivers/net/dsa/mv88e6xxx/hwtstamp.h b/drivers/net/dsa/mv88e6xxx/hwtstamp.h
index 85acc758e3eb..6013d7edbf73 100644
--- a/drivers/net/dsa/mv88e6xxx/hwtstamp.h
+++ b/drivers/net/dsa/mv88e6xxx/hwtstamp.h
@@ -129,6 +129,7 @@ int mv88e6352_hwtstamp_port_enable(struct mv88e6xxx_chip *chip, int port);
int mv88e6352_hwtstamp_port_disable(struct mv88e6xxx_chip *chip, int port);
int mv88e6165_global_enable(struct mv88e6xxx_chip *chip);
int mv88e6165_global_disable(struct mv88e6xxx_chip *chip);
+long mv88e6xxx_hwtstamp_work(struct mv88e6xxx_chip *chip);
#else /* !CONFIG_NET_DSA_MV88E6XXX_PTP */
diff --git a/drivers/net/dsa/mv88e6xxx/ptp.c b/drivers/net/dsa/mv88e6xxx/ptp.c
index aed4a4b07f34..9d6f880b2430 100644
--- a/drivers/net/dsa/mv88e6xxx/ptp.c
+++ b/drivers/net/dsa/mv88e6xxx/ptp.c
@@ -87,6 +87,7 @@ static int mv88e6xxx_tai_read(struct mv88e6xxx_chip *chip, int addr,
return chip->info->ops->avb_ops->tai_read(chip, addr, data, len);
}
+#ifndef USE_MARVELL_TAI
static int mv88e6xxx_tai_write(struct mv88e6xxx_chip *chip, int addr, u16 data)
{
if (!chip->info->ops->avb_ops->tai_write)
@@ -94,6 +95,7 @@ static int mv88e6xxx_tai_write(struct mv88e6xxx_chip *chip, int addr, u16 data)
return chip->info->ops->avb_ops->tai_write(chip, addr, data);
}
+#endif
/* TODO: places where this are called should be using pinctrl */
static int mv88e6352_set_gpio_func(struct mv88e6xxx_chip *chip, int pin,
@@ -138,9 +140,8 @@ mv88e6xxx_cc_coeff_get(struct mv88e6xxx_chip *chip)
}
}
-static u64 mv88e6352_ptp_clock_read(const struct cyclecounter *cc)
+static u64 mv88e6352_ptp_clock_read(struct mv88e6xxx_chip *chip)
{
- struct mv88e6xxx_chip *chip = cc_to_chip(cc);
u16 phc_time[2];
int err;
@@ -152,9 +153,8 @@ static u64 mv88e6352_ptp_clock_read(const struct cyclecounter *cc)
return ((u32)phc_time[1] << 16) | phc_time[0];
}
-static u64 mv88e6165_ptp_clock_read(const struct cyclecounter *cc)
+static u64 mv88e6165_ptp_clock_read(struct mv88e6xxx_chip *chip)
{
- struct mv88e6xxx_chip *chip = cc_to_chip(cc);
u16 phc_time[2];
int err;
@@ -166,6 +166,7 @@ static u64 mv88e6165_ptp_clock_read(const struct cyclecounter *cc)
return ((u32)phc_time[1] << 16) | phc_time[0];
}
+#ifndef USE_MARVELL_TAI
/* mv88e6352_config_eventcap - configure TAI event capture
* @event: PTP_CLOCK_PPS (internal) or PTP_CLOCK_EXTTS (external)
* @rising: zero for falling-edge trigger, else rising-edge trigger
@@ -324,6 +325,52 @@ static int mv88e6xxx_ptp_settime(struct ptp_clock_info *ptp,
return 0;
}
+static int mv88e6xxx_ptp_enable_extts(struct mv88e6xxx_chip *chip,
+ struct ptp_extts_request *req, int on)
+{
+ int pin;
+
+ /* Reject requests with unsupported flags */
+ if (req->flags & ~(PTP_ENABLE_FEATURE |
+ PTP_RISING_EDGE |
+ PTP_FALLING_EDGE |
+ PTP_STRICT_FLAGS))
+ return -EOPNOTSUPP;
+
+ if (!chip->info->ops->ptp_ops->ptp_enable_extts)
+ return -EOPNOTSUPP;
+
+ pin = ptp_find_pin(chip->ptp_clock, PTP_PF_EXTTS, req->index);
+ if (pin < 0)
+ return -EBUSY;
+
+ return chip->info->ops->ptp_ops->ptp_enable_extts(chip, rq, on);
+}
+
+static int mv88e6xxx_ptp_enable(struct ptp_clock_info *ptp,
+ struct ptp_clock_request *rq, int on)
+{
+ struct mv88e6xxx_chip *chip = ptp_to_chip(ptp);
+
+ switch (rq->type) {
+ case PTP_CLK_REQ_EXTTS:
+ return mv88e6xxx_ptp_enable_extts(chip, &rq->extts, on);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static int mv88e6xxx_ptp_verify(struct ptp_clock_info *ptp, unsigned int pin,
+ enum ptp_pin_function func, unsigned int chan)
+{
+ struct mv88e6xxx_chip *chip = ptp_to_chip(ptp);
+
+ if (!chip->info->ops->ptp_ops->ptp_verify)
+ return -EOPNOTSUPP;
+
+ return chip->info->ops->ptp_ops->ptp_verify(chip, pin, func, chan);
+}
+
static int mv88e6352_ptp_enable_extts(struct mv88e6xxx_chip *chip,
struct ptp_clock_request *rq, int on)
{
@@ -376,21 +423,30 @@ static int mv88e6352_ptp_enable_extts(struct mv88e6xxx_chip *chip,
return err;
}
+#endif
-static int mv88e6352_ptp_enable(struct ptp_clock_info *ptp,
- struct ptp_clock_request *rq, int on)
+static int mv88e6352_ptp_pin_setup(struct mv88e6xxx_chip *chip,
+ int pin, unsigned int flags, int enable)
{
- struct mv88e6xxx_chip *chip = ptp_to_chip(ptp);
+ int func, err;
- switch (rq->type) {
- case PTP_CLK_REQ_EXTTS:
- return mv88e6352_ptp_enable_extts(chip, rq, on);
- default:
+ /* Reject requests to enable time stamping on both edges. */
+ if (flags & PTP_STRICT_FLAGS &&
+ flags & PTP_ENABLE_FEATURE &&
+ (flags & PTP_EXTTS_EDGES) == PTP_EXTTS_EDGES)
return -EOPNOTSUPP;
- }
+
+ if (enable)
+ func = MV88E6352_G2_SCRATCH_GPIO_PCTL_EVREQ;
+ else
+ func = MV88E6352_G2_SCRATCH_GPIO_PCTL_GPIO;
+
+ err = mv88e6352_set_gpio_func(chip, pin, func, true);
+
+ return enable ? err : 0;
}
-static int mv88e6352_ptp_verify(struct ptp_clock_info *ptp, unsigned int pin,
+static int mv88e6352_ptp_verify(struct mv88e6xxx_chip *chip, unsigned int pin,
enum ptp_pin_function func, unsigned int chan)
{
switch (func) {
@@ -422,9 +478,13 @@ const struct mv88e6xxx_ptp_ops mv88e6165_ptp_ops = {
const struct mv88e6xxx_ptp_ops mv88e6250_ptp_ops = {
.clock_read = mv88e6352_ptp_clock_read,
- .ptp_enable = mv88e6352_ptp_enable,
.ptp_verify = mv88e6352_ptp_verify,
+#ifndef USE_MARVELL_TAI
+ .ptp_enable_extts = mv88e6352_ptp_enable_extts,
.event_work = mv88e6352_tai_event_work,
+#else
+ .ptp_pin_setup = mv88e6352_ptp_pin_setup,
+#endif
.port_enable = mv88e6352_hwtstamp_port_enable,
.port_disable = mv88e6352_hwtstamp_port_disable,
.n_ext_ts = 1,
@@ -445,9 +505,13 @@ const struct mv88e6xxx_ptp_ops mv88e6250_ptp_ops = {
const struct mv88e6xxx_ptp_ops mv88e6352_ptp_ops = {
.clock_read = mv88e6352_ptp_clock_read,
- .ptp_enable = mv88e6352_ptp_enable,
.ptp_verify = mv88e6352_ptp_verify,
+#ifndef USE_MARVELL_TAI
+ .ptp_enable_extts = mv88e6352_ptp_enable_extts,
.event_work = mv88e6352_tai_event_work,
+#else
+ .ptp_pin_setup = mv88e6352_ptp_pin_setup,
+#endif
.port_enable = mv88e6352_hwtstamp_port_enable,
.port_disable = mv88e6352_hwtstamp_port_disable,
.n_ext_ts = 1,
@@ -468,9 +532,13 @@ const struct mv88e6xxx_ptp_ops mv88e6352_ptp_ops = {
const struct mv88e6xxx_ptp_ops mv88e6390_ptp_ops = {
.clock_read = mv88e6352_ptp_clock_read,
- .ptp_enable = mv88e6352_ptp_enable,
.ptp_verify = mv88e6352_ptp_verify,
+#ifndef USE_MARVELL_TAI
+ .ptp_enable_extts = mv88e6352_ptp_enable_extts,
.event_work = mv88e6352_tai_event_work,
+#else
+ .ptp_pin_setup = mv88e6352_ptp_pin_setup,
+#endif
.port_enable = mv88e6352_hwtstamp_port_enable,
.port_disable = mv88e6352_hwtstamp_port_disable,
.set_ptp_cpu_port = mv88e6390_g1_set_ptp_cpu_port,
@@ -490,12 +558,32 @@ const struct mv88e6xxx_ptp_ops mv88e6390_ptp_ops = {
(1 << HWTSTAMP_FILTER_PTP_V2_DELAY_REQ),
};
+static int mv88e6xxx_set_ptp_cpu_port(struct mv88e6xxx_chip *chip)
+{
+ const struct mv88e6xxx_ptp_ops *ptp_ops = chip->info->ops->ptp_ops;
+ struct dsa_port *dp;
+ int upstream = 0;
+ int err;
+
+ dsa_switch_for_each_user_port(dp, chip->ds) {
+ upstream = dsa_upstream_port(chip->ds, dp->index);
+ break;
+ }
+
+ err = ptp_ops->set_ptp_cpu_port(chip, upstream);
+ if (err)
+ dev_err(chip->dev, "Failed to set PTP CPU destination port!\n");
+
+ return err;
+}
+
+#ifndef USE_MARVELL_TAI
static u64 mv88e6xxx_ptp_clock_read(const struct cyclecounter *cc)
{
struct mv88e6xxx_chip *chip = cc_to_chip(cc);
if (chip->info->ops->ptp_ops->clock_read)
- return chip->info->ops->ptp_ops->clock_read(cc);
+ return chip->info->ops->ptp_ops->clock_read(chip);
return 0;
}
@@ -516,6 +604,11 @@ static void mv88e6xxx_ptp_overflow_check(struct work_struct *work)
MV88E6XXX_TAI_OVERFLOW_PERIOD);
}
+static long mv88e6xxx_ptp_aux_work(struct ptp_clock_info *ptp)
+{
+ return mv88e6xxx_hwtstamp_work(ptp_to_chip(ptp));
+}
+
int mv88e6xxx_ptp_setup(struct mv88e6xxx_chip *chip)
{
const struct mv88e6xxx_ptp_ops *ptp_ops = chip->info->ops->ptp_ops;
@@ -555,6 +648,7 @@ int mv88e6xxx_ptp_setup(struct mv88e6xxx_chip *chip)
ppd->index = i;
ppd->func = PTP_PF_NONE;
}
+
chip->ptp_clock_info.pin_config = chip->pin_config;
chip->ptp_clock_info.max_adj = MV88E6XXX_MAX_ADJ_PPB;
@@ -562,25 +656,14 @@ int mv88e6xxx_ptp_setup(struct mv88e6xxx_chip *chip)
chip->ptp_clock_info.adjtime = mv88e6xxx_ptp_adjtime;
chip->ptp_clock_info.gettime64 = mv88e6xxx_ptp_gettime;
chip->ptp_clock_info.settime64 = mv88e6xxx_ptp_settime;
- chip->ptp_clock_info.enable = ptp_ops->ptp_enable;
- chip->ptp_clock_info.verify = ptp_ops->ptp_verify;
- chip->ptp_clock_info.do_aux_work = mv88e6xxx_hwtstamp_work;
+ chip->ptp_clock_info.enable = mv88e6xxx_ptp_enable;
+ chip->ptp_clock_info.verify = mv88e6xxx_ptp_verify;
+ chip->ptp_clock_info.do_aux_work = mv88e6xxx_ptp_aux_work;
if (ptp_ops->set_ptp_cpu_port) {
- struct dsa_port *dp;
- int upstream = 0;
- int err;
-
- dsa_switch_for_each_user_port(dp, chip->ds) {
- upstream = dsa_upstream_port(chip->ds, dp->index);
- break;
- }
-
- err = ptp_ops->set_ptp_cpu_port(chip, upstream);
- if (err) {
- dev_err(chip->dev, "Failed to set PTP CPU destination port!\n");
+ err = mv88e6xxx_set_ptp_cpu_port(chip);
+ if (err)
return err;
- }
}
chip->ptp_clock = ptp_clock_register(&chip->ptp_clock_info, chip->dev);
@@ -604,3 +687,208 @@ void mv88e6xxx_ptp_free(struct mv88e6xxx_chip *chip)
chip->ptp_clock = NULL;
}
}
+#else
+static struct mv88e6xxx_chip *dev_to_chip(struct device *dev)
+{
+ struct dsa_switch *ds = dev_get_drvdata(dev);
+
+ return ds->priv;
+}
+
+static int mv88e6xxx_tai_enable(struct device *dev)
+{
+ return 0;
+}
+
+static u64 mv88e6xxx_tai_clock_read(struct device *dev,
+ struct ptp_system_timestamp *sts)
+{
+ struct mv88e6xxx_chip *chip = dev_to_chip(dev);
+ int err = 0;
+
+ if (chip->info->ops->ptp_ops->clock_read) {
+ mv88e6xxx_reg_lock(chip);
+ ptp_read_system_prets(sts);
+ err = chip->info->ops->ptp_ops->clock_read(chip);
+ ptp_read_system_postts(sts);
+ mv88e6xxx_reg_unlock(chip);
+ }
+
+ return err;
+}
+
+static int mv88e6xxx_tai_extts_read(struct device *dev, int reg,
+ struct marvell_extts *extts)
+{
+ struct mv88e6xxx_chip *chip = dev_to_chip(dev);
+ u16 regs[3];
+ int ret;
+
+ mv88e6xxx_reg_lock(chip);
+ ret = chip->info->ops->avb_ops->tai_read(chip, reg, regs, 3);
+ if (ret < 0)
+ goto unlock;
+
+ extts->status = regs[0];
+ extts->time = regs[1] | regs[2] << 16;
+
+ /* Clear valid if set */
+ if (regs[0] & MV_STATUS_EVENTCAPVALID) {
+ chip->info->ops->avb_ops->tai_write(chip, reg, 0);
+ ret = 1;
+ } else {
+ ret = 0;
+ }
+
+unlock:
+ mv88e6xxx_reg_unlock(chip);
+
+ return ret;
+}
+
+static int mv88e6xxx_tai_pin_verify(struct device *dev, int pin,
+ enum ptp_pin_function func,
+ unsigned int chan)
+{
+ struct mv88e6xxx_chip *chip = dev_to_chip(dev);
+
+ if (!chip->info->ops->ptp_ops->ptp_verify)
+ return -EOPNOTSUPP;
+
+ return chip->info->ops->ptp_ops->ptp_verify(chip, pin, func, chan);
+}
+
+static int mv88e6xxx_tai_pin_setup(struct device *dev, int pin,
+ unsigned int flags, int enable)
+{
+ struct mv88e6xxx_chip *chip = dev_to_chip(dev);
+
+ if (!chip->info->ops->ptp_ops->ptp_pin_setup)
+ return -EOPNOTSUPP;
+
+ return chip->info->ops->ptp_ops->ptp_pin_setup(chip, pin, flags,
+ enable);
+}
+
+static int mv88e6xxx_tai_write(struct device *dev, u8 reg, u16 val)
+{
+ struct mv88e6xxx_chip *chip = dev_to_chip(dev);
+ int err;
+
+ mv88e6xxx_reg_lock(chip);
+ err = chip->info->ops->avb_ops->tai_write(chip, reg, val);
+ mv88e6xxx_reg_unlock(chip);
+
+ return err;
+}
+
+static int mv88e6xxx_tai_modify(struct device *dev, u8 reg, u16 mask, u16 val)
+{
+ struct mv88e6xxx_chip *chip = dev_to_chip(dev);
+ u16 old, new;
+ int err;
+
+ mv88e6xxx_reg_lock(chip);
+ err = chip->info->ops->avb_ops->tai_read(chip, reg, &old, 1);
+ if (err < 0)
+ goto unlock;
+
+ new = (old & ~mask) | val;
+ if (new != old)
+ err = chip->info->ops->avb_ops->tai_write(chip, reg, new);
+
+unlock:
+ mv88e6xxx_reg_unlock(chip);
+ return err;
+}
+
+static int mv88e6xxx_ptp_global_write(struct device *dev, u8 reg, u16 val)
+{
+ return 0;
+}
+
+static int mv88e6xxx_ptp_port_read_ts(struct device *dev, struct marvell_ts *ts,
+ u8 reg)
+{
+ return 0;
+}
+
+static int mv88e6xxx_ptp_port_write(struct device *dev, u8 reg, u16 val)
+{
+ return 0;
+}
+
+static int mv88e6xxx_ptp_port_modify(struct device *dev, u8 reg, u16 mask,
+ u16 val)
+{
+ return 0;
+}
+
+static long mv88e6xxx_ptp_aux_work(struct device *dev)
+{
+ return mv88e6xxx_hwtstamp_work(dev_to_chip(dev));
+}
+
+static const struct marvell_ptp_ops mv88e6xxx_ptp_ops = {
+ .tai_enable = mv88e6xxx_tai_enable,
+ .tai_clock_read = mv88e6xxx_tai_clock_read,
+ .tai_extts_read = mv88e6xxx_tai_extts_read,
+ .tai_pin_verify = mv88e6xxx_tai_pin_verify,
+ .tai_pin_setup = mv88e6xxx_tai_pin_setup,
+ .tai_write = mv88e6xxx_tai_write,
+ .tai_modify = mv88e6xxx_tai_modify,
+ .ptp_global_write = mv88e6xxx_ptp_global_write,
+ .ptp_port_read_ts = mv88e6xxx_ptp_port_read_ts,
+ .ptp_port_write = mv88e6xxx_ptp_port_write,
+ .ptp_port_modify = mv88e6xxx_ptp_port_modify,
+ .ptp_aux_work = mv88e6xxx_ptp_aux_work,
+};
+
+int mv88e6xxx_ptp_setup(struct mv88e6xxx_chip *chip)
+{
+ const struct mv88e6xxx_ptp_ops *ptp_ops = chip->info->ops->ptp_ops;
+ struct marvell_tai_param tai_param;
+ int i, n_pins, err;
+
+ /* Set up the cycle counter */
+ chip->cc_coeffs = mv88e6xxx_cc_coeff_get(chip);
+ if (IS_ERR(chip->cc_coeffs))
+ return PTR_ERR(chip->cc_coeffs);
+
+ if (ptp_ops->set_ptp_cpu_port) {
+ err = mv88e6xxx_set_ptp_cpu_port(chip);
+ if (err)
+ return err;
+ }
+
+ memset(&tai_param, 0, sizeof(tai_param));
+ tai_param.cc_mult_num = chip->cc_coeffs->cc_mult_num;
+ tai_param.cc_mult_den = chip->cc_coeffs->cc_mult_dem;
+ tai_param.cc_mult = chip->cc_coeffs->cc_mult;
+ tai_param.cc_shift = chip->cc_coeffs->cc_shift;
+ tai_param.n_ext_ts = ptp_ops->n_ext_ts;
+
+ n_pins = mv88e6xxx_num_gpio(chip);
+ for (i = 0; i < n_pins; ++i) {
+ struct ptp_pin_desc *ppd = &chip->pin_config[i];
+
+ snprintf(ppd->name, sizeof(ppd->name), "mv88e6xxx_gpio%d", i);
+ ppd->index = i;
+ ppd->func = PTP_PF_NONE;
+ }
+
+ mv88e6xxx_reg_unlock(chip);
+ err = marvell_tai_probe(&chip->tai, &mv88e6xxx_ptp_ops, &tai_param,
+ chip->pin_config, n_pins,
+ dev_name(chip->dev), chip->dev);
+ mv88e6xxx_reg_lock(chip);
+
+ return err;
+}
+
+void mv88e6xxx_ptp_free(struct mv88e6xxx_chip *chip)
+{
+ if (chip->tai)
+ marvell_tai_remove(chip->tai);
+}
+#endif
diff --git a/drivers/net/dsa/mv88e6xxx/ptp.h b/drivers/net/dsa/mv88e6xxx/ptp.h
index 6c4d09adc93c..f10ca6e91fe4 100644
--- a/drivers/net/dsa/mv88e6xxx/ptp.h
+++ b/drivers/net/dsa/mv88e6xxx/ptp.h
@@ -141,7 +141,6 @@
#ifdef CONFIG_NET_DSA_MV88E6XXX_PTP
-long mv88e6xxx_hwtstamp_work(struct ptp_clock_info *ptp);
int mv88e6xxx_ptp_setup(struct mv88e6xxx_chip *chip);
void mv88e6xxx_ptp_free(struct mv88e6xxx_chip *chip);
--
2.30.2
^ permalink raw reply related [flat|nested] 28+ messages in thread
* [PATCH RFC net-next 5/5] mv88e6xxx: cleanup ptp tai
2025-04-11 21:26 [PATCH RFC net-next 0/5] Marvell PTP support Russell King (Oracle)
` (3 preceding siblings ...)
2025-04-11 21:26 ` [PATCH RFC net-next 4/5] mv88e6xxx: convert to marvell TAI Russell King (Oracle)
@ 2025-04-11 21:26 ` Russell King (Oracle)
2025-04-11 21:30 ` [PATCH RFC net-next 0/5] Marvell PTP support Russell King (Oracle)
2025-04-16 10:51 ` Russell King (Oracle)
6 siblings, 0 replies; 28+ messages in thread
From: Russell King (Oracle) @ 2025-04-11 21:26 UTC (permalink / raw)
To: Andrew Lunn, Heiner Kallweit
Cc: David S. Miller, Eric Dumazet, Jakub Kicinski, Kory Maincent,
netdev, Paolo Abeni, Richard Cochran, Vladimir Oltean
---
drivers/net/dsa/mv88e6xxx/chip.h | 22 +-
drivers/net/dsa/mv88e6xxx/hwtstamp.c | 24 --
drivers/net/dsa/mv88e6xxx/ptp.c | 417 +--------------------------
3 files changed, 12 insertions(+), 451 deletions(-)
diff --git a/drivers/net/dsa/mv88e6xxx/chip.h b/drivers/net/dsa/mv88e6xxx/chip.h
index 6fb7d0fa0180..a509cb34a167 100644
--- a/drivers/net/dsa/mv88e6xxx/chip.h
+++ b/drivers/net/dsa/mv88e6xxx/chip.h
@@ -7,7 +7,7 @@
#ifndef _MV88E6XXX_CHIP_H
#define _MV88E6XXX_CHIP_H
-#define USE_MARVELL_TAI
+
#include <linux/idr.h>
#include <linux/if_vlan.h>
#include <linux/irq.h>
@@ -413,29 +413,9 @@ struct mv88e6xxx_chip {
/* GPIO resources */
u8 gpio_data[2];
-#ifdef USE_MARVELL_TAI
struct marvell_tai *tai;
-#else
- /* This cyclecounter abstracts the switch PTP time.
- * reg_lock must be held for any operation that read()s.
- */
- struct cyclecounter tstamp_cc;
- struct timecounter tstamp_tc;
- struct delayed_work overflow_work;
-#endif
- const struct mv88e6xxx_cc_coeffs *cc_coeffs;
-
-#ifndef USE_MARVELL_TAI
- struct ptp_clock *ptp_clock;
- struct ptp_clock_info ptp_clock_info;
- struct delayed_work tai_event_work;
-#endif
struct ptp_pin_desc pin_config[MV88E6XXX_MAX_GPIO];
-#ifndef USE_MARVELL_TAI
- u16 trig_config;
- u16 evcap_config;
-#endif
u16 enable_count;
/* Current ingress and egress monitor ports */
diff --git a/drivers/net/dsa/mv88e6xxx/hwtstamp.c b/drivers/net/dsa/mv88e6xxx/hwtstamp.c
index 232deff1b2ba..942ea77921f9 100644
--- a/drivers/net/dsa/mv88e6xxx/hwtstamp.c
+++ b/drivers/net/dsa/mv88e6xxx/hwtstamp.c
@@ -79,11 +79,7 @@ int mv88e6xxx_get_ts_info(struct dsa_switch *ds, int port,
SOF_TIMESTAMPING_TX_HARDWARE |
SOF_TIMESTAMPING_RX_HARDWARE |
SOF_TIMESTAMPING_RAW_HARDWARE;
-#ifdef USE_MARVELL_TAI
info->phc_index = marvell_tai_ptp_clock_index(chip->tai);
-#else
- info->phc_index = ptp_clock_index(chip->ptp_clock);
-#endif
info->tx_types =
(1 << HWTSTAMP_TX_OFF) |
(1 << HWTSTAMP_TX_ON);
@@ -297,13 +293,7 @@ static void mv88e6xxx_get_rxts(struct mv88e6xxx_chip *chip,
if (mv88e6xxx_ts_valid(status) && seq_match(skb, seq_id)) {
ns = timehi << 16 | timelo;
-#ifdef USE_MARVELL_TAI
ns = marvell_tai_cyc2time(chip->tai, ns);
-#else
- mv88e6xxx_reg_lock(chip);
- ns = timecounter_cyc2time(&chip->tstamp_tc, ns);
- mv88e6xxx_reg_unlock(chip);
-#endif
shwt = skb_hwtstamps(skb);
memset(shwt, 0, sizeof(*shwt));
shwt->hwtstamp = ns_to_ktime(ns);
@@ -360,11 +350,7 @@ bool mv88e6xxx_port_rxtstamp(struct dsa_switch *ds, int port,
else
skb_queue_tail(&ps->rx_queue, skb);
-#ifdef USE_MARVELL_TAI
marvell_tai_schedule(chip->tai, 0);
-#else
- ptp_schedule_worker(chip->ptp_clock, 0);
-#endif
return true;
}
@@ -425,13 +411,7 @@ static int mv88e6xxx_txtstamp_work(struct mv88e6xxx_chip *chip,
memset(&shhwtstamps, 0, sizeof(shhwtstamps));
time_raw = ((u32)departure_block[2] << 16) | departure_block[1];
-#ifdef USE_MARVELL_TAI
ns = marvell_tai_cyc2time(chip->tai, time_raw);
-#else
- mv88e6xxx_reg_lock(chip);
- ns = timecounter_cyc2time(&chip->tstamp_tc, time_raw);
- mv88e6xxx_reg_unlock(chip);
-#endif
shhwtstamps.hwtstamp = ns_to_ktime(ns);
dev_dbg(chip->dev,
@@ -510,11 +490,7 @@ void mv88e6xxx_port_txtstamp(struct dsa_switch *ds, int port,
ps->tx_tstamp_start = jiffies;
ps->tx_seq_id = be16_to_cpu(hdr->sequence_id);
-#ifdef USE_MARVELL_TAI
marvell_tai_schedule(chip->tai, 0);
-#else
- ptp_schedule_worker(chip->ptp_clock, 0);
-#endif
}
int mv88e6165_global_disable(struct mv88e6xxx_chip *chip)
diff --git a/drivers/net/dsa/mv88e6xxx/ptp.c b/drivers/net/dsa/mv88e6xxx/ptp.c
index 9d6f880b2430..d8d7412fa755 100644
--- a/drivers/net/dsa/mv88e6xxx/ptp.c
+++ b/drivers/net/dsa/mv88e6xxx/ptp.c
@@ -87,16 +87,6 @@ static int mv88e6xxx_tai_read(struct mv88e6xxx_chip *chip, int addr,
return chip->info->ops->avb_ops->tai_read(chip, addr, data, len);
}
-#ifndef USE_MARVELL_TAI
-static int mv88e6xxx_tai_write(struct mv88e6xxx_chip *chip, int addr, u16 data)
-{
- if (!chip->info->ops->avb_ops->tai_write)
- return -EOPNOTSUPP;
-
- return chip->info->ops->avb_ops->tai_write(chip, addr, data);
-}
-#endif
-
/* TODO: places where this are called should be using pinctrl */
static int mv88e6352_set_gpio_func(struct mv88e6xxx_chip *chip, int pin,
int func, int input)
@@ -166,265 +156,6 @@ static u64 mv88e6165_ptp_clock_read(struct mv88e6xxx_chip *chip)
return ((u32)phc_time[1] << 16) | phc_time[0];
}
-#ifndef USE_MARVELL_TAI
-/* mv88e6352_config_eventcap - configure TAI event capture
- * @event: PTP_CLOCK_PPS (internal) or PTP_CLOCK_EXTTS (external)
- * @rising: zero for falling-edge trigger, else rising-edge trigger
- *
- * This will also reset the capture sequence counter.
- */
-static int mv88e6352_config_eventcap(struct mv88e6xxx_chip *chip, int event,
- int rising)
-{
- u16 global_config;
- u16 cap_config;
- int err;
-
- chip->evcap_config = MV88E6XXX_TAI_CFG_CAP_OVERWRITE |
- MV88E6XXX_TAI_CFG_CAP_CTR_START;
- if (!rising)
- chip->evcap_config |= MV88E6XXX_TAI_CFG_EVREQ_FALLING;
-
- global_config = (chip->evcap_config | chip->trig_config);
- err = mv88e6xxx_tai_write(chip, MV88E6XXX_TAI_CFG, global_config);
- if (err)
- return err;
-
- if (event == PTP_CLOCK_PPS) {
- cap_config = MV88E6XXX_TAI_EVENT_STATUS_CAP_TRIG;
- } else if (event == PTP_CLOCK_EXTTS) {
- /* if STATUS_CAP_TRIG is unset we capture PTP_EVREQ events */
- cap_config = 0;
- } else {
- return -EINVAL;
- }
-
- /* Write the capture config; this also clears the capture counter */
- err = mv88e6xxx_tai_write(chip, MV88E6XXX_TAI_EVENT_STATUS,
- cap_config);
-
- return err;
-}
-
-static void mv88e6352_tai_event_work(struct work_struct *ugly)
-{
- struct delayed_work *dw = to_delayed_work(ugly);
- struct mv88e6xxx_chip *chip = dw_tai_event_to_chip(dw);
- struct ptp_clock_event ev;
- u16 status[4];
- u32 raw_ts;
- int err;
-
- mv88e6xxx_reg_lock(chip);
- err = mv88e6xxx_tai_read(chip, MV88E6XXX_TAI_EVENT_STATUS,
- status, ARRAY_SIZE(status));
- mv88e6xxx_reg_unlock(chip);
-
- if (err) {
- dev_err(chip->dev, "failed to read TAI status register\n");
- return;
- }
- if (status[0] & MV88E6XXX_TAI_EVENT_STATUS_ERROR) {
- dev_warn(chip->dev, "missed event capture\n");
- return;
- }
- if (!(status[0] & MV88E6XXX_TAI_EVENT_STATUS_VALID))
- goto out;
-
- raw_ts = ((u32)status[2] << 16) | status[1];
-
- /* Clear the valid bit so the next timestamp can come in */
- status[0] &= ~MV88E6XXX_TAI_EVENT_STATUS_VALID;
- mv88e6xxx_reg_lock(chip);
- err = mv88e6xxx_tai_write(chip, MV88E6XXX_TAI_EVENT_STATUS, status[0]);
- mv88e6xxx_reg_unlock(chip);
- if (err) {
- dev_err(chip->dev, "failed to write TAI status register\n");
- return;
- }
-
- /* This is an external timestamp */
- ev.type = PTP_CLOCK_EXTTS;
-
- /* We only have one timestamping channel. */
- ev.index = 0;
- mv88e6xxx_reg_lock(chip);
- ev.timestamp = timecounter_cyc2time(&chip->tstamp_tc, raw_ts);
- mv88e6xxx_reg_unlock(chip);
-
- ptp_clock_event(chip->ptp_clock, &ev);
-out:
- schedule_delayed_work(&chip->tai_event_work, TAI_EVENT_WORK_INTERVAL);
-}
-
-static int mv88e6xxx_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm)
-{
- struct mv88e6xxx_chip *chip = ptp_to_chip(ptp);
- int neg_adj = 0;
- u32 diff, mult;
- u64 adj;
-
- if (scaled_ppm < 0) {
- neg_adj = 1;
- scaled_ppm = -scaled_ppm;
- }
-
- mult = chip->cc_coeffs->cc_mult;
- adj = chip->cc_coeffs->cc_mult_num;
- adj *= scaled_ppm;
- diff = div_u64(adj, chip->cc_coeffs->cc_mult_dem);
-
- mv88e6xxx_reg_lock(chip);
-
- timecounter_read(&chip->tstamp_tc);
- chip->tstamp_cc.mult = neg_adj ? mult - diff : mult + diff;
-
- mv88e6xxx_reg_unlock(chip);
-
- return 0;
-}
-
-static int mv88e6xxx_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta)
-{
- struct mv88e6xxx_chip *chip = ptp_to_chip(ptp);
-
- mv88e6xxx_reg_lock(chip);
- timecounter_adjtime(&chip->tstamp_tc, delta);
- mv88e6xxx_reg_unlock(chip);
-
- return 0;
-}
-
-static int mv88e6xxx_ptp_gettime(struct ptp_clock_info *ptp,
- struct timespec64 *ts)
-{
- struct mv88e6xxx_chip *chip = ptp_to_chip(ptp);
- u64 ns;
-
- mv88e6xxx_reg_lock(chip);
- ns = timecounter_read(&chip->tstamp_tc);
- mv88e6xxx_reg_unlock(chip);
-
- *ts = ns_to_timespec64(ns);
-
- return 0;
-}
-
-static int mv88e6xxx_ptp_settime(struct ptp_clock_info *ptp,
- const struct timespec64 *ts)
-{
- struct mv88e6xxx_chip *chip = ptp_to_chip(ptp);
- u64 ns;
-
- ns = timespec64_to_ns(ts);
-
- mv88e6xxx_reg_lock(chip);
- timecounter_init(&chip->tstamp_tc, &chip->tstamp_cc, ns);
- mv88e6xxx_reg_unlock(chip);
-
- return 0;
-}
-
-static int mv88e6xxx_ptp_enable_extts(struct mv88e6xxx_chip *chip,
- struct ptp_extts_request *req, int on)
-{
- int pin;
-
- /* Reject requests with unsupported flags */
- if (req->flags & ~(PTP_ENABLE_FEATURE |
- PTP_RISING_EDGE |
- PTP_FALLING_EDGE |
- PTP_STRICT_FLAGS))
- return -EOPNOTSUPP;
-
- if (!chip->info->ops->ptp_ops->ptp_enable_extts)
- return -EOPNOTSUPP;
-
- pin = ptp_find_pin(chip->ptp_clock, PTP_PF_EXTTS, req->index);
- if (pin < 0)
- return -EBUSY;
-
- return chip->info->ops->ptp_ops->ptp_enable_extts(chip, rq, on);
-}
-
-static int mv88e6xxx_ptp_enable(struct ptp_clock_info *ptp,
- struct ptp_clock_request *rq, int on)
-{
- struct mv88e6xxx_chip *chip = ptp_to_chip(ptp);
-
- switch (rq->type) {
- case PTP_CLK_REQ_EXTTS:
- return mv88e6xxx_ptp_enable_extts(chip, &rq->extts, on);
- default:
- return -EOPNOTSUPP;
- }
-}
-
-static int mv88e6xxx_ptp_verify(struct ptp_clock_info *ptp, unsigned int pin,
- enum ptp_pin_function func, unsigned int chan)
-{
- struct mv88e6xxx_chip *chip = ptp_to_chip(ptp);
-
- if (!chip->info->ops->ptp_ops->ptp_verify)
- return -EOPNOTSUPP;
-
- return chip->info->ops->ptp_ops->ptp_verify(chip, pin, func, chan);
-}
-
-static int mv88e6352_ptp_enable_extts(struct mv88e6xxx_chip *chip,
- struct ptp_clock_request *rq, int on)
-{
- int rising = (rq->extts.flags & PTP_RISING_EDGE);
- int func;
- int pin;
- int err;
-
- /* Reject requests with unsupported flags */
- if (rq->extts.flags & ~(PTP_ENABLE_FEATURE |
- PTP_RISING_EDGE |
- PTP_FALLING_EDGE |
- PTP_STRICT_FLAGS))
- return -EOPNOTSUPP;
-
- /* Reject requests to enable time stamping on both edges. */
- if ((rq->extts.flags & PTP_STRICT_FLAGS) &&
- (rq->extts.flags & PTP_ENABLE_FEATURE) &&
- (rq->extts.flags & PTP_EXTTS_EDGES) == PTP_EXTTS_EDGES)
- return -EOPNOTSUPP;
-
- pin = ptp_find_pin(chip->ptp_clock, PTP_PF_EXTTS, rq->extts.index);
-
- if (pin < 0)
- return -EBUSY;
-
- mv88e6xxx_reg_lock(chip);
-
- if (on) {
- func = MV88E6352_G2_SCRATCH_GPIO_PCTL_EVREQ;
-
- err = mv88e6352_set_gpio_func(chip, pin, func, true);
- if (err)
- goto out;
-
- schedule_delayed_work(&chip->tai_event_work,
- TAI_EVENT_WORK_INTERVAL);
-
- err = mv88e6352_config_eventcap(chip, PTP_CLOCK_EXTTS, rising);
- } else {
- func = MV88E6352_G2_SCRATCH_GPIO_PCTL_GPIO;
-
- err = mv88e6352_set_gpio_func(chip, pin, func, true);
-
- cancel_delayed_work_sync(&chip->tai_event_work);
- }
-
-out:
- mv88e6xxx_reg_unlock(chip);
-
- return err;
-}
-#endif
-
static int mv88e6352_ptp_pin_setup(struct mv88e6xxx_chip *chip,
int pin, unsigned int flags, int enable)
{
@@ -478,13 +209,8 @@ const struct mv88e6xxx_ptp_ops mv88e6165_ptp_ops = {
const struct mv88e6xxx_ptp_ops mv88e6250_ptp_ops = {
.clock_read = mv88e6352_ptp_clock_read,
- .ptp_verify = mv88e6352_ptp_verify,
-#ifndef USE_MARVELL_TAI
- .ptp_enable_extts = mv88e6352_ptp_enable_extts,
- .event_work = mv88e6352_tai_event_work,
-#else
.ptp_pin_setup = mv88e6352_ptp_pin_setup,
-#endif
+ .ptp_verify = mv88e6352_ptp_verify,
.port_enable = mv88e6352_hwtstamp_port_enable,
.port_disable = mv88e6352_hwtstamp_port_disable,
.n_ext_ts = 1,
@@ -505,13 +231,8 @@ const struct mv88e6xxx_ptp_ops mv88e6250_ptp_ops = {
const struct mv88e6xxx_ptp_ops mv88e6352_ptp_ops = {
.clock_read = mv88e6352_ptp_clock_read,
- .ptp_verify = mv88e6352_ptp_verify,
-#ifndef USE_MARVELL_TAI
- .ptp_enable_extts = mv88e6352_ptp_enable_extts,
- .event_work = mv88e6352_tai_event_work,
-#else
.ptp_pin_setup = mv88e6352_ptp_pin_setup,
-#endif
+ .ptp_verify = mv88e6352_ptp_verify,
.port_enable = mv88e6352_hwtstamp_port_enable,
.port_disable = mv88e6352_hwtstamp_port_disable,
.n_ext_ts = 1,
@@ -532,13 +253,8 @@ const struct mv88e6xxx_ptp_ops mv88e6352_ptp_ops = {
const struct mv88e6xxx_ptp_ops mv88e6390_ptp_ops = {
.clock_read = mv88e6352_ptp_clock_read,
- .ptp_verify = mv88e6352_ptp_verify,
-#ifndef USE_MARVELL_TAI
- .ptp_enable_extts = mv88e6352_ptp_enable_extts,
- .event_work = mv88e6352_tai_event_work,
-#else
.ptp_pin_setup = mv88e6352_ptp_pin_setup,
-#endif
+ .ptp_verify = mv88e6352_ptp_verify,
.port_enable = mv88e6352_hwtstamp_port_enable,
.port_disable = mv88e6352_hwtstamp_port_disable,
.set_ptp_cpu_port = mv88e6390_g1_set_ptp_cpu_port,
@@ -577,117 +293,6 @@ static int mv88e6xxx_set_ptp_cpu_port(struct mv88e6xxx_chip *chip)
return err;
}
-#ifndef USE_MARVELL_TAI
-static u64 mv88e6xxx_ptp_clock_read(const struct cyclecounter *cc)
-{
- struct mv88e6xxx_chip *chip = cc_to_chip(cc);
-
- if (chip->info->ops->ptp_ops->clock_read)
- return chip->info->ops->ptp_ops->clock_read(chip);
-
- return 0;
-}
-
-/* With a 250MHz input clock, the 32-bit timestamp counter overflows in ~17.2
- * seconds; this task forces periodic reads so that we don't miss any.
- */
-#define MV88E6XXX_TAI_OVERFLOW_PERIOD (HZ * 8)
-static void mv88e6xxx_ptp_overflow_check(struct work_struct *work)
-{
- struct delayed_work *dw = to_delayed_work(work);
- struct mv88e6xxx_chip *chip = dw_overflow_to_chip(dw);
- struct timespec64 ts;
-
- mv88e6xxx_ptp_gettime(&chip->ptp_clock_info, &ts);
-
- schedule_delayed_work(&chip->overflow_work,
- MV88E6XXX_TAI_OVERFLOW_PERIOD);
-}
-
-static long mv88e6xxx_ptp_aux_work(struct ptp_clock_info *ptp)
-{
- return mv88e6xxx_hwtstamp_work(ptp_to_chip(ptp));
-}
-
-int mv88e6xxx_ptp_setup(struct mv88e6xxx_chip *chip)
-{
- const struct mv88e6xxx_ptp_ops *ptp_ops = chip->info->ops->ptp_ops;
- int i;
-
- /* Set up the cycle counter */
- chip->cc_coeffs = mv88e6xxx_cc_coeff_get(chip);
- if (IS_ERR(chip->cc_coeffs))
- return PTR_ERR(chip->cc_coeffs);
-
- memset(&chip->tstamp_cc, 0, sizeof(chip->tstamp_cc));
- chip->tstamp_cc.read = mv88e6xxx_ptp_clock_read;
- chip->tstamp_cc.mask = CYCLECOUNTER_MASK(32);
- chip->tstamp_cc.mult = chip->cc_coeffs->cc_mult;
- chip->tstamp_cc.shift = chip->cc_coeffs->cc_shift;
-
- timecounter_init(&chip->tstamp_tc, &chip->tstamp_cc,
- ktime_to_ns(ktime_get_real()));
-
- INIT_DELAYED_WORK(&chip->overflow_work, mv88e6xxx_ptp_overflow_check);
- if (ptp_ops->event_work)
- INIT_DELAYED_WORK(&chip->tai_event_work, ptp_ops->event_work);
-
- chip->ptp_clock_info.owner = THIS_MODULE;
- snprintf(chip->ptp_clock_info.name, sizeof(chip->ptp_clock_info.name),
- "%s", dev_name(chip->dev));
-
- chip->ptp_clock_info.n_ext_ts = ptp_ops->n_ext_ts;
- chip->ptp_clock_info.n_per_out = 0;
- chip->ptp_clock_info.n_pins = mv88e6xxx_num_gpio(chip);
- chip->ptp_clock_info.pps = 0;
-
- for (i = 0; i < chip->ptp_clock_info.n_pins; ++i) {
- struct ptp_pin_desc *ppd = &chip->pin_config[i];
-
- snprintf(ppd->name, sizeof(ppd->name), "mv88e6xxx_gpio%d", i);
- ppd->index = i;
- ppd->func = PTP_PF_NONE;
- }
-
- chip->ptp_clock_info.pin_config = chip->pin_config;
-
- chip->ptp_clock_info.max_adj = MV88E6XXX_MAX_ADJ_PPB;
- chip->ptp_clock_info.adjfine = mv88e6xxx_ptp_adjfine;
- chip->ptp_clock_info.adjtime = mv88e6xxx_ptp_adjtime;
- chip->ptp_clock_info.gettime64 = mv88e6xxx_ptp_gettime;
- chip->ptp_clock_info.settime64 = mv88e6xxx_ptp_settime;
- chip->ptp_clock_info.enable = mv88e6xxx_ptp_enable;
- chip->ptp_clock_info.verify = mv88e6xxx_ptp_verify;
- chip->ptp_clock_info.do_aux_work = mv88e6xxx_ptp_aux_work;
-
- if (ptp_ops->set_ptp_cpu_port) {
- err = mv88e6xxx_set_ptp_cpu_port(chip);
- if (err)
- return err;
- }
-
- chip->ptp_clock = ptp_clock_register(&chip->ptp_clock_info, chip->dev);
- if (IS_ERR(chip->ptp_clock))
- return PTR_ERR(chip->ptp_clock);
-
- schedule_delayed_work(&chip->overflow_work,
- MV88E6XXX_TAI_OVERFLOW_PERIOD);
-
- return 0;
-}
-
-void mv88e6xxx_ptp_free(struct mv88e6xxx_chip *chip)
-{
- if (chip->ptp_clock) {
- cancel_delayed_work_sync(&chip->overflow_work);
- if (chip->info->ops->ptp_ops->event_work)
- cancel_delayed_work_sync(&chip->tai_event_work);
-
- ptp_clock_unregister(chip->ptp_clock);
- chip->ptp_clock = NULL;
- }
-}
-#else
static struct mv88e6xxx_chip *dev_to_chip(struct device *dev)
{
struct dsa_switch *ds = dev_get_drvdata(dev);
@@ -847,13 +452,14 @@ static const struct marvell_ptp_ops mv88e6xxx_ptp_ops = {
int mv88e6xxx_ptp_setup(struct mv88e6xxx_chip *chip)
{
const struct mv88e6xxx_ptp_ops *ptp_ops = chip->info->ops->ptp_ops;
+ const struct mv88e6xxx_cc_coeffs *cc_coeffs;
struct marvell_tai_param tai_param;
int i, n_pins, err;
/* Set up the cycle counter */
- chip->cc_coeffs = mv88e6xxx_cc_coeff_get(chip);
- if (IS_ERR(chip->cc_coeffs))
- return PTR_ERR(chip->cc_coeffs);
+ cc_coeffs = mv88e6xxx_cc_coeff_get(chip);
+ if (IS_ERR(cc_coeffs))
+ return PTR_ERR(cc_coeffs);
if (ptp_ops->set_ptp_cpu_port) {
err = mv88e6xxx_set_ptp_cpu_port(chip);
@@ -862,10 +468,10 @@ int mv88e6xxx_ptp_setup(struct mv88e6xxx_chip *chip)
}
memset(&tai_param, 0, sizeof(tai_param));
- tai_param.cc_mult_num = chip->cc_coeffs->cc_mult_num;
- tai_param.cc_mult_den = chip->cc_coeffs->cc_mult_dem;
- tai_param.cc_mult = chip->cc_coeffs->cc_mult;
- tai_param.cc_shift = chip->cc_coeffs->cc_shift;
+ tai_param.cc_mult_num = cc_coeffs->cc_mult_num;
+ tai_param.cc_mult_den = cc_coeffs->cc_mult_dem;
+ tai_param.cc_mult = cc_coeffs->cc_mult;
+ tai_param.cc_shift = cc_coeffs->cc_shift;
tai_param.n_ext_ts = ptp_ops->n_ext_ts;
n_pins = mv88e6xxx_num_gpio(chip);
@@ -891,4 +497,3 @@ void mv88e6xxx_ptp_free(struct mv88e6xxx_chip *chip)
if (chip->tai)
marvell_tai_remove(chip->tai);
}
-#endif
--
2.30.2
^ permalink raw reply related [flat|nested] 28+ messages in thread
* Re: [PATCH RFC net-next 1/5] net: mvpp2: add support for hardware timestamps
2025-04-11 21:26 ` [PATCH RFC net-next 1/5] net: mvpp2: add support for hardware timestamps Russell King
@ 2025-04-11 21:29 ` Russell King (Oracle)
2025-04-14 12:51 ` Kory Maincent
1 sibling, 0 replies; 28+ messages in thread
From: Russell King (Oracle) @ 2025-04-11 21:29 UTC (permalink / raw)
To: Andrew Lunn, Heiner Kallweit
Cc: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Kory Maincent, Marcin Wojtas, netdev, Paolo Abeni
On Fri, Apr 11, 2025 at 10:26:31PM +0100, Russell King wrote:
> Add support for hardware timestamps in (e.g.) the PHY by calling
> skb_tx_timestamp() as close as reasonably possible to the point that
> the hardware is instructed to send the queued packets.
>
> Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
This patch dates from prior to me joining Oracle, and is unchanged
apart from normal rebasing.
> ---
> drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c | 2 ++
> 1 file changed, 2 insertions(+)
>
> diff --git a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c
> index 416a926a8281..e3f8aa139d1e 100644
> --- a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c
> +++ b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c
> @@ -4439,6 +4439,8 @@ static netdev_tx_t mvpp2_tx(struct sk_buff *skb, struct net_device *dev)
> txq_pcpu->count += frags;
> aggr_txq->count += frags;
>
> + skb_tx_timestamp(skb);
> +
> /* Enable transmit */
> wmb();
> mvpp2_aggr_txq_pend_desc_add(port, frags);
> --
> 2.30.2
>
>
--
RMK's Patch system: https://www.armlinux.org.uk/developer/patches/
FTTP is here! 80Mbps down 10Mbps up. Decent connectivity at last!
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH RFC net-next 2/5] ptp: marvell: add core support for Marvell PTP v2.1
2025-04-11 21:26 ` [PATCH RFC net-next 2/5] ptp: marvell: add core support for Marvell PTP v2.1 Russell King
@ 2025-04-11 21:30 ` Russell King (Oracle)
2025-04-16 8:48 ` Kory Maincent
1 sibling, 0 replies; 28+ messages in thread
From: Russell King (Oracle) @ 2025-04-11 21:30 UTC (permalink / raw)
To: Andrew Lunn, Heiner Kallweit
Cc: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Kory Maincent, netdev, Paolo Abeni, Richard Cochran
On Fri, Apr 11, 2025 at 10:26:37PM +0100, Russell King wrote:
> Provide core support for the Marvell PTP v2.1 implementations, which
> consist of a TAI (time application interface) and timestamping blocks.
> This hardware can be found in Marvell 88E151x PHYs, Armada 38x and
> Armada 37xx (mvneta), as well as Marvell DSA devices.
>
> Support for both arrival timestamps is supported, we use arrival 1 for
> PTP peer delay messages, and arrival 0 for all other messages.
>
> External event capture is also supported.
>
> PPS output and trigger generation is not supported.
>
> This core takes inspiration from the existing Marvell 88E6xxx DSA PTP
> code and DP83640 drivers. Like the original 88E6xxx DSA code, we
> use a delayed work to keep the cycle counter updated, and a separate
> delayed work for event capture.
>
> We expose the ptp clock aux work to allow users to support single and
> multi-port designs - where there is one Marvell TAI instance and a
> number of Marvell TS instances.
>
> Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
> Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
These patches are from before I joined Oracle (hence the first sob) but
have seen further work (hence the second.) Same for the next patch.
> ---
> drivers/ptp/Kconfig | 4 +
> drivers/ptp/Makefile | 2 +
> drivers/ptp/ptp_marvell_tai.c | 449 +++++++++++++++++++++++++
> drivers/ptp/ptp_marvell_ts.c | 593 ++++++++++++++++++++++++++++++++++
> include/linux/marvell_ptp.h | 129 ++++++++
> 5 files changed, 1177 insertions(+)
> create mode 100644 drivers/ptp/ptp_marvell_tai.c
> create mode 100644 drivers/ptp/ptp_marvell_ts.c
> create mode 100644 include/linux/marvell_ptp.h
>
> diff --git a/drivers/ptp/Kconfig b/drivers/ptp/Kconfig
> index 07bf7f9aae01..27b54f37b9ab 100644
> --- a/drivers/ptp/Kconfig
> +++ b/drivers/ptp/Kconfig
> @@ -184,6 +184,10 @@ config PTP_1588_CLOCK_FC3W
> To compile this driver as a module, choose M here: the module
> will be called ptp_fc3.
>
> +config PTP_1588_CLOCK_MARVELL
> + tristate
> + depends on PTP_1588_CLOCK
> +
> config PTP_1588_CLOCK_MOCK
> tristate "Mock-up PTP clock"
> depends on PTP_1588_CLOCK
> diff --git a/drivers/ptp/Makefile b/drivers/ptp/Makefile
> index 25f846fe48c9..6248f75d9335 100644
> --- a/drivers/ptp/Makefile
> +++ b/drivers/ptp/Makefile
> @@ -12,6 +12,8 @@ obj-$(CONFIG_PTP_1588_CLOCK_INES) += ptp_ines.o
> obj-$(CONFIG_PTP_1588_CLOCK_PCH) += ptp_pch.o
> obj-$(CONFIG_PTP_1588_CLOCK_KVM) += ptp_kvm.o
> obj-$(CONFIG_PTP_1588_CLOCK_VMCLOCK) += ptp_vmclock.o
> +obj-$(CONFIG_PTP_1588_CLOCK_MARVELL) += ptp-marvell.o
> +ptp-marvell-y := ptp_marvell_tai.o ptp_marvell_ts.o
> obj-$(CONFIG_PTP_1588_CLOCK_QORIQ) += ptp-qoriq.o
> ptp-qoriq-y += ptp_qoriq.o
> ptp-qoriq-$(CONFIG_DEBUG_FS) += ptp_qoriq_debugfs.o
> diff --git a/drivers/ptp/ptp_marvell_tai.c b/drivers/ptp/ptp_marvell_tai.c
> new file mode 100644
> index 000000000000..eea7ccdce729
> --- /dev/null
> +++ b/drivers/ptp/ptp_marvell_tai.c
> @@ -0,0 +1,449 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * TAI (time application interface) driver for Marvell PHYs and Marvell NETA.
> + *
> + * This file implements TAI support as a PTP clock. Timecounter/cyclecounter
> + * representation taken from Marvell 88E6xxx DSA driver. We may need to share
> + * the TAI between multiple PHYs in a multiport PHY.
> + */
> +#include <linux/if_ether.h>
> +#include <linux/ktime.h>
> +#include <linux/slab.h>
> +#include <linux/marvell_ptp.h>
> +
> +#define TAI_CONFIG_0 0
> +#define TAI_CONFIG_0_EVENTCAPOV BIT(15)
> +#define TAI_CONFIG_0_EVENTCTRSTART BIT(14)
> +#define TAI_CONFIG_0_EVENTPHASE BIT(13)
> +#define TAI_CONFIG_0_TRIGGENINTEN BIT(9)
> +#define TAI_CONFIG_0_EVENTCAPINTEN BIT(8)
> +
> +#define TAI_CONFIG_9 9
> +#define TAI_CONFIG_9_EVENTCAPERR BIT(9)
> +#define TAI_CONFIG_9_EVENTCAPVALID BIT(8)
> +
> +#define TAI_EVENT_CAPTURE_TIME_LO 10
> +#define TAI_EVENT_CAPTURE_TIME_HI 11
> +
> +#define PTPG_CONFIG_0 0
> +#define PTPG_CONFIG_1 1
> +#define PTPG_CONFIG_2 2
> +#define PTPG_CONFIG_3 3
> +#define PTPG_CONFIG_3_TSATSFD BIT(0)
> +#define PTPG_STATUS 8
> +
> +#define TAI_EVENT_POLL_INTERVAL msecs_to_jiffies(100)
> +
> +struct marvell_tai {
> + const struct marvell_ptp_ops *ops;
> + struct device *dev;
> +
> + struct ptp_clock_info caps;
> + struct ptp_clock *ptp_clock;
> +
> + u32 cc_mult_num;
> + u32 cc_mult_den;
> + u32 cc_mult;
> +
> + struct mutex mutex;
> + struct timecounter timecounter;
> + struct cyclecounter cyclecounter;
> +
> + long half_overflow_period;
> + struct delayed_work overflow_work;
> +
> + bool defunct;
> + bool extts_poll;
> + struct delayed_work event_work;
> +
> + /* Used while reading the TAI */
> + struct ptp_system_timestamp *sts;
> +};
> +
> +static struct marvell_tai *cc_to_tai(const struct cyclecounter *cc)
> +{
> + return container_of(cc, struct marvell_tai, cyclecounter);
> +}
> +
> +/* Read the global time registers using the readplus command */
> +static u64 marvell_tai_clock_read(const struct cyclecounter *cc)
> +{
> + struct marvell_tai *tai = cc_to_tai(cc);
> +
> + return tai->ops->tai_clock_read(tai->dev, tai->sts);
> +}
> +
> +u64 marvell_tai_cyc2time(struct marvell_tai *tai, u32 cyc)
> +{
> + u64 ns;
> +
> + mutex_lock(&tai->mutex);
> + ns = timecounter_cyc2time(&tai->timecounter, cyc);
> + mutex_unlock(&tai->mutex);
> +
> + return ns;
> +}
> +EXPORT_SYMBOL_GPL(marvell_tai_cyc2time);
> +
> +static struct marvell_tai *ptp_to_tai(struct ptp_clock_info *ptp)
> +{
> + return container_of(ptp, struct marvell_tai, caps);
> +}
> +
> +static int marvell_tai_adjfine(struct ptp_clock_info *ptp, long scaled_ppm)
> +{
> + struct marvell_tai *tai = ptp_to_tai(ptp);
> + bool neg;
> + u32 diff;
> + u64 adj;
> +
> + neg = scaled_ppm < 0;
> + if (neg)
> + scaled_ppm = -scaled_ppm;
> +
> + adj = tai->cc_mult_num;
> + adj *= scaled_ppm;
> + diff = div_u64(adj, tai->cc_mult_den);
> +
> + mutex_lock(&tai->mutex);
> + timecounter_read(&tai->timecounter);
> + tai->cyclecounter.mult = neg ? tai->cc_mult - diff :
> + tai->cc_mult + diff;
> + mutex_unlock(&tai->mutex);
> +
> + return 0;
> +}
> +
> +static int marvell_tai_adjtime(struct ptp_clock_info *ptp, s64 delta)
> +{
> + struct marvell_tai *tai = ptp_to_tai(ptp);
> +
> + mutex_lock(&tai->mutex);
> + timecounter_adjtime(&tai->timecounter, delta);
> + mutex_unlock(&tai->mutex);
> +
> + return 0;
> +}
> +
> +static int marvell_tai_gettimex64(struct ptp_clock_info *ptp,
> + struct timespec64 *ts,
> + struct ptp_system_timestamp *sts)
> +{
> + struct marvell_tai *tai = ptp_to_tai(ptp);
> + u64 ns;
> +
> + mutex_lock(&tai->mutex);
> + tai->sts = sts;
> + ns = timecounter_read(&tai->timecounter);
> + tai->sts = NULL;
> + mutex_unlock(&tai->mutex);
> +
> + *ts = ns_to_timespec64(ns);
> +
> + return 0;
> +}
> +
> +static int marvell_tai_settime64(struct ptp_clock_info *ptp,
> + const struct timespec64 *ts)
> +{
> + struct marvell_tai *tai = ptp_to_tai(ptp);
> + u64 ns = timespec64_to_ns(ts);
> +
> + mutex_lock(&tai->mutex);
> + timecounter_init(&tai->timecounter, &tai->cyclecounter, ns);
> + mutex_unlock(&tai->mutex);
> +
> + return 0;
> +}
> +
> +static void marvell_tai_extts(struct marvell_tai *tai)
> +{
> + struct marvell_extts extts;
> + struct ptp_clock_event ev;
> + int err;
> +
> + err = tai->ops->tai_extts_read(tai->dev, TAI_CONFIG_9, &extts);
> + if (err <= 0)
> + return;
> +
> + if (extts.status & TAI_CONFIG_9_EVENTCAPERR) {
> + dev_warn(tai->dev, "extts timestamp overrun (%x)\n",
> + extts.status);
> + return;
> + }
> +
> + ev.type = PTP_CLOCK_EXTTS;
> + ev.index = 0;
> + ev.timestamp = marvell_tai_cyc2time(tai, extts.time);
> +
> + ptp_clock_event(tai->ptp_clock, &ev);
> +}
> +
> +static int marvell_tai_enable_extts(struct marvell_tai *tai,
> + struct ptp_extts_request *req, int enable)
> +{
> + int err, pin;
> + u16 cfg0;
> +
> + if (req->flags & ~(PTP_ENABLE_FEATURE | PTP_RISING_EDGE |
> + PTP_FALLING_EDGE | PTP_STRICT_FLAGS))
> + return -EINVAL;
> +
> + pin = ptp_find_pin(tai->ptp_clock, PTP_PF_EXTTS, req->index);
> + if (pin < 0)
> + return -EBUSY;
> +
> + /* Setup this pin, validating flags as appropriate */
> + err = tai->ops->tai_pin_setup(tai->dev, pin, req->flags, enable);
> + if (err < 0)
> + return err;
> +
> + if (enable) {
> + /* Clear the status */
> + err = tai->ops->tai_write(tai->dev, TAI_CONFIG_9, 0);
> + if (err < 0)
> + return err;
> +
> + cfg0 = TAI_CONFIG_0_EVENTCAPINTEN |
> + TAI_CONFIG_0_EVENTCTRSTART;
> +
> + /*
> + * For compatibility with DSA, we test for !rising rather
> + * than for falling.
> + */
> + if (!(req->flags & PTP_RISING_EDGE))
> + cfg0 |= TAI_CONFIG_0_EVENTPHASE;
> +
> + /* Enable the event interrupt and counter */
> + err = tai->ops->tai_modify(tai->dev, TAI_CONFIG_0,
> + TAI_CONFIG_0_EVENTCAPOV |
> + TAI_CONFIG_0_EVENTCTRSTART |
> + TAI_CONFIG_0_EVENTCAPINTEN, cfg0);
> + if (err < 0)
> + return err;
> +
> + schedule_delayed_work(&tai->event_work,
> + TAI_EVENT_POLL_INTERVAL);
> + } else {
> + /* Disable the event interrupt and counter */
> + err = tai->ops->tai_modify(tai->dev, TAI_CONFIG_0,
> + TAI_CONFIG_0_EVENTCTRSTART |
> + TAI_CONFIG_0_EVENTCAPINTEN, 0);
> + if (err < 0)
> + return err;
> +
> + cancel_delayed_work_sync(&tai->event_work);
> + }
> +
> + return 0;
> +}
> +
> +static int marvell_tai_enable(struct ptp_clock_info *ptp,
> + struct ptp_clock_request *req, int enable)
> +{
> + struct marvell_tai *tai = ptp_to_tai(ptp);
> + int err;
> +
> + switch (req->type) {
> + case PTP_PF_EXTTS:
> + err = marvell_tai_enable_extts(tai, &req->extts, enable);
> + break;
> +
> + default:
> + err = -EOPNOTSUPP;
> + break;
> + }
> +
> + return err;
> +}
> +
> +static int marvell_tai_verify(struct ptp_clock_info *ptp, unsigned int pin,
> + enum ptp_pin_function func, unsigned int chan)
> +{
> + struct marvell_tai *tai = ptp_to_tai(ptp);
> +
> + /* Always allow a pin to be set to no function */
> + if (func == PTP_PF_NONE)
> + return 0;
> +
> + if (!tai->ops->tai_pin_verify)
> + return -EOPNOTSUPP;
> +
> + return tai->ops->tai_pin_verify(tai->dev, pin, func, chan);
> +}
> +
> +/* Periodically read the timecounter to keep the time refreshed. */
> +static long marvell_tai_aux_work(struct ptp_clock_info *ptp)
> +{
> + struct marvell_tai *tai = ptp_to_tai(ptp);
> + long ret = -1;
> +
> + if (tai->ops->ptp_aux_work)
> + ret = tai->ops->ptp_aux_work(tai->dev);
> +
> + return ret;
> +}
> +
> +#define event_work_to_tai(w) \
> + container_of(to_delayed_work(w), struct marvell_tai, event_work)
> +static void marvell_tai_event_work(struct work_struct *w)
> +{
> + struct marvell_tai *tai = event_work_to_tai(w);
> +
> + if (tai->defunct)
> + return;
> +
> + marvell_tai_extts(tai);
> +
> + schedule_delayed_work(&tai->event_work, TAI_EVENT_POLL_INTERVAL);
> +}
> +
> +#define overflow_work_to_tai(w) \
> + container_of(to_delayed_work(w), struct marvell_tai, overflow_work)
> +static void marvell_tai_overflow_work(struct work_struct *w)
> +{
> + struct marvell_tai *tai = overflow_work_to_tai(w);
> +
> + /* Read the timecounter to update */
> + mutex_lock(&tai->mutex);
> + timecounter_read(&tai->timecounter);
> + mutex_unlock(&tai->mutex);
> +
> + schedule_delayed_work(&tai->overflow_work, tai->half_overflow_period);
> +}
> +
> +/* Configure the global (shared between ports) configuration for the PHY. */
> +static int marvell_tai_global_config(struct marvell_tai *tai)
> +{
> + int err;
> +
> + /* Enable TAI */
> + err = tai->ops->tai_enable(tai->dev);
> + if (err)
> + return err;
> +
> + /* Set ether-type for IEEE1588 packets */
> + err = tai->ops->ptp_global_write(tai->dev, PTPG_CONFIG_0, ETH_P_1588);
> + if (err < 0)
> + return err;
> +
> + /* MsdIDTSEn - Enable timestamping on all PTP MessageIDs */
> + err = tai->ops->ptp_global_write(tai->dev, PTPG_CONFIG_1,
> + MV_PTP_MSD_ID_TS_EN);
> + if (err < 0)
> + return err;
> +
> + /* TSArrPtr - Point to Arr0 registers */
> + err = tai->ops->ptp_global_write(tai->dev, PTPG_CONFIG_2,
> + MV_PTP_TS_ARR_PTR);
> + if (err < 0)
> + return err;
> +
> + /* TSAtSFD - timestamp at SFD */
> + err = tai->ops->ptp_global_write(tai->dev, PTPG_CONFIG_3,
> + PTPG_CONFIG_3_TSATSFD);
> + if (err < 0)
> + return err;
> +
> + return 0;
> +}
> +
> +int marvell_tai_ptp_clock_index(struct marvell_tai *tai)
> +{
> + return ptp_clock_index(tai->ptp_clock);
> +}
> +EXPORT_SYMBOL_GPL(marvell_tai_ptp_clock_index);
> +
> +int marvell_tai_schedule(struct marvell_tai *tai, unsigned long delay)
> +{
> + return ptp_schedule_worker(tai->ptp_clock, delay);
> +}
> +EXPORT_SYMBOL_GPL(marvell_tai_schedule);
> +
> +int marvell_tai_probe(struct marvell_tai **taip,
> + const struct marvell_ptp_ops *ops,
> + const struct marvell_tai_param *param,
> + struct ptp_pin_desc *pin_config, int n_pins,
> + const char *name, struct device *dev)
> +{
> + struct marvell_tai *tai;
> + u64 overflow_ns;
> + int err;
> +
> + tai = devm_kzalloc(dev, sizeof(*tai), GFP_KERNEL);
> + if (!tai)
> + return -ENOMEM;
> +
> + mutex_init(&tai->mutex);
> +
> + tai->dev = dev;
> + tai->ops = ops;
> + tai->cc_mult_num = param->cc_mult_num;
> + tai->cc_mult_den = param->cc_mult_den;
> + tai->cc_mult = param->cc_mult;
> +
> + err = marvell_tai_global_config(tai);
> + if (err < 0)
> + return err;
> +
> + tai->cyclecounter.read = marvell_tai_clock_read;
> + tai->cyclecounter.mask = CYCLECOUNTER_MASK(32);
> + tai->cyclecounter.mult = param->cc_mult;
> + tai->cyclecounter.shift = param->cc_shift;
> +
> + overflow_ns = BIT_ULL(32) * param->cc_mult;
> + overflow_ns >>= param->cc_shift;
> + tai->half_overflow_period = nsecs_to_jiffies64(overflow_ns / 2);
> +
> + timecounter_init(&tai->timecounter, &tai->cyclecounter,
> + ktime_to_ns(ktime_get_real()));
> +
> + tai->caps.owner = THIS_MODULE;
> + strscpy(tai->caps.name, name, sizeof(tai->caps.name));
> + /* max_adj of 1000000 is what MV88E6xxx DSA uses */
> + tai->caps.max_adj = 1000000;
> + tai->caps.n_ext_ts = param->n_ext_ts;
> + tai->caps.n_pins = n_pins;
> + tai->caps.pin_config = pin_config;
> + tai->caps.adjfine = marvell_tai_adjfine;
> + tai->caps.adjtime = marvell_tai_adjtime;
> + tai->caps.gettimex64 = marvell_tai_gettimex64;
> + tai->caps.settime64 = marvell_tai_settime64;
> + tai->caps.enable = marvell_tai_enable;
> + tai->caps.verify = marvell_tai_verify;
> + tai->caps.do_aux_work = marvell_tai_aux_work;
> +
> + INIT_DELAYED_WORK(&tai->overflow_work, marvell_tai_overflow_work);
> + INIT_DELAYED_WORK(&tai->event_work, marvell_tai_event_work);
> +
> + tai->ptp_clock = ptp_clock_register(&tai->caps, dev);
> + if (IS_ERR(tai->ptp_clock)) {
> + kfree(tai);
> + return PTR_ERR(tai->ptp_clock);
> + }
> +
> + /*
> + * Kick off the auxiliary worker to run once every half-overflow
> + * period to keep the timecounter properly updated.
> + */
> + schedule_delayed_work(&tai->overflow_work, tai->half_overflow_period);
> +
> + *taip = tai;
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(marvell_tai_probe);
> +
> +void marvell_tai_remove(struct marvell_tai *tai)
> +{
> + /* Avoid races with the event work - mark defunct before
> + * unregistering, which goes against "unpublish then tear down"
> + */
> + tai->defunct = true;
> + cancel_delayed_work_sync(&tai->event_work);
> +
> + ptp_clock_unregister(tai->ptp_clock);
> +
> + cancel_delayed_work_sync(&tai->overflow_work);
> +}
> +EXPORT_SYMBOL_GPL(marvell_tai_remove);
> diff --git a/drivers/ptp/ptp_marvell_ts.c b/drivers/ptp/ptp_marvell_ts.c
> new file mode 100644
> index 000000000000..a2e1ae9e4acc
> --- /dev/null
> +++ b/drivers/ptp/ptp_marvell_ts.c
> @@ -0,0 +1,593 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * Marvell PTP driver for 88E1510, 88E1512, 88E1514 and 88E1518 PHYs
> + *
> + * Ideas taken from 88E6xxx DSA and DP83640 drivers. This file
> + * implements the packet timestamping support only (PTP). TAI
> + * support is separate.
> + */
> +#include <linux/ethtool.h>
> +#include <linux/if_vlan.h>
> +#include <linux/interrupt.h>
> +#include <linux/marvell_ptp.h>
> +#include <linux/netdevice.h>
> +#include <linux/ptp_classify.h>
> +#include <linux/ptp_clock_kernel.h>
> +#include <linux/uaccess.h>
> +
> +#define TX_TIMEOUT_MS 40
> +#define RX_TIMEOUT_MS 40
> +
> +#define PTP_PORT_CONFIG_0 0
> +#define PTP_PORT_CONFIG_0_DISTSPECCHECK BIT(11)
> +#define PTP_PORT_CONFIG_0_DISTSOVERWRITE BIT(1)
> +#define PTP_PORT_CONFIG_0_DISPTP BIT(0)
> +#define PTP_PORT_CONFIG_1 1
> +#define PTP_PORT_CONFIG_1_IPJUMP(x) (((x) & 0x3f) << 8)
> +#define PTP_PORT_CONFIG_1_ETJUMP(x) ((x) & 0x1f)
> +#define PTP_PORT_CONFIG_2 2
> +#define PTP_PORT_CONFIG_2_DEPINTEN BIT(1)
> +#define PTP_PORT_CONFIG_2_ARRINTEN BIT(0)
> +#define PTP_ARR_STATUS0 8
> +#define PTP_ARR_STATUS1 12
> +#define PTP_DEP_STATUS 16
> +
> +struct marvell_ptp_cb {
> + unsigned long timeout;
> + u16 seq;
> +};
> +#define MARVELL_PTP_CB(skb) ((struct marvell_ptp_cb *)(skb)->cb)
> +
> +/* RX queue support */
> +
> +/* Deliver a skb with its timestamp back to the networking core */
> +static void marvell_rxq_rx(struct sk_buff *skb, u64 ns)
> +{
> + struct skb_shared_hwtstamps *shhwtstamps = skb_hwtstamps(skb);
> +
> + memset(shhwtstamps, 0, sizeof(*shhwtstamps));
> + shhwtstamps->hwtstamp = ns_to_ktime(ns);
> + netif_rx(skb);
> +}
> +
> +/* Get a rx timestamp entry. Try the free list, and if that fails,
> + * steal the oldest off the pending list.
> + */
> +static struct marvell_rxts *marvell_rxq_get_rxts(struct marvell_rxq *rxq)
> +{
> + if (!list_empty(&rxq->rx_free))
> + return list_first_entry(&rxq->rx_free, struct marvell_rxts,
> + node);
> +
> + return list_last_entry(&rxq->rx_pend, struct marvell_rxts, node);
> +}
> +
> +static void marvell_rxq_init(struct marvell_rxq *rxq)
> +{
> + int i;
> +
> + mutex_init(&rxq->rx_mutex);
> + INIT_LIST_HEAD(&rxq->rx_free);
> + INIT_LIST_HEAD(&rxq->rx_pend);
> + skb_queue_head_init(&rxq->rx_queue);
> +
> + for (i = 0; i < ARRAY_SIZE(rxq->rx_ts); i++)
> + list_add_tail(&rxq->rx_ts[i].node, &rxq->rx_free);
> +}
> +
> +static void marvell_rxq_purge(struct marvell_rxq *rxq)
> +{
> + skb_queue_purge(&rxq->rx_queue);
> +}
> +
> +static void marvell_rxq_rx_ts(struct marvell_rxq *rxq, u16 seq, u64 ns)
> +{
> + struct marvell_rxts *rxts;
> + struct sk_buff *skb;
> + bool found = false;
> +
> + mutex_lock(&rxq->rx_mutex);
> +
> + /* Search the rx queue for a matching skb */
> + skb_queue_walk(&rxq->rx_queue, skb) {
> + if (MARVELL_PTP_CB(skb)->seq == seq) {
> + __skb_unlink(skb, &rxq->rx_queue);
> + found = true;
> + break;
> + }
> + }
> +
> + if (!found) {
> + rxts = marvell_rxq_get_rxts(rxq);
> + rxts->ns = ns;
> + rxts->seq = seq;
> + list_move(&rxts->node, &rxq->rx_pend);
> + }
> +
> + mutex_unlock(&rxq->rx_mutex);
> +
> + if (found)
> + marvell_rxq_rx(skb, ns);
> +}
> +
> +static bool marvell_rxq_rxtstamp(struct marvell_rxq *rxq, struct sk_buff *skb,
> + u16 seq)
> +{
> + struct marvell_rxts *rxts;
> + bool found = false;
> + u64 ns;
> +
> + mutex_lock(&rxq->rx_mutex);
> +
> + /* Search the pending receive timestamps for a matching seqid */
> + list_for_each_entry(rxts, &rxq->rx_pend, node) {
> + if (rxts->seq == seq) {
> + found = true;
> + ns = rxts->ns;
> + /* Move this timestamp entry to the free list */
> + list_move_tail(&rxts->node, &rxq->rx_free);
> + break;
> + }
> + }
> +
> + if (!found) {
> + /* Store the seqid and queue the skb. Do this under the lock
> + * to ensure we don't miss any timestamps appended to the
> + * rx_pend list.
> + */
> + MARVELL_PTP_CB(skb)->seq = seq;
> + MARVELL_PTP_CB(skb)->timeout = jiffies +
> + msecs_to_jiffies(RX_TIMEOUT_MS);
> + __skb_queue_tail(&rxq->rx_queue, skb);
> + }
> +
> + mutex_unlock(&rxq->rx_mutex);
> +
> + if (found)
> + /* We found the corresponding timestamp. If we can add the
> + * timestamp, do we need to go through the netif_rx_ni()
> + * path, or would it be more efficient to add the timestamp
> + * and return "false" from marvell_ptp_rxtstamp() instead?
> + */
> + marvell_rxq_rx(skb, ns);
> +
> + return found;
> +}
> +
> +static void marvell_rxq_expire(struct marvell_rxq *rxq,
> + struct sk_buff_head *list)
> +{
> + struct sk_buff *skb;
> +
> + mutex_lock(&rxq->rx_mutex);
> + while ((skb = skb_dequeue(&rxq->rx_queue)) != NULL) {
> + if (!time_is_before_jiffies(MARVELL_PTP_CB(skb)->timeout)) {
> + __skb_queue_head(&rxq->rx_queue, skb);
> + break;
> + }
> + __skb_queue_tail(list, skb);
> + }
> + mutex_unlock(&rxq->rx_mutex);
> +}
> +
> +/* Extract the sequence ID */
> +static u16 ptp_seqid(const struct ptp_header *ptp_hdr)
> +{
> + const __be16 *seqp = &ptp_hdr->sequence_id;
> +
> + return be16_to_cpup(seqp);
> +}
> +
> +static u8 ptp_msgid(const struct ptp_header *ptp_hdr)
> +{
> + return ptp_hdr->tsmt & 15;
> +}
> +
> +static void marvell_ptp_schedule(struct marvell_ptp *ptp)
> +{
> + marvell_tai_schedule(ptp->tai, 0);
> +}
> +
> +/* Check for a rx timestamp entry, try to find the corresponding skb and
> + * deliver it, otherwise add the rx timestamp to the queue of pending
> + * timestamps.
> + */
> +static int marvell_ptp_rx_ts(struct marvell_ptp *ptp, int q)
> +{
> + struct marvell_ts ts;
> + u16 reg;
> + int err;
> + u64 ns;
> +
> + if (q)
> + reg = PTP_ARR_STATUS1;
> + else
> + reg = PTP_ARR_STATUS0;
> +
> + err = ptp->ops->ptp_port_read_ts(ptp->dev, &ts, reg);
> + if (err <= 0)
> + return 0;
> +
> + if ((ts.stat & MV_STATUS_INTSTATUS_MASK) !=
> + MV_STATUS_INTSTATUS_NORMAL)
> + dev_warn(ptp->dev,
> + "rx timestamp overrun (q=%u stat=0x%x seq=%u)\n",
> + q, ts.stat, ts.seq);
> +
> + ns = marvell_tai_cyc2time(ptp->tai, ts.time);
> +
> + marvell_rxq_rx_ts(&ptp->rxq[q], ts.seq, ns);
> +
> + return 1;
> +}
> +
> +/* Check whether the packet is suitable for timestamping, and if so,
> + * try to find a pending timestamp for it. If no timestamp is found,
> + * queue the packet with a timeout.
> + */
> +bool marvell_ptp_rxtstamp(struct marvell_ptp *ptp, struct sk_buff *skb,
> + int type)
> +{
> + const struct ptp_header *ptp_hdr;
> + u16 msgidvec, seq;
> + u8 msgid;
> + int q;
> +
> + if (ptp->rx_filter == HWTSTAMP_FILTER_NONE)
> + return false;
> +
> + ptp_hdr = ptp_parse_header(skb, type);
> + if (!ptp_hdr)
> + return false;
> +
> + msgid = ptp_msgid(ptp_hdr);
> + seq = ptp_seqid(ptp_hdr);
> +
> + /* Only check for timestamps for PTP packets whose message ID value
> + * is one that we are capturing timestamps for. This is part of the
> + * global configuration and is therefore fixed.
> + */
> + msgidvec = BIT(msgid);
> + if (msgidvec & ~MV_PTP_MSD_ID_TS_EN) {
> + dev_dbg(ptp->dev, "not timestamping rx msgid %u seq %u\n",
> + msgid, seq);
> + return false;
> + }
> +
> + /* Determine the queue which the timestamp for this message ID will
> + * appear. This is part of the global configuration and is therefore
> + * fixed.
> + */
> + q = !!(msgidvec & MV_PTP_TS_ARR_PTR);
> +
> + if (!marvell_rxq_rxtstamp(&ptp->rxq[q], skb, seq))
> + marvell_ptp_schedule(ptp);
> +
> + return true;
> +}
> +EXPORT_SYMBOL_GPL(marvell_ptp_rxtstamp);
> +
> +/* Move any expired skbs on to our own list, and then hand the contents of
> + * our list to netif_rx() - this avoids calling netif_rx() with our
> + * mutex held.
> + */
> +static void marvell_ptp_rx_expire(struct marvell_ptp *ptp)
> +{
> + struct sk_buff_head list;
> + struct sk_buff *skb;
> + int i;
> +
> + __skb_queue_head_init(&list);
> +
> + for (i = 0; i < ARRAY_SIZE(ptp->rxq); i++)
> + marvell_rxq_expire(&ptp->rxq[i], &list);
> +
> + while ((skb = __skb_dequeue(&list)) != NULL)
> + netif_rx(skb);
> +}
> +
> +/* Complete the transmit timestamping; this is called to read the transmit
> + * timestamp from the PHY, and report back the transmitted timestamp.
> + */
> +static int marvell_ptp_txtstamp_complete(struct marvell_ptp *ptp)
> +{
> + struct skb_shared_hwtstamps shhwtstamps;
> + struct sk_buff *skb = ptp->tx_skb;
> + struct marvell_ts ts;
> + int err;
> + u64 ns;
> +
> + err = ptp->ops->ptp_port_read_ts(ptp->dev, &ts, PTP_DEP_STATUS);
> + if (err < 0)
> + goto fail;
> +
> + if (err == 0) {
> + if (time_is_before_jiffies(MARVELL_PTP_CB(skb)->timeout)) {
> + dev_warn(ptp->dev, "tx timestamp timeout\n");
> + goto free;
> + }
> + return 0;
> + }
> +
> + /* Check the status */
> + if ((ts.stat & MV_STATUS_INTSTATUS_MASK) !=
> + MV_STATUS_INTSTATUS_NORMAL) {
> + dev_warn(ptp->dev, "tx timestamp overrun (stat=0x%x seq=%u)\n",
> + ts.stat, ts.seq);
> + goto free;
> + }
> +
> + /* Reject if the sequence number doesn't match */
> + if (ts.seq != MARVELL_PTP_CB(skb)->seq) {
> + dev_warn(ptp->dev, "tx timestamp unexpected sequence id\n");
> + goto free;
> + }
> +
> + ptp->tx_skb = NULL;
> +
> + /* Set the timestamp */
> + ns = marvell_tai_cyc2time(ptp->tai, ts.time);
> + memset(&shhwtstamps, 0, sizeof(shhwtstamps));
> + shhwtstamps.hwtstamp = ns_to_ktime(ns);
> + skb_complete_tx_timestamp(skb, &shhwtstamps);
> + return 1;
> +
> +fail:
> + dev_err_ratelimited(ptp->dev, "failed reading PTP: %pe\n",
> + ERR_PTR(err));
> +free:
> + dev_kfree_skb_any(skb);
> + ptp->tx_skb = NULL;
> + return -1;
> +}
> +
> +/* Check whether the skb will be timestamped on transmit; we only support
> + * a single outstanding skb. Add it if the slot is available.
> + */
> +static bool marvell_ptp_do_txtstamp(struct marvell_ptp *ptp,
> + struct sk_buff *skb, int type)
> +{
> + const struct ptp_header *ptp_hdr;
> + u8 msgid;
> +
> + if (ptp->tx_type != HWTSTAMP_TX_ON)
> + return false;
> +
> + if (!(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP))
> + return false;
> +
> + ptp_hdr = ptp_parse_header(skb, type);
> + if (!ptp_hdr)
> + return false;
> +
> + msgid = ptp_msgid(ptp_hdr);
> + if (BIT(msgid) & ~MV_PTP_MSD_ID_TS_EN) {
> + dev_dbg(ptp->dev, "not timestamping tx msgid %u seq %u\n",
> + msgid, ptp_seqid(ptp_hdr));
> + return false;
> + }
> +
> + MARVELL_PTP_CB(skb)->seq = ptp_seqid(ptp_hdr);
> + MARVELL_PTP_CB(skb)->timeout = jiffies +
> + msecs_to_jiffies(TX_TIMEOUT_MS);
> +
> + if (cmpxchg(&ptp->tx_skb, NULL, skb) != NULL)
> + return false;
> +
> + /* DP83640 marks the skb for hw timestamping. Since the MAC driver
> + * may call skb_tx_timestamp() but may not support timestamping
> + * itself, it may not set this flag. So, we need to do this here.
> + */
> + skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS;
> + marvell_ptp_schedule(ptp);
> +
> + return true;
> +}
> +
> +void marvell_ptp_txtstamp(struct marvell_ptp *ptp, struct sk_buff *skb,
> + int type)
> +{
> + if (!marvell_ptp_do_txtstamp(ptp, skb, type))
> + kfree_skb(skb);
> +}
> +EXPORT_SYMBOL_GPL(marvell_ptp_txtstamp);
> +
> +int marvell_ptp_hwtstamp(struct marvell_ptp *ptp,
> + struct kernel_hwtstamp_config *kcfg,
> + struct netlink_ext_ack *ack)
> +{
> + u16 cfg0 = PTP_PORT_CONFIG_0_DISPTP;
> + u16 cfg2 = 0;
> + int err;
> +
> + if (kcfg->flags)
> + return -EINVAL;
> +
> + switch (kcfg->tx_type) {
> + case HWTSTAMP_TX_OFF:
> + break;
> +
> + case HWTSTAMP_TX_ON:
> + cfg0 = 0;
> + cfg2 |= PTP_PORT_CONFIG_2_DEPINTEN;
> + break;
> +
> + default:
> + return -ERANGE;
> + }
> +
> + switch (kcfg->rx_filter) {
> + case HWTSTAMP_FILTER_NONE:
> + break;
> +
> + case HWTSTAMP_FILTER_PTP_V1_L4_EVENT:
> + case HWTSTAMP_FILTER_PTP_V1_L4_SYNC:
> + case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ:
> + case HWTSTAMP_FILTER_PTP_V2_L4_EVENT:
> + case HWTSTAMP_FILTER_PTP_V2_L4_SYNC:
> + case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ:
> + case HWTSTAMP_FILTER_PTP_V2_L2_EVENT:
> + case HWTSTAMP_FILTER_PTP_V2_L2_SYNC:
> + case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ:
> + case HWTSTAMP_FILTER_PTP_V2_EVENT:
> + case HWTSTAMP_FILTER_PTP_V2_SYNC:
> + case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ:
> + /* We accept 802.1AS, IEEE 1588v1 and IEEE 1588v2. We could
> + * filter on 802.1AS using the transportSpecific field, but
> + * that affects the transmit path too.
> + */
> + kcfg->rx_filter = HWTSTAMP_FILTER_SOME;
> + cfg0 = 0;
> + cfg2 |= PTP_PORT_CONFIG_2_ARRINTEN;
> + break;
> +
> + default:
> + return -ERANGE;
> + }
> +
> + err = ptp->ops->ptp_port_modify(ptp->dev, PTP_PORT_CONFIG_0,
> + PTP_PORT_CONFIG_0_DISPTP, cfg0);
> + if (err)
> + return err;
> +
> + err = ptp->ops->ptp_port_write(ptp->dev, PTP_PORT_CONFIG_2, cfg2);
> + if (err)
> + return err;
> +
> + ptp->tx_type = kcfg->tx_type;
> + ptp->rx_filter = kcfg->rx_filter;
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(marvell_ptp_hwtstamp);
> +
> +int marvell_ptp_ts_info(struct marvell_ptp *ptp,
> + struct kernel_ethtool_ts_info *ts_info)
> +{
> + ts_info->so_timestamping = SOF_TIMESTAMPING_TX_HARDWARE |
> + SOF_TIMESTAMPING_RX_HARDWARE |
> + SOF_TIMESTAMPING_RAW_HARDWARE;
> + ts_info->phc_index = marvell_tai_ptp_clock_index(ptp->tai);
> + ts_info->tx_types = BIT(HWTSTAMP_TX_OFF) |
> + BIT(HWTSTAMP_TX_ON);
> + ts_info->rx_filters = BIT(HWTSTAMP_FILTER_NONE) |
> + BIT(HWTSTAMP_FILTER_SOME);
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(marvell_ptp_ts_info);
> +
> +static int marvell_ptp_port_config(struct marvell_ptp *ptp)
> +{
> + int err;
> +
> + /* Disable transport specific check (if the PTP common header)
> + * Disable timestamp overwriting (so we can read a stable entry.)
> + * Disable PTP
> + */
> + err = ptp->ops->ptp_port_write(ptp->dev, PTP_PORT_CONFIG_0,
> + PTP_PORT_CONFIG_0_DISTSPECCHECK |
> + PTP_PORT_CONFIG_0_DISTSOVERWRITE |
> + PTP_PORT_CONFIG_0_DISPTP);
> + if (err < 0)
> + return err;
> +
> + /* Set ether-type jump to 12 (to ether protocol)
> + * Set IP jump to 2 (to skip over ether protocol)
> + * Does this mean it won't pick up on VLAN packets?
> + */
> + err = ptp->ops->ptp_port_write(ptp->dev, PTP_PORT_CONFIG_1,
> + PTP_PORT_CONFIG_1_ETJUMP(12) |
> + PTP_PORT_CONFIG_1_IPJUMP(2));
> + if (err < 0)
> + return err;
> +
> + /* Disable all interrupts */
> + ptp->ops->ptp_port_write(ptp->dev, PTP_PORT_CONFIG_2, 0);
> +
> + return 0;
> +}
> +
> +static void marvell_ptp_port_disable(struct marvell_ptp *ptp)
> +{
> + /* Disable PTP */
> + ptp->ops->ptp_port_write(ptp->dev, PTP_PORT_CONFIG_0,
> + PTP_PORT_CONFIG_0_DISPTP);
> +
> + /* Disable interrupts */
> + ptp->ops->ptp_port_write(ptp->dev, PTP_PORT_CONFIG_2, 0);
> +}
> +
> +long marvell_ptp_aux_work(struct marvell_ptp *ptp)
> +{
> + if (ptp->tx_skb)
> + marvell_ptp_txtstamp_complete(ptp);
> +
> + marvell_ptp_rx_ts(ptp, 0);
> + marvell_ptp_rx_ts(ptp, 1);
> + marvell_ptp_rx_expire(ptp);
> +
> + if (ptp->tx_skb)
> + return 0;
> + else if (!skb_queue_empty(&ptp->rxq[0].rx_queue) ||
> + !skb_queue_empty(&ptp->rxq[1].rx_queue))
> + return 1;
> +
> + return -1;
> +}
> +EXPORT_SYMBOL_GPL(marvell_ptp_aux_work);
> +
> +irqreturn_t marvell_ptp_irq(struct marvell_ptp *ptp)
> +{
> + irqreturn_t ret = IRQ_NONE;
> +
> + if (marvell_ptp_rx_ts(ptp, 0))
> + ret = IRQ_HANDLED;
> +
> + if (marvell_ptp_rx_ts(ptp, 1))
> + ret = IRQ_HANDLED;
> +
> + if (ptp->tx_skb && marvell_ptp_txtstamp_complete(ptp))
> + ret = IRQ_HANDLED;
> +
> + return ret;
> +}
> +EXPORT_SYMBOL_GPL(marvell_ptp_irq);
> +
> +int marvell_ptp_probe(struct marvell_ptp *ptp, struct device *dev,
> + struct marvell_tai *tai,
> + const struct marvell_ptp_ops *ops)
> +{
> + int i;
> +
> + ptp->ops = ops;
> + ptp->dev = dev;
> + ptp->tai = tai;
> +
> + for (i = 0; i < ARRAY_SIZE(ptp->rxq); i++)
> + marvell_rxq_init(&ptp->rxq[i]);
> +
> + /* Configure this PTP port */
> + return marvell_ptp_port_config(ptp);
> +}
> +EXPORT_SYMBOL_GPL(marvell_ptp_probe);
> +
> +void marvell_ptp_remove(struct marvell_ptp *ptp)
> +{
> + int i;
> +
> + /* Free or dequeue all pending skbs */
> + if (ptp->tx_skb)
> + kfree_skb(ptp->tx_skb);
> +
> + for (i = 0; i < ARRAY_SIZE(ptp->rxq); i++)
> + marvell_rxq_purge(&ptp->rxq[i]);
> +
> + /* Ensure that the port is disabled */
> + marvell_ptp_port_disable(ptp);
> +}
> +EXPORT_SYMBOL_GPL(marvell_ptp_remove);
> +
> +MODULE_AUTHOR("Russell King");
> +MODULE_DESCRIPTION("Marvell PTP library");
> +MODULE_LICENSE("GPL v2");
> diff --git a/include/linux/marvell_ptp.h b/include/linux/marvell_ptp.h
> new file mode 100644
> index 000000000000..6e515648abaa
> --- /dev/null
> +++ b/include/linux/marvell_ptp.h
> @@ -0,0 +1,129 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +#ifndef LINUX_MARVELL_PTP_H
> +#define LINUX_MARVELL_PTP_H
> +
> +#include <linux/irqreturn.h>
> +#include <linux/list.h>
> +#include <linux/mutex.h>
> +#include <linux/net_tstamp.h>
> +#include <linux/ptp_classify.h>
> +#include <linux/ptp_clock_kernel.h>
> +#include <linux/skbuff.h>
> +#include <linux/timecounter.h>
> +
> +struct device;
> +struct ifreq;
> +struct kernel_ethtool_ts_info;
> +struct marvell_tai;
> +struct netlink_ext_ack;
> +
> +#define MV_PTP_MSGTYPE_DELAY_RESP 9
> +
> +/* This defines which incoming or outgoing PTP frames are timestampped */
> +#define MV_PTP_MSD_ID_TS_EN (BIT(PTP_MSGTYPE_SYNC) | \
> + BIT(PTP_MSGTYPE_DELAY_REQ) | \
> + BIT(MV_PTP_MSGTYPE_DELAY_RESP))
> +/* Direct Sync messages to Arr0 and delay messages to Arr1 */
> +#define MV_PTP_TS_ARR_PTR (BIT(PTP_MSGTYPE_DELAY_REQ) | \
> + BIT(MV_PTP_MSGTYPE_DELAY_RESP))
> +
> +struct marvell_extts {
> + u32 time;
> + u8 status;
> +#define MV_STATUS_EVENTCAPVALID BIT(8)
> +};
> +
> +struct marvell_ts {
> + u32 time;
> + u16 stat;
> +#define MV_STATUS_INTSTATUS_MASK 0x0006
> +#define MV_STATUS_INTSTATUS_NORMAL 0x0000
> +#define MV_STATUS_VALID BIT(0)
> + u16 seq;
> +};
> +
> +struct marvell_ptp_ops {
> + int (*tai_enable)(struct device *dev);
> + u64 (*tai_clock_read)(struct device *dev,
> + struct ptp_system_timestamp *sts);
> + int (*tai_extts_read)(struct device *dev, int reg,
> + struct marvell_extts *extts);
> + int (*tai_pin_verify)(struct device *dev, int pin,
> + enum ptp_pin_function func, unsigned int chan);
> + int (*tai_pin_setup)(struct device *dev, int pin, unsigned int flags,
> + int enable);
> + int (*tai_write)(struct device *dev, u8 reg, u16 val);
> + int (*tai_modify)(struct device *dev, u8 reg, u16 mask, u16 val);
> + int (*ptp_global_write)(struct device *dev, u8 reg, u16 val);
> + int (*ptp_port_read_ts)(struct device *dev, struct marvell_ts *ts,
> + u8 reg);
> + int (*ptp_port_write)(struct device *dev, u8 reg, u16 val);
> + int (*ptp_port_modify)(struct device *dev, u8 reg, u16 mask, u16 val);
> + long (*ptp_aux_work)(struct device *dev);
> +};
> +
> +/* TAI module */
> +struct marvell_tai_param {
> + u32 cc_mult_num;
> + u32 cc_mult_den;
> + u32 cc_mult;
> + int cc_shift;
> +
> + int n_ext_ts;
> +};
> +
> +u64 marvell_tai_cyc2time(struct marvell_tai *tai, u32 cyc);
> +int marvell_tai_ptp_clock_index(struct marvell_tai *tai);
> +int marvell_tai_schedule(struct marvell_tai *tai, unsigned long delay);
> +int marvell_tai_probe(struct marvell_tai **taip,
> + const struct marvell_ptp_ops *ops,
> + const struct marvell_tai_param *param,
> + struct ptp_pin_desc *pin_config, int n_pins,
> + const char *name, struct device *dev);
> +void marvell_tai_remove(struct marvell_tai *tai);
> +
> +/* Timestamping module */
> +struct marvell_rxts {
> + struct list_head node;
> + u64 ns;
> + u16 seq;
> +};
> +
> +struct marvell_rxq {
> + struct mutex rx_mutex;
> + struct list_head rx_free;
> + struct list_head rx_pend;
> + struct sk_buff_head rx_queue;
> + struct marvell_rxts rx_ts[64];
> +};
> +
> +struct marvell_ptp {
> + struct marvell_tai *tai;
> + const struct marvell_ptp_ops *ops;
> + struct device *dev;
> +
> + /* We only support one outstanding transmit skb */
> + struct sk_buff *tx_skb;
> + enum hwtstamp_tx_types tx_type;
> +
> + struct marvell_rxq rxq[2];
> + enum hwtstamp_rx_filters rx_filter;
> +};
> +
> +bool marvell_ptp_rxtstamp(struct marvell_ptp *ptp, struct sk_buff *skb,
> + int type);
> +void marvell_ptp_txtstamp(struct marvell_ptp *ptp, struct sk_buff *skb,
> + int type);
> +int marvell_ptp_hwtstamp(struct marvell_ptp *ptp,
> + struct kernel_hwtstamp_config *kcfg,
> + struct netlink_ext_ack *ack);
> +int marvell_ptp_ts_info(struct marvell_ptp *ptp,
> + struct kernel_ethtool_ts_info *ts_info);
> +long marvell_ptp_aux_work(struct marvell_ptp *ptp);
> +irqreturn_t marvell_ptp_irq(struct marvell_ptp *ptp);
> +int marvell_ptp_probe(struct marvell_ptp *ptp, struct device *dev,
> + struct marvell_tai *tai,
> + const struct marvell_ptp_ops *ops);
> +void marvell_ptp_remove(struct marvell_ptp *ptp);
> +
> +#endif
> --
> 2.30.2
>
>
--
RMK's Patch system: https://www.armlinux.org.uk/developer/patches/
FTTP is here! 80Mbps down 10Mbps up. Decent connectivity at last!
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH RFC net-next 0/5] Marvell PTP support
2025-04-11 21:26 [PATCH RFC net-next 0/5] Marvell PTP support Russell King (Oracle)
` (4 preceding siblings ...)
2025-04-11 21:26 ` [PATCH RFC net-next 5/5] mv88e6xxx: cleanup ptp tai Russell King (Oracle)
@ 2025-04-11 21:30 ` Russell King (Oracle)
2025-04-16 10:51 ` Russell King (Oracle)
6 siblings, 0 replies; 28+ messages in thread
From: Russell King (Oracle) @ 2025-04-11 21:30 UTC (permalink / raw)
To: Andrew Lunn, Heiner Kallweit
Cc: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Kory Maincent, Marcin Wojtas, netdev, Paolo Abeni,
Richard Cochran, Vladimir Oltean
On Fri, Apr 11, 2025 at 10:26:15PM +0100, Russell King (Oracle) wrote:
> Hi,
>
> This series is a work in progress, and represents the current state of
> things, superseding Kory's patches which were based in a very old
> version of my patches - and my patches were subsequently refactored
> and further developed about five years ago. Due to them breaking
> mvpp2 if merged, there was no point in posting them until such time
> that the underlying issues with PTP were resolved - and they now have
> been.
>
> Marvell re-uses their PTP IP in several of their products - PHYs,
> switches and even some ethernet MACs contain the same IP. It really
> doesn't make sense to duplicate the code in each of these use cases.
>
> Therefore, this series introduces a Marvell PTP core that can be
> re-used - a TAI module, which handles the global parts of the PTP
> core, and the TS module, which handles the per-port timestamping.
>
> I will note at this point that although the Armada 388 TRM states that
> NETA contains the same IP, attempts to access the registers returns
> zero, and it is not known if that is due to the board missing something
> or whether it isn't actually implemented. I do have some early work
> re-using this, but when I discovered that the TAI registers read as
> zero and wouldn't accept writes, I haven't progressed that.
>
> Today, I have converted the mv88e6xxx DSA code to use the Marvell TAI
> module from patch 1, and for the sake of getting the code out there,
Correction: patch 2.
> I have included the "hacky" patches in this series - with the issues
> with DSA VLANs that I reported this evening and subsequently
> investigated, I've not had any spare time to properly prepare that
> part of this series. (Being usurped from phylink by stmmac - for which
> I have a big stack of patches that I can't get out because of being
> usurped, and then again by Marvell PTP, and then again by DSA VLAN
> stuff... yea, I'm feeling like I have zero time to do anything right
> now.) The mv88e6xxx DSA code still needs to be converted to use the
> Marvell TS part of patch 1, but I won't be able to test that after
> Sunday, and I'm certainly not working on this over this weekend.
>
> Anyway, this is what it is - and this is likely the state of it for
> a while yet, because I won't be able to sensibly access the hardware
> for testing for an undefined period of time.
>
> The PHY parts seem to work, although not 100% reliably, with the
> occasional overrun, particularly on the receive side. I'm not sure
> whether this is down to a hardware bug or not, or MDIO driver bug,
> because we certainly aren't missing timestamping a SKB. This has been
> tested at L2 and L4.
>
> I'm not sure which packets we should be timestamping (remembering
> that this is global config across all ports.)
> https://chronos.uk/wordpress/wp-content/uploads/TechnicalBrief-IEEE1588v2PTP.pdf
> suggests Sync, Delay_req and Delay_resp need to be timestamped,
> possibly PDelay_req and PDelay_resp as well, but I haven't seen
> those produced by PTPDv2 nor ptp4l.
>
> There's probably other stuff I should mention, but as I've been at
> this into the evening for almost every day this week, I'm mentally
> exhausted.
>
> Sorry also if this isn't coherent.
>
> drivers/net/dsa/mv88e6xxx/Kconfig | 1 +
> drivers/net/dsa/mv88e6xxx/chip.h | 26 +-
> drivers/net/dsa/mv88e6xxx/hwtstamp.c | 17 +-
> drivers/net/dsa/mv88e6xxx/hwtstamp.h | 1 +
> drivers/net/dsa/mv88e6xxx/ptp.c | 519 ++++++++-------------
> drivers/net/dsa/mv88e6xxx/ptp.h | 1 -
> drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c | 2 +
> drivers/net/phy/Kconfig | 13 +
> drivers/net/phy/Makefile | 1 +
> drivers/net/phy/marvell.c | 21 +-
> drivers/net/phy/marvell_ptp.c | 307 ++++++++++++
> drivers/net/phy/marvell_ptp.h | 21 +
> drivers/ptp/Kconfig | 4 +
> drivers/ptp/Makefile | 2 +
> drivers/ptp/ptp_marvell_tai.c | 449 ++++++++++++++++++
> drivers/ptp/ptp_marvell_ts.c | 593 ++++++++++++++++++++++++
> include/linux/marvell_ptp.h | 129 ++++++
> 17 files changed, 1764 insertions(+), 343 deletions(-)
>
> --
> RMK's Patch system: https://www.armlinux.org.uk/developer/patches/
> FTTP is here! 80Mbps down 10Mbps up. Decent connectivity at last!
>
--
RMK's Patch system: https://www.armlinux.org.uk/developer/patches/
FTTP is here! 80Mbps down 10Mbps up. Decent connectivity at last!
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH RFC net-next 3/5] net: phy: add Marvell PHY PTP support
2025-04-11 21:26 ` [PATCH RFC net-next 3/5] net: phy: add Marvell PHY PTP support Russell King
@ 2025-04-14 12:33 ` Kory Maincent
2025-04-14 12:37 ` Russell King (Oracle)
2025-04-14 13:57 ` Russell King (Oracle)
2025-04-14 14:43 ` Kory Maincent
1 sibling, 2 replies; 28+ messages in thread
From: Kory Maincent @ 2025-04-14 12:33 UTC (permalink / raw)
To: Russell King
Cc: Andrew Lunn, Heiner Kallweit, David S. Miller, Eric Dumazet,
Jakub Kicinski, netdev, Paolo Abeni, Richard Cochran
On Fri, 11 Apr 2025 22:26:42 +0100
Russell King <rmk+kernel@armlinux.org.uk> wrote:
> Add PTP basic support for Marvell 88E151x single port PHYs. These
> PHYs support timestamping the egress and ingress of packets, but does
> not support any packet modification, nor do we support any filtering
> beyond selecting packets that the hardware recognises as PTP/802.1AS.
>
> The PHYs support hardware pins for providing an external clock for the
> TAI counter, and a separate pin that can be used for event capture or
> generation of a trigger (either a pulse or periodic). Only event
> capture is supported.
>
> We currently use a delayed work to poll for the timestamps which is
> far from ideal, but we also provide a function that can be called from
> an interrupt handler - which would be good to tie into the main Marvell
> PHY driver.
>
> The driver takes inspiration from the Marvell 88E6xxx DSA and DP83640
> drivers. The hardware is very similar to the implementation found in
> the 88E6xxx DSA driver, but the access methods are very different,
> although it may be possible to create a library that both can use
> along with accessor functions.
I wanted to test it, but this patch does not build.
drivers/net/phy/marvell_ptp.c:269:33: error: passing argument 4 of ‘marvell_tai_probe’ from incompatible pointer type [-Werror=incompatible-pointer-types]
269 | "Marvell PHY", dev);
| ^~~~~~~~~~~~~
| |
| char *
In file included from drivers/net/phy/marvell_ptp.c:9:
./include/linux/marvell_ptp.h:81:44: note: expected ‘struct ptp_pin_desc *’ but argument is of type ‘char *’
81 | struct ptp_pin_desc *pin_config, int n_pins,
| ~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~
drivers/net/phy/marvell_ptp.c:269:48: warning: passing argument 5 of ‘marvell_tai_probe’ makes integer from pointer without a cast [-Wint-conversion]
269 | "Marvell PHY", dev);
| ^~~
| |
| struct device *
In file included from drivers/net/phy/marvell_ptp.c:9:
./include/linux/marvell_ptp.h:81:60: note: expected ‘int’ but argument is of type ‘struct device *’
81 | struct ptp_pin_desc *pin_config, int n_pins,
| ~~~~^~~~~~
drivers/net/phy/marvell_ptp.c:267:15: error: too few arguments to function ‘marvell_tai_probe’
267 | err = marvell_tai_probe(&tai, &marvell_phy_ptp_ops,
| ^~~~~~~~~~~~~~~~~
In file included from drivers/net/phy/marvell_ptp.c:9:
./include/linux/marvell_ptp.h:78:5: note: declared here
78 | int marvell_tai_probe(struct marvell_tai **taip,
| ^~~~~~~~~~~~~~~~~
Regards,
--
Köry Maincent, Bootlin
Embedded Linux and kernel engineering
https://bootlin.com
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH RFC net-next 3/5] net: phy: add Marvell PHY PTP support
2025-04-14 12:33 ` Kory Maincent
@ 2025-04-14 12:37 ` Russell King (Oracle)
2025-04-14 13:57 ` Russell King (Oracle)
1 sibling, 0 replies; 28+ messages in thread
From: Russell King (Oracle) @ 2025-04-14 12:37 UTC (permalink / raw)
To: Kory Maincent
Cc: Andrew Lunn, Heiner Kallweit, David S. Miller, Eric Dumazet,
Jakub Kicinski, netdev, Paolo Abeni, Richard Cochran
On Mon, Apr 14, 2025 at 02:33:06PM +0200, Kory Maincent wrote:
> On Fri, 11 Apr 2025 22:26:42 +0100
> Russell King <rmk+kernel@armlinux.org.uk> wrote:
>
> > Add PTP basic support for Marvell 88E151x single port PHYs. These
> > PHYs support timestamping the egress and ingress of packets, but does
> > not support any packet modification, nor do we support any filtering
> > beyond selecting packets that the hardware recognises as PTP/802.1AS.
> >
> > The PHYs support hardware pins for providing an external clock for the
> > TAI counter, and a separate pin that can be used for event capture or
> > generation of a trigger (either a pulse or periodic). Only event
> > capture is supported.
> >
> > We currently use a delayed work to poll for the timestamps which is
> > far from ideal, but we also provide a function that can be called from
> > an interrupt handler - which would be good to tie into the main Marvell
> > PHY driver.
> >
> > The driver takes inspiration from the Marvell 88E6xxx DSA and DP83640
> > drivers. The hardware is very similar to the implementation found in
> > the 88E6xxx DSA driver, but the access methods are very different,
> > although it may be possible to create a library that both can use
> > along with accessor functions.
>
> I wanted to test it, but this patch does not build.
>
> drivers/net/phy/marvell_ptp.c:269:33: error: passing argument 4 of ‘marvell_tai_probe’ from incompatible pointer type [-Werror=incompatible-pointer-types]
> 269 | "Marvell PHY", dev);
> | ^~~~~~~~~~~~~
> | |
> | char *
> In file included from drivers/net/phy/marvell_ptp.c:9:
> ./include/linux/marvell_ptp.h:81:44: note: expected ‘struct ptp_pin_desc *’ but argument is of type ‘char *’
> 81 | struct ptp_pin_desc *pin_config, int n_pins,
> | ~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~
> drivers/net/phy/marvell_ptp.c:269:48: warning: passing argument 5 of ‘marvell_tai_probe’ makes integer from pointer without a cast [-Wint-conversion]
> 269 | "Marvell PHY", dev);
> | ^~~
> | |
> | struct device *
> In file included from drivers/net/phy/marvell_ptp.c:9:
> ./include/linux/marvell_ptp.h:81:60: note: expected ‘int’ but argument is of type ‘struct device *’
> 81 | struct ptp_pin_desc *pin_config, int n_pins,
> | ~~~~^~~~~~
> drivers/net/phy/marvell_ptp.c:267:15: error: too few arguments to function ‘marvell_tai_probe’
> 267 | err = marvell_tai_probe(&tai, &marvell_phy_ptp_ops,
> | ^~~~~~~~~~~~~~~~~
> In file included from drivers/net/phy/marvell_ptp.c:9:
> ./include/linux/marvell_ptp.h:78:5: note: declared here
> 78 | int marvell_tai_probe(struct marvell_tai **taip,
> | ^~~~~~~~~~~~~~~~~
>
Add NULL, 0 before the name.
Sorry, but I have way too many patches to deal with.
--
RMK's Patch system: https://www.armlinux.org.uk/developer/patches/
FTTP is here! 80Mbps down 10Mbps up. Decent connectivity at last!
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH RFC net-next 1/5] net: mvpp2: add support for hardware timestamps
2025-04-11 21:26 ` [PATCH RFC net-next 1/5] net: mvpp2: add support for hardware timestamps Russell King
2025-04-11 21:29 ` Russell King (Oracle)
@ 2025-04-14 12:51 ` Kory Maincent
2025-04-14 14:43 ` Russell King (Oracle)
1 sibling, 1 reply; 28+ messages in thread
From: Kory Maincent @ 2025-04-14 12:51 UTC (permalink / raw)
To: Russell King
Cc: Andrew Lunn, Heiner Kallweit, Andrew Lunn, David S. Miller,
Eric Dumazet, Jakub Kicinski, Marcin Wojtas, netdev, Paolo Abeni
On Fri, 11 Apr 2025 22:26:31 +0100
Russell King <rmk+kernel@armlinux.org.uk> wrote:
> Add support for hardware timestamps in (e.g.) the PHY by calling
> skb_tx_timestamp() as close as reasonably possible to the point that
> the hardware is instructed to send the queued packets.
>
> Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
Reviewed-by: Kory Maincent <kory.maincent@bootlin.com>
> ---
> drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c | 2 ++
> 1 file changed, 2 insertions(+)
>
> diff --git a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c
> b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c index
> 416a926a8281..e3f8aa139d1e 100644 ---
> a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c +++
> b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c @@ -4439,6 +4439,8 @@
> static netdev_tx_t mvpp2_tx(struct sk_buff *skb, struct net_device *dev)
> txq_pcpu->count += frags; aggr_txq->count += frags;
>
> + skb_tx_timestamp(skb);
> +
> /* Enable transmit */
> wmb();
> mvpp2_aggr_txq_pend_desc_add(port, frags);
Small question for my curiosity here. Shouldn't we move the skb_tx_timestamp()
call after the memory barrier for a better precision or is it negligible?
Regards,
--
Köry Maincent, Bootlin
Embedded Linux and kernel engineering
https://bootlin.com
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH RFC net-next 3/5] net: phy: add Marvell PHY PTP support
2025-04-14 12:33 ` Kory Maincent
2025-04-14 12:37 ` Russell King (Oracle)
@ 2025-04-14 13:57 ` Russell King (Oracle)
1 sibling, 0 replies; 28+ messages in thread
From: Russell King (Oracle) @ 2025-04-14 13:57 UTC (permalink / raw)
To: Kory Maincent
Cc: Andrew Lunn, Heiner Kallweit, David S. Miller, Eric Dumazet,
Jakub Kicinski, netdev, Paolo Abeni, Richard Cochran
On Mon, Apr 14, 2025 at 02:33:06PM +0200, Kory Maincent wrote:
> On Fri, 11 Apr 2025 22:26:42 +0100
> Russell King <rmk+kernel@armlinux.org.uk> wrote:
>
> > Add PTP basic support for Marvell 88E151x single port PHYs. These
> > PHYs support timestamping the egress and ingress of packets, but does
> > not support any packet modification, nor do we support any filtering
> > beyond selecting packets that the hardware recognises as PTP/802.1AS.
> >
> > The PHYs support hardware pins for providing an external clock for the
> > TAI counter, and a separate pin that can be used for event capture or
> > generation of a trigger (either a pulse or periodic). Only event
> > capture is supported.
> >
> > We currently use a delayed work to poll for the timestamps which is
> > far from ideal, but we also provide a function that can be called from
> > an interrupt handler - which would be good to tie into the main Marvell
> > PHY driver.
> >
> > The driver takes inspiration from the Marvell 88E6xxx DSA and DP83640
> > drivers. The hardware is very similar to the implementation found in
> > the 88E6xxx DSA driver, but the access methods are very different,
> > although it may be possible to create a library that both can use
> > along with accessor functions.
>
> I wanted to test it, but this patch does not build.
>
> drivers/net/phy/marvell_ptp.c:269:33: error: passing argument 4 of ‘marvell_tai_probe’ from incompatible pointer type [-Werror=incompatible-pointer-types]
> 269 | "Marvell PHY", dev);
> | ^~~~~~~~~~~~~
> | |
> | char *
> In file included from drivers/net/phy/marvell_ptp.c:9:
> ./include/linux/marvell_ptp.h:81:44: note: expected ‘struct ptp_pin_desc *’ but argument is of type ‘char *’
> 81 | struct ptp_pin_desc *pin_config, int n_pins,
> | ~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~
> drivers/net/phy/marvell_ptp.c:269:48: warning: passing argument 5 of ‘marvell_tai_probe’ makes integer from pointer without a cast [-Wint-conversion]
> 269 | "Marvell PHY", dev);
> | ^~~
> | |
> | struct device *
> In file included from drivers/net/phy/marvell_ptp.c:9:
> ./include/linux/marvell_ptp.h:81:60: note: expected ‘int’ but argument is of type ‘struct device *’
> 81 | struct ptp_pin_desc *pin_config, int n_pins,
> | ~~~~^~~~~~
> drivers/net/phy/marvell_ptp.c:267:15: error: too few arguments to function ‘marvell_tai_probe’
> 267 | err = marvell_tai_probe(&tai, &marvell_phy_ptp_ops,
> | ^~~~~~~~~~~~~~~~~
> In file included from drivers/net/phy/marvell_ptp.c:9:
> ./include/linux/marvell_ptp.h:78:5: note: declared here
> 78 | int marvell_tai_probe(struct marvell_tai **taip,
> | ^~~~~~~~~~~~~~~~~
>
Looks like it's because this patch is missing - it's marked in my tree
as a HACK and I don't remember why as it's been soo many years since I
was working on this.
I also have no way to test whether this even works (all of my platforms
that have any kind of PTP support don't wire any of the "ext" pins for
synchronisation.)
Simplest solution is as I said in my previous reply - pass NULL, 0 in
the appropriate place to marvell_tai_probe() and then this patch won't
be necessary.
8<===
From: Russell King <rmk+kernel@armlinux.org.uk>
Subject: [PATCH] net: phy: marvell TAI: pin support *HACK*
Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
---
drivers/net/phy/marvell_ptp.c | 84 +++++++++++++++++++++++++++++++++++
1 file changed, 84 insertions(+)
diff --git a/drivers/net/phy/marvell_ptp.c b/drivers/net/phy/marvell_ptp.c
index 3ba71c44ffb0..ff24d4a8ad41 100644
--- a/drivers/net/phy/marvell_ptp.c
+++ b/drivers/net/phy/marvell_ptp.c
@@ -29,6 +29,7 @@
struct marvell_phy_ptp {
struct marvell_ptp ptp;
struct mii_timestamper mii_ts;
+ struct ptp_pin_desc pins[2];
};
static struct marvell_phy_ptp *mii_ts_to_phy_ptp(struct mii_timestamper *mii_ts)
@@ -101,6 +102,76 @@ static u64 marvell_phy_tai_clock_read(struct device *dev,
return lo | hi << 16;
}
+static int marvell_phy_tai_extts_read(struct device *dev, int reg,
+ struct marvell_extts *ts)
+{
+ struct phy_device *phydev = to_phy_device(dev);
+ int ret, oldpage;
+
+ oldpage = phy_select_page(phydev, MARVELL_PAGE_TAI_GLOBAL);
+ if (oldpage >= 0) {
+ ret = __phy_read(phydev, reg);
+ if (ret < 0)
+ goto restore;
+
+ ts->status = ret;
+ if (!(ts->status & MV_STATUS_EVENTCAPVALID)) {
+ ret = 0;
+ goto restore;
+ }
+
+ /* Read low timestamp */
+ ret = __phy_read(phydev, reg + 1);
+ if (ret < 0)
+ goto restore;
+
+ ts->time = ret;
+
+ /* Read high timestamp */
+ ret = __phy_read(phydev, reg + 2);
+ if (ret < 0)
+ goto restore;
+
+ ts->time |= ret << 16;
+
+ /* Clear valid */
+ __phy_write(phydev, reg, 0);
+
+ ret = 1;
+ }
+
+restore:
+ return phy_restore_page(phydev, oldpage, ret);
+}
+
+static int marvell_phy_tai_pin_verify(struct device *dev, int pin,
+ enum ptp_pin_function func,
+ unsigned int chan)
+{
+ if (pin == 1 && func == PTP_PF_EXTTS)
+ return 0;
+
+ return -EOPNOTSUPP;
+}
+
+static int marvell_phy_tai_pin_setup(struct device *dev, int pin,
+ unsigned int flags, int enable)
+{
+ struct phy_device *phydev = to_phy_device(dev);
+ struct marvell_phy_ptp *phy_ptp = mii_ts_to_phy_ptp(phydev->mii_ts);
+
+ if (phy_ptp->pins[pin].func != PTP_PF_EXTTS)
+ return -EOPNOTSUPP;
+
+ if (enable && (!(flags & PTP_RISING_EDGE) || flags & PTP_FALLING_EDGE))
+ return -EOPNOTSUPP;
+
+ /* Route LED[1] to event input */
+ return phy_modify_paged(phydev, MARVELL_PAGE_MISC, GCR,
+ GCR_PTP_INPUT_SOURCE,
+ enable ? GCR_PTP_INPUT_SOURCE : 0);
+}
+
static int marvell_phy_tai_write(struct device *dev, u8 reg, u16 val)
{
struct phy_device *phydev = to_phy_device(dev);
@@ -210,6 +281,9 @@ static long marvell_phy_ptp_aux_work(struct device *dev)
static const struct marvell_ptp_ops marvell_phy_ptp_ops = {
.tai_enable = marvell_phy_tai_enable,
.tai_clock_read = marvell_phy_tai_clock_read,
+ .tai_extts_read = marvell_phy_tai_extts_read,
+ .tai_pin_verify = marvell_phy_tai_pin_verify,
+ .tai_pin_setup = marvell_phy_tai_pin_setup,
.tai_write = marvell_phy_tai_write,
.tai_modify = marvell_phy_tai_modify,
.ptp_global_write = marvell_phy_ptp_global_write,
@@ -225,6 +299,8 @@ static const struct marvell_tai_param marvell_phy_tai_param = {
.cc_mult_den = 15625U,
.cc_mult = 8 << 28,
.cc_shift = 28,
+
+ .n_ext_ts = 1,
};
/* This function should be called from the PHY threaded interrupt
@@ -263,9 +339,17 @@ int marvell_phy_ptp_probe(struct phy_device *phydev)
phy_ptp->mii_ts.hwtstamp = marvell_phy_ptp_hwtstamp;
phy_ptp->mii_ts.ts_info = marvell_phy_ptp_ts_info;
+ strscpy(phy_ptp->pins[0].name, "CONFIG", sizeof(phy_ptp->pins[0].name));
+ phy_ptp->pins[0].index = 0;
+ phy_ptp->pins[0].func = PTP_PF_NONE;
+ strscpy(phy_ptp->pins[1].name, "LED[1]", sizeof(phy_ptp->pins[1].name));
+ phy_ptp->pins[1].index = 0;
+ phy_ptp->pins[1].func = PTP_PF_NONE;
+
/* Get the TAI for this PHY. */
err = marvell_tai_probe(&tai, &marvell_phy_ptp_ops,
&marvell_phy_tai_param,
+ phy_ptp->pins, ARRAY_SIZE(phy_ptp->pins),
"Marvell PHY", dev);
if (err)
return err;
--
2.30.2
--
RMK's Patch system: https://www.armlinux.org.uk/developer/patches/
FTTP is here! 80Mbps down 10Mbps up. Decent connectivity at last!
^ permalink raw reply related [flat|nested] 28+ messages in thread
* Re: [PATCH RFC net-next 3/5] net: phy: add Marvell PHY PTP support
2025-04-11 21:26 ` [PATCH RFC net-next 3/5] net: phy: add Marvell PHY PTP support Russell King
2025-04-14 12:33 ` Kory Maincent
@ 2025-04-14 14:43 ` Kory Maincent
2025-04-14 14:55 ` Russell King (Oracle)
1 sibling, 1 reply; 28+ messages in thread
From: Kory Maincent @ 2025-04-14 14:43 UTC (permalink / raw)
To: Russell King
Cc: Andrew Lunn, Heiner Kallweit, David S. Miller, Eric Dumazet,
Jakub Kicinski, netdev, Paolo Abeni, Richard Cochran
On Fri, 11 Apr 2025 22:26:42 +0100
Russell King <rmk+kernel@armlinux.org.uk> wrote:
> Add PTP basic support for Marvell 88E151x single port PHYs. These
> PHYs support timestamping the egress and ingress of packets, but does
> not support any packet modification, nor do we support any filtering
> beyond selecting packets that the hardware recognises as PTP/802.1AS.
>
> The PHYs support hardware pins for providing an external clock for the
> TAI counter, and a separate pin that can be used for event capture or
> generation of a trigger (either a pulse or periodic). Only event
> capture is supported.
>
> We currently use a delayed work to poll for the timestamps which is
> far from ideal, but we also provide a function that can be called from
> an interrupt handler - which would be good to tie into the main Marvell
> PHY driver.
>
> The driver takes inspiration from the Marvell 88E6xxx DSA and DP83640
> drivers. The hardware is very similar to the implementation found in
> the 88E6xxx DSA driver, but the access methods are very different,
> although it may be possible to create a library that both can use
> along with accessor functions.
It seems a lot less stable than the first version of the driver.
Lots of overrun:
ptp4l[949.894]: port 1 (eth0): assuming the grand master role
[ 954.899275] Marvell 88E1510 ff0d0000.ethernet-ffffffff:01: tx timestamp overrun (stat=0x5 seq=0)
[ 954.908670] Marvell 88E1510 ff0d0000.ethernet-ffffffff:01: rx timestamp overrun (q=1 stat=0x5 seq=0)
PTP L2 in master mode is not working at all maybe because there is no
configuration of PTP global config 1 register.
Faced also a case where it has really high offsets and I need to reboot the
board to see precise synchronization again:
ptp4l[4649.618]: port 1: LISTENING to UNCALIBRATED on RS_SLAVE
ptp4l[4652.519]: master offset 7650600217 s0 freq +32767998 path delay -33923681843
ptp4l[4653.487]: master offset 7617827584 s1 freq +6 path delay -33923681843
ptp4l[4654.454]: master offset -27201 s2 freq -27195 path delay -33923681843
ptp4l[4654.454]: port 1: UNCALIBRATED to SLAVE on MASTER_CLOCK_SELECTED
ptp4l[4655.422]: master offset 33926879673 s2 freq +32767999 path delay -67850561538
ptp4l[4656.389]: master offset 33649307442 s2 freq +32767999 path delay -67605734496
ptp4l[4657.356]: master offset 33454635535 s2 freq +32767999 path delay -67443834753
The PTP set as master mode seems really buggy.
I will try to debug this!
Regards,
--
Köry Maincent, Bootlin
Embedded Linux and kernel engineering
https://bootlin.com
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH RFC net-next 1/5] net: mvpp2: add support for hardware timestamps
2025-04-14 12:51 ` Kory Maincent
@ 2025-04-14 14:43 ` Russell King (Oracle)
2025-04-14 15:02 ` Kory Maincent
0 siblings, 1 reply; 28+ messages in thread
From: Russell King (Oracle) @ 2025-04-14 14:43 UTC (permalink / raw)
To: Kory Maincent
Cc: Andrew Lunn, Heiner Kallweit, Andrew Lunn, David S. Miller,
Eric Dumazet, Jakub Kicinski, Marcin Wojtas, netdev, Paolo Abeni
On Mon, Apr 14, 2025 at 02:51:50PM +0200, Kory Maincent wrote:
> On Fri, 11 Apr 2025 22:26:31 +0100
> Russell King <rmk+kernel@armlinux.org.uk> wrote:
>
> > Add support for hardware timestamps in (e.g.) the PHY by calling
> > skb_tx_timestamp() as close as reasonably possible to the point that
> > the hardware is instructed to send the queued packets.
> >
> > Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
>
> Reviewed-by: Kory Maincent <kory.maincent@bootlin.com>
>
> > ---
> > drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c | 2 ++
> > 1 file changed, 2 insertions(+)
> >
> > diff --git a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c
> > b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c index
> > 416a926a8281..e3f8aa139d1e 100644 ---
> > a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c +++
> > b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c @@ -4439,6 +4439,8 @@
> > static netdev_tx_t mvpp2_tx(struct sk_buff *skb, struct net_device *dev)
> > txq_pcpu->count += frags; aggr_txq->count += frags;
> >
> > + skb_tx_timestamp(skb);
> > +
> > /* Enable transmit */
> > wmb();
> > mvpp2_aggr_txq_pend_desc_add(port, frags);
>
> Small question for my curiosity here. Shouldn't we move the skb_tx_timestamp()
> call after the memory barrier for a better precision or is it negligible?
Depends what the wmb() is there for, which is entirely undocumented.
mvpp2_aggr_txq_pend_desc_add() uses writel(), which is itself required
to ensure that writes to memory before the writel() occurs are visible
to DMA agents. So, the wmb() there shouldn't be necessary if all
that's going on here is to ensure that the packet is visible to the
buffer manager hardware.
On arm64, that's __io_wmb(), which becomes __dma_wmb() and ultimately
"dmb oshst". wmb() on the other hand is a heavier barrier, "dsb st".
This driver ends up doing both, inexplicably.
It would help if people would document what purpose a barrier exists
for.
Without knowing what the wmb() is there for, I wouldn't like to move
it the other side.
--
RMK's Patch system: https://www.armlinux.org.uk/developer/patches/
FTTP is here! 80Mbps down 10Mbps up. Decent connectivity at last!
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH RFC net-next 3/5] net: phy: add Marvell PHY PTP support
2025-04-14 14:43 ` Kory Maincent
@ 2025-04-14 14:55 ` Russell King (Oracle)
2025-04-14 15:21 ` Kory Maincent
0 siblings, 1 reply; 28+ messages in thread
From: Russell King (Oracle) @ 2025-04-14 14:55 UTC (permalink / raw)
To: Kory Maincent
Cc: Andrew Lunn, Heiner Kallweit, David S. Miller, Eric Dumazet,
Jakub Kicinski, netdev, Paolo Abeni, Richard Cochran
On Mon, Apr 14, 2025 at 04:43:14PM +0200, Kory Maincent wrote:
> On Fri, 11 Apr 2025 22:26:42 +0100
> Russell King <rmk+kernel@armlinux.org.uk> wrote:
>
> > Add PTP basic support for Marvell 88E151x single port PHYs. These
> > PHYs support timestamping the egress and ingress of packets, but does
> > not support any packet modification, nor do we support any filtering
> > beyond selecting packets that the hardware recognises as PTP/802.1AS.
> >
> > The PHYs support hardware pins for providing an external clock for the
> > TAI counter, and a separate pin that can be used for event capture or
> > generation of a trigger (either a pulse or periodic). Only event
> > capture is supported.
> >
> > We currently use a delayed work to poll for the timestamps which is
> > far from ideal, but we also provide a function that can be called from
> > an interrupt handler - which would be good to tie into the main Marvell
> > PHY driver.
> >
> > The driver takes inspiration from the Marvell 88E6xxx DSA and DP83640
> > drivers. The hardware is very similar to the implementation found in
> > the 88E6xxx DSA driver, but the access methods are very different,
> > although it may be possible to create a library that both can use
> > along with accessor functions.
>
> It seems a lot less stable than the first version of the driver.
>
> Lots of overrun:
> ptp4l[949.894]: port 1 (eth0): assuming the grand master role
> [ 954.899275] Marvell 88E1510 ff0d0000.ethernet-ffffffff:01: tx timestamp overrun (stat=0x5 seq=0)
> [ 954.908670] Marvell 88E1510 ff0d0000.ethernet-ffffffff:01: rx timestamp overrun (q=1 stat=0x5 seq=0)
I've explained why this happens - it will occur if timestamping has
been enabled but there's been no packets to be stamped for a while
but there _have_ been packets on the network that the hardware
decided to stamp. There is no way around this because the driver isn't
told when to shutdown timestamping.
> PTP L2 in master mode is not working at all maybe because there is no
> configuration of PTP global config 1 register.
No, sorry, wrong. There is, and you haven't read what I've written
previously if you're still thinking this.
Look in "ptp: marvell: add core support for Marvell PTP v2.1".
Look at marvell_tai_global_config(). (I've explained why it's there.)
Look at:
+ /* MsdIDTSEn - Enable timestamping on all PTP MessageIDs */
+ err = tai->ops->ptp_global_write(tai->dev, PTPG_CONFIG_1,
+ MV_PTP_MSD_ID_TS_EN);
> Faced also a case where it has really high offsets and I need to reboot the
> board to see precise synchronization again:
> ptp4l[4649.618]: port 1: LISTENING to UNCALIBRATED on RS_SLAVE
> ptp4l[4652.519]: master offset 7650600217 s0 freq +32767998 path delay -33923681843
> ptp4l[4653.487]: master offset 7617827584 s1 freq +6 path delay -33923681843
> ptp4l[4654.454]: master offset -27201 s2 freq -27195 path delay -33923681843
> ptp4l[4654.454]: port 1: UNCALIBRATED to SLAVE on MASTER_CLOCK_SELECTED
> ptp4l[4655.422]: master offset 33926879673 s2 freq +32767999 path delay -67850561538
> ptp4l[4656.389]: master offset 33649307442 s2 freq +32767999 path delay -67605734496
> ptp4l[4657.356]: master offset 33454635535 s2 freq +32767999 path delay -67443834753
I don't see that, I get way smaller offsets with this when using the
ZII rev B platform as the master.
> The PTP set as master mode seems really buggy.
That's something I didn't test, but as the hardware is symmetrical (there
aren't separate settings for PTP MSGIDs that get stamped on the transmit
and receive paths) I'm not sure what's going on. If one enables
timestamping for MSG ID 0 (Sync) then the hardware will stamp packets
received and transmitted that have a PTP MSG ID 0.
In any case, I can't do any testing now, sorry.
--
RMK's Patch system: https://www.armlinux.org.uk/developer/patches/
FTTP is here! 80Mbps down 10Mbps up. Decent connectivity at last!
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH RFC net-next 1/5] net: mvpp2: add support for hardware timestamps
2025-04-14 14:43 ` Russell King (Oracle)
@ 2025-04-14 15:02 ` Kory Maincent
0 siblings, 0 replies; 28+ messages in thread
From: Kory Maincent @ 2025-04-14 15:02 UTC (permalink / raw)
To: Russell King (Oracle)
Cc: Andrew Lunn, Heiner Kallweit, Andrew Lunn, David S. Miller,
Eric Dumazet, Jakub Kicinski, Marcin Wojtas, netdev, Paolo Abeni
On Mon, 14 Apr 2025 15:43:22 +0100
"Russell King (Oracle)" <linux@armlinux.org.uk> wrote:
> On Mon, Apr 14, 2025 at 02:51:50PM +0200, Kory Maincent wrote:
> > On Fri, 11 Apr 2025 22:26:31 +0100
> > Russell King <rmk+kernel@armlinux.org.uk> wrote:
> >
> > > Add support for hardware timestamps in (e.g.) the PHY by calling
> > > skb_tx_timestamp() as close as reasonably possible to the point that
> > > the hardware is instructed to send the queued packets.
> > >
> > > Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
> >
> > Reviewed-by: Kory Maincent <kory.maincent@bootlin.com>
> >
> > > ---
> > > drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c | 2 ++
> > > 1 file changed, 2 insertions(+)
> > >
> > > diff --git a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c
> > > b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c index
> > > 416a926a8281..e3f8aa139d1e 100644 ---
> > > a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c +++
> > > b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c @@ -4439,6 +4439,8 @@
> > > static netdev_tx_t mvpp2_tx(struct sk_buff *skb, struct net_device *dev)
> > > txq_pcpu->count += frags; aggr_txq->count += frags;
> > >
> > > + skb_tx_timestamp(skb);
> > > +
> > > /* Enable transmit */
> > > wmb();
> > > mvpp2_aggr_txq_pend_desc_add(port, frags);
> >
> > Small question for my curiosity here. Shouldn't we move the
> > skb_tx_timestamp() call after the memory barrier for a better precision or
> > is it negligible?
>
> Depends what the wmb() is there for, which is entirely undocumented.
>
> mvpp2_aggr_txq_pend_desc_add() uses writel(), which is itself required
> to ensure that writes to memory before the writel() occurs are visible
> to DMA agents. So, the wmb() there shouldn't be necessary if all
> that's going on here is to ensure that the packet is visible to the
> buffer manager hardware.
>
> On arm64, that's __io_wmb(), which becomes __dma_wmb() and ultimately
> "dmb oshst". wmb() on the other hand is a heavier barrier, "dsb st".
> This driver ends up doing both, inexplicably.
Indeed and that is the same case for all the wmb() call in that file.
Not sure two consecutive memory barrier are useful here, indeed a bit of doc
would have been great!
Thanks!
Regards,
--
Köry Maincent, Bootlin
Embedded Linux and kernel engineering
https://bootlin.com
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH RFC net-next 3/5] net: phy: add Marvell PHY PTP support
2025-04-14 14:55 ` Russell King (Oracle)
@ 2025-04-14 15:21 ` Kory Maincent
2025-04-14 16:16 ` Kory Maincent
0 siblings, 1 reply; 28+ messages in thread
From: Kory Maincent @ 2025-04-14 15:21 UTC (permalink / raw)
To: Russell King (Oracle)
Cc: Andrew Lunn, Heiner Kallweit, David S. Miller, Eric Dumazet,
Jakub Kicinski, netdev, Paolo Abeni, Richard Cochran
On Mon, 14 Apr 2025 15:55:09 +0100
"Russell King (Oracle)" <linux@armlinux.org.uk> wrote:
> On Mon, Apr 14, 2025 at 04:43:14PM +0200, Kory Maincent wrote:
> > On Fri, 11 Apr 2025 22:26:42 +0100
> > Russell King <rmk+kernel@armlinux.org.uk> wrote:
> >
> > > Add PTP basic support for Marvell 88E151x single port PHYs. These
> > > PHYs support timestamping the egress and ingress of packets, but does
> > > not support any packet modification, nor do we support any filtering
> > > beyond selecting packets that the hardware recognises as PTP/802.1AS.
> > >
> > > The PHYs support hardware pins for providing an external clock for the
> > > TAI counter, and a separate pin that can be used for event capture or
> > > generation of a trigger (either a pulse or periodic). Only event
> > > capture is supported.
> > >
> > > We currently use a delayed work to poll for the timestamps which is
> > > far from ideal, but we also provide a function that can be called from
> > > an interrupt handler - which would be good to tie into the main Marvell
> > > PHY driver.
> > >
> > > The driver takes inspiration from the Marvell 88E6xxx DSA and DP83640
> > > drivers. The hardware is very similar to the implementation found in
> > > the 88E6xxx DSA driver, but the access methods are very different,
> > > although it may be possible to create a library that both can use
> > > along with accessor functions.
> >
> > It seems a lot less stable than the first version of the driver.
> >
> > Lots of overrun:
> > ptp4l[949.894]: port 1 (eth0): assuming the grand master role
> > [ 954.899275] Marvell 88E1510 ff0d0000.ethernet-ffffffff:01: tx timestamp
> > overrun (stat=0x5 seq=0) [ 954.908670] Marvell 88E1510
> > ff0d0000.ethernet-ffffffff:01: rx timestamp overrun (q=1 stat=0x5 seq=0)
>
> I've explained why this happens - it will occur if timestamping has
> been enabled but there's been no packets to be stamped for a while
> but there _have_ been packets on the network that the hardware
> decided to stamp. There is no way around this because the driver isn't
> told when to shutdown timestamping.
Yes but I didn't faced this on your first version. Maybe there is a way to
avoid this.
I have not yet dive into your code to check the changes but I will.
> > PTP L2 in master mode is not working at all maybe because there is no
> > configuration of PTP global config 1 register.
>
> No, sorry, wrong. There is, and you haven't read what I've written
> previously if you're still thinking this.
>
> Look in "ptp: marvell: add core support for Marvell PTP v2.1".
>
> Look at marvell_tai_global_config(). (I've explained why it's there.)
>
> Look at:
>
> + /* MsdIDTSEn - Enable timestamping on all PTP MessageIDs */
> + err = tai->ops->ptp_global_write(tai->dev, PTPG_CONFIG_1,
> + MV_PTP_MSD_ID_TS_EN);
Oop sorry, missed that custom ops.
> > Faced also a case where it has really high offsets and I need to reboot the
> > board to see precise synchronization again:
> > ptp4l[4649.618]: port 1: LISTENING to UNCALIBRATED on RS_SLAVE
> > ptp4l[4652.519]: master offset 7650600217 s0 freq +32767998 path delay
> > -33923681843 ptp4l[4653.487]: master offset 7617827584 s1 freq +6 path
> > delay -33923681843 ptp4l[4654.454]: master offset -27201 s2 freq
> > -27195 path delay -33923681843 ptp4l[4654.454]: port 1: UNCALIBRATED to
> > SLAVE on MASTER_CLOCK_SELECTED ptp4l[4655.422]: master offset 33926879673
> > s2 freq +32767999 path delay -67850561538 ptp4l[4656.389]: master offset
> > 33649307442 s2 freq +32767999 path delay -67605734496 ptp4l[4657.356]:
> > master offset 33454635535 s2 freq +32767999 path delay -67443834753
>
> I don't see that, I get way smaller offsets with this when using the
> ZII rev B platform as the master.
>
> > The PTP set as master mode seems really buggy.
>
> That's something I didn't test, but as the hardware is symmetrical (there
> aren't separate settings for PTP MSGIDs that get stamped on the transmit
> and receive paths) I'm not sure what's going on. If one enables
> timestamping for MSG ID 0 (Sync) then the hardware will stamp packets
> received and transmitted that have a PTP MSG ID 0.
>
> In any case, I can't do any testing now, sorry.
Yes, I will investigate.
Regards,
--
Köry Maincent, Bootlin
Embedded Linux and kernel engineering
https://bootlin.com
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH RFC net-next 3/5] net: phy: add Marvell PHY PTP support
2025-04-14 15:21 ` Kory Maincent
@ 2025-04-14 16:16 ` Kory Maincent
2025-04-14 16:35 ` Russell King (Oracle)
0 siblings, 1 reply; 28+ messages in thread
From: Kory Maincent @ 2025-04-14 16:16 UTC (permalink / raw)
To: Russell King (Oracle)
Cc: Andrew Lunn, Heiner Kallweit, David S. Miller, Eric Dumazet,
Jakub Kicinski, netdev, Paolo Abeni, Richard Cochran
On Mon, 14 Apr 2025 17:21:37 +0200
Kory Maincent <kory.maincent@bootlin.com> wrote:
> On Mon, 14 Apr 2025 15:55:09 +0100
> "Russell King (Oracle)" <linux@armlinux.org.uk> wrote:
>
> > On Mon, Apr 14, 2025 at 04:43:14PM +0200, Kory Maincent wrote:
> > > On Fri, 11 Apr 2025 22:26:42 +0100
> > > Russell King <rmk+kernel@armlinux.org.uk> wrote:
> > >
> [...]
> > >
> > > It seems a lot less stable than the first version of the driver.
FYI there is also an invalid wait context error:
[ 65.041285] =============================
[ 65.045286] [ BUG: Invalid wait context ]
[ 65.049289] 6.14.0-13314-g04846c13cbec-dirty #91 Not tainted
[ 65.054938] -----------------------------
[ 65.058939] swapper/0/0 is trying to lock:
[ 65.063028] ffff000805b0c110 (&rxq->rx_mutex){....}-{4:4}, at:
marvell_ptp_rxtstamp+0xf0/0x228
[ 65.071665] other info that might help us debug this:
[ 65.076707] context-{3:3}
[ 65.079320] no locks held by swapper/0/0.
[ 65.083321] stack backtrace:
[ 65.086198] CPU: 0 UID: 0 PID: 0 Comm: swapper/0 Not tainted
6.14.0-13314-g04846c13cbec-dirty #91 PREEMPT(voluntary)
[ 65.086207] Hardware name: Fluyt Prototype 1 Carrier Board ZU9 mpsoc (DT)
[ 65.086211] Call trace:
[ 65.086214] show_stack+0x18/0x24 (C)
[ 65.086224] dump_stack_lvl+0xa4/0xf4
[ 65.086233] dump_stack+0x18/0x24
[ 65.086240] __lock_acquire+0xa14/0x2000
[ 65.086250] lock_acquire+0x1c4/0x360
[ 65.086259] __mutex_lock+0xa0/0x52c
[ 65.086266] mutex_lock_nested+0x24/0x30
[ 65.086273] marvell_ptp_rxtstamp+0xf0/0x228
[ 65.086282] marvell_phy_ptp_rxtstamp+0x18/0x24
[ 65.086290] skb_defer_rx_timestamp+0x104/0x31c
Regards,
--
Köry Maincent, Bootlin
Embedded Linux and kernel engineering
https://bootlin.com
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH RFC net-next 3/5] net: phy: add Marvell PHY PTP support
2025-04-14 16:16 ` Kory Maincent
@ 2025-04-14 16:35 ` Russell King (Oracle)
0 siblings, 0 replies; 28+ messages in thread
From: Russell King (Oracle) @ 2025-04-14 16:35 UTC (permalink / raw)
To: Kory Maincent
Cc: Andrew Lunn, Heiner Kallweit, David S. Miller, Eric Dumazet,
Jakub Kicinski, netdev, Paolo Abeni, Richard Cochran
On Mon, Apr 14, 2025 at 06:16:43PM +0200, Kory Maincent wrote:
> On Mon, 14 Apr 2025 17:21:37 +0200
> Kory Maincent <kory.maincent@bootlin.com> wrote:
>
> > On Mon, 14 Apr 2025 15:55:09 +0100
> > "Russell King (Oracle)" <linux@armlinux.org.uk> wrote:
> >
> > > On Mon, Apr 14, 2025 at 04:43:14PM +0200, Kory Maincent wrote:
> > > > On Fri, 11 Apr 2025 22:26:42 +0100
> > > > Russell King <rmk+kernel@armlinux.org.uk> wrote:
> > > >
> > [...]
> > > >
> > > > It seems a lot less stable than the first version of the driver.
>
> FYI there is also an invalid wait context error:
> [ 65.041285] =============================
> [ 65.045286] [ BUG: Invalid wait context ]
> [ 65.049289] 6.14.0-13314-g04846c13cbec-dirty #91 Not tainted
> [ 65.054938] -----------------------------
> [ 65.058939] swapper/0/0 is trying to lock:
> [ 65.063028] ffff000805b0c110 (&rxq->rx_mutex){....}-{4:4}, at:
> marvell_ptp_rxtstamp+0xf0/0x228
> [ 65.071665] other info that might help us debug this:
> [ 65.076707] context-{3:3}
> [ 65.079320] no locks held by swapper/0/0.
> [ 65.083321] stack backtrace:
> [ 65.086198] CPU: 0 UID: 0 PID: 0 Comm: swapper/0 Not tainted
> 6.14.0-13314-g04846c13cbec-dirty #91 PREEMPT(voluntary)
> [ 65.086207] Hardware name: Fluyt Prototype 1 Carrier Board ZU9 mpsoc (DT)
> [ 65.086211] Call trace:
> [ 65.086214] show_stack+0x18/0x24 (C)
> [ 65.086224] dump_stack_lvl+0xa4/0xf4
> [ 65.086233] dump_stack+0x18/0x24
> [ 65.086240] __lock_acquire+0xa14/0x2000
> [ 65.086250] lock_acquire+0x1c4/0x360
> [ 65.086259] __mutex_lock+0xa0/0x52c
> [ 65.086266] mutex_lock_nested+0x24/0x30
> [ 65.086273] marvell_ptp_rxtstamp+0xf0/0x228
> [ 65.086282] marvell_phy_ptp_rxtstamp+0x18/0x24
> [ 65.086290] skb_defer_rx_timestamp+0x104/0x31c
Hmm, I guess that mutex needs to be a spinlock then.
--
RMK's Patch system: https://www.armlinux.org.uk/developer/patches/
FTTP is here! 80Mbps down 10Mbps up. Decent connectivity at last!
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH RFC net-next 2/5] ptp: marvell: add core support for Marvell PTP v2.1
2025-04-11 21:26 ` [PATCH RFC net-next 2/5] ptp: marvell: add core support for Marvell PTP v2.1 Russell King
2025-04-11 21:30 ` Russell King (Oracle)
@ 2025-04-16 8:48 ` Kory Maincent
2025-04-16 9:22 ` Russell King (Oracle)
1 sibling, 1 reply; 28+ messages in thread
From: Kory Maincent @ 2025-04-16 8:48 UTC (permalink / raw)
To: Russell King
Cc: Andrew Lunn, Heiner Kallweit, Andrew Lunn, David S. Miller,
Eric Dumazet, Jakub Kicinski, netdev, Paolo Abeni,
Richard Cochran
On Fri, 11 Apr 2025 22:26:37 +0100
Russell King <rmk+kernel@armlinux.org.uk> wrote:
> Provide core support for the Marvell PTP v2.1 implementations, which
> consist of a TAI (time application interface) and timestamping blocks.
> This hardware can be found in Marvell 88E151x PHYs, Armada 38x and
> Armada 37xx (mvneta), as well as Marvell DSA devices.
>
> Support for both arrival timestamps is supported, we use arrival 1 for
> PTP peer delay messages, and arrival 0 for all other messages.
>
> External event capture is also supported.
>
> PPS output and trigger generation is not supported.
>
> This core takes inspiration from the existing Marvell 88E6xxx DSA PTP
> code and DP83640 drivers. Like the original 88E6xxx DSA code, we
> use a delayed work to keep the cycle counter updated, and a separate
> delayed work for event capture.
>
> We expose the ptp clock aux work to allow users to support single and
> multi-port designs - where there is one Marvell TAI instance and a
> number of Marvell TS instances.
...
> +#define MV_PTP_MSGTYPE_DELAY_RESP 9
> +
> +/* This defines which incoming or outgoing PTP frames are timestampped */
> +#define MV_PTP_MSD_ID_TS_EN (BIT(PTP_MSGTYPE_SYNC) | \
> + BIT(PTP_MSGTYPE_DELAY_REQ) | \
> + BIT(MV_PTP_MSGTYPE_DELAY_RESP))
> +/* Direct Sync messages to Arr0 and delay messages to Arr1 */
> +#define MV_PTP_TS_ARR_PTR (BIT(PTP_MSGTYPE_DELAY_REQ) | \
> + BIT(MV_PTP_MSGTYPE_DELAY_RESP))
Why did you have chosen to use two queues with two separate behavior?
I have tried using only one queue and the PTP as master behaves correctly
without all these overrun. It is way better with one queue.
Maybe it was not the best approach if you want to use the two queues.
Regards,
--
Köry Maincent, Bootlin
Embedded Linux and kernel engineering
https://bootlin.com
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH RFC net-next 2/5] ptp: marvell: add core support for Marvell PTP v2.1
2025-04-16 8:48 ` Kory Maincent
@ 2025-04-16 9:22 ` Russell King (Oracle)
2025-04-16 13:14 ` Kory Maincent
2025-06-13 15:19 ` Kory Maincent
0 siblings, 2 replies; 28+ messages in thread
From: Russell King (Oracle) @ 2025-04-16 9:22 UTC (permalink / raw)
To: Kory Maincent
Cc: Andrew Lunn, Heiner Kallweit, Andrew Lunn, David S. Miller,
Eric Dumazet, Jakub Kicinski, netdev, Paolo Abeni,
Richard Cochran
On Wed, Apr 16, 2025 at 10:48:49AM +0200, Kory Maincent wrote:
> On Fri, 11 Apr 2025 22:26:37 +0100
> Russell King <rmk+kernel@armlinux.org.uk> wrote:
> > Provide core support for the Marvell PTP v2.1 implementations, which
> > consist of a TAI (time application interface) and timestamping blocks.
> > This hardware can be found in Marvell 88E151x PHYs, Armada 38x and
> > Armada 37xx (mvneta), as well as Marvell DSA devices.
> >
> > Support for both arrival timestamps is supported, we use arrival 1 for
> > PTP peer delay messages, and arrival 0 for all other messages.
> >
> > External event capture is also supported.
> >
> > PPS output and trigger generation is not supported.
> >
> > This core takes inspiration from the existing Marvell 88E6xxx DSA PTP
> > code and DP83640 drivers. Like the original 88E6xxx DSA code, we
> > use a delayed work to keep the cycle counter updated, and a separate
> > delayed work for event capture.
> >
> > We expose the ptp clock aux work to allow users to support single and
> > multi-port designs - where there is one Marvell TAI instance and a
> > number of Marvell TS instances.
>
> ...
>
> > +#define MV_PTP_MSGTYPE_DELAY_RESP 9
> > +
> > +/* This defines which incoming or outgoing PTP frames are timestampped */
> > +#define MV_PTP_MSD_ID_TS_EN (BIT(PTP_MSGTYPE_SYNC) | \
> > + BIT(PTP_MSGTYPE_DELAY_REQ) | \
> > + BIT(MV_PTP_MSGTYPE_DELAY_RESP))
> > +/* Direct Sync messages to Arr0 and delay messages to Arr1 */
> > +#define MV_PTP_TS_ARR_PTR (BIT(PTP_MSGTYPE_DELAY_REQ) | \
> > + BIT(MV_PTP_MSGTYPE_DELAY_RESP))
>
> Why did you have chosen to use two queues with two separate behavior?
> I have tried using only one queue and the PTP as master behaves correctly
> without all these overrun. It is way better with one queue.
> Maybe it was not the best approach if you want to use the two queues.
First, both queues have the same behaviour.
Second, because they *aren't* queues as they can only stamp one message.
The sync messages come from the master on a regular basis. The delay
response messages come from the master in response to a delay request
message, the timing of which is determined by the local slave.
If the local end sends a delay request just at the point that the master
sends a sync message causing the master to immediately follow the sync
message with the delay response message, then we could get an overrun
on a single queue - because we'll stamp the sync message and if we don't
read the timestamp quickly enough, the stamp registers will be busy
preventing the timestamp of the delay response being captured.
With the overruns that I've seen, they've always been on the second
"queue" and have always been for a sequence number several in the past
beyond the point that the overrun has been reported. However, the
packet which the sequence number matches had already been received -
and several others have also been received. I've been wondering if it's
a hardware bug, or maybe it's something other bits of the kernel is
doing wrong.
--
RMK's Patch system: https://www.armlinux.org.uk/developer/patches/
FTTP is here! 80Mbps down 10Mbps up. Decent connectivity at last!
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH RFC net-next 0/5] Marvell PTP support
2025-04-11 21:26 [PATCH RFC net-next 0/5] Marvell PTP support Russell King (Oracle)
` (5 preceding siblings ...)
2025-04-11 21:30 ` [PATCH RFC net-next 0/5] Marvell PTP support Russell King (Oracle)
@ 2025-04-16 10:51 ` Russell King (Oracle)
2025-04-16 13:19 ` Kory Maincent
6 siblings, 1 reply; 28+ messages in thread
From: Russell King (Oracle) @ 2025-04-16 10:51 UTC (permalink / raw)
To: Andrew Lunn, Heiner Kallweit
Cc: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Kory Maincent, Marcin Wojtas, netdev, Paolo Abeni,
Richard Cochran, Vladimir Oltean
On Fri, Apr 11, 2025 at 10:26:15PM +0100, Russell King (Oracle) wrote:
> Hi,
>
> This series is a work in progress, and represents the current state of
> things, superseding Kory's patches which were based in a very old
> version of my patches - and my patches were subsequently refactored
> and further developed about five years ago. Due to them breaking
> mvpp2 if merged, there was no point in posting them until such time
> that the underlying issues with PTP were resolved - and they now have
> been.
>
> Marvell re-uses their PTP IP in several of their products - PHYs,
> switches and even some ethernet MACs contain the same IP. It really
> doesn't make sense to duplicate the code in each of these use cases.
>
> Therefore, this series introduces a Marvell PTP core that can be
> re-used - a TAI module, which handles the global parts of the PTP
> core, and the TS module, which handles the per-port timestamping.
>
> I will note at this point that although the Armada 388 TRM states that
> NETA contains the same IP, attempts to access the registers returns
> zero, and it is not known if that is due to the board missing something
> or whether it isn't actually implemented. I do have some early work
> re-using this, but when I discovered that the TAI registers read as
> zero and wouldn't accept writes, I haven't progressed that.
>
> Today, I have converted the mv88e6xxx DSA code to use the Marvell TAI
> module from patch 1, and for the sake of getting the code out there,
> I have included the "hacky" patches in this series - with the issues
> with DSA VLANs that I reported this evening and subsequently
> investigated, I've not had any spare time to properly prepare that
> part of this series. (Being usurped from phylink by stmmac - for which
> I have a big stack of patches that I can't get out because of being
> usurped, and then again by Marvell PTP, and then again by DSA VLAN
> stuff... yea, I'm feeling like I have zero time to do anything right
> now.) The mv88e6xxx DSA code still needs to be converted to use the
> Marvell TS part of patch 1, but I won't be able to test that after
> Sunday, and I'm certainly not working on this over this weekend.
>
> Anyway, this is what it is - and this is likely the state of it for
> a while yet, because I won't be able to sensibly access the hardware
> for testing for an undefined period of time.
>
> The PHY parts seem to work, although not 100% reliably, with the
> occasional overrun, particularly on the receive side. I'm not sure
> whether this is down to a hardware bug or not, or MDIO driver bug,
> because we certainly aren't missing timestamping a SKB. This has been
> tested at L2 and L4.
>
> I'm not sure which packets we should be timestamping (remembering
> that this is global config across all ports.)
> https://chronos.uk/wordpress/wp-content/uploads/TechnicalBrief-IEEE1588v2PTP.pdf
> suggests Sync, Delay_req and Delay_resp need to be timestamped,
> possibly PDelay_req and PDelay_resp as well, but I haven't seen
> those produced by PTPDv2 nor ptp4l.
>
> There's probably other stuff I should mention, but as I've been at
> this into the evening for almost every day this week, I'm mentally
> exhausted.
>
> Sorry also if this isn't coherent.
I've just updated this series for the supported pins flags that was
merged into net-next last night.
Kory, if you have any changes you want me to review before sending
out the updates, please send soon. Thanks.
--
RMK's Patch system: https://www.armlinux.org.uk/developer/patches/
FTTP is here! 80Mbps down 10Mbps up. Decent connectivity at last!
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH RFC net-next 2/5] ptp: marvell: add core support for Marvell PTP v2.1
2025-04-16 9:22 ` Russell King (Oracle)
@ 2025-04-16 13:14 ` Kory Maincent
2025-06-13 15:19 ` Kory Maincent
1 sibling, 0 replies; 28+ messages in thread
From: Kory Maincent @ 2025-04-16 13:14 UTC (permalink / raw)
To: Russell King (Oracle)
Cc: Andrew Lunn, Heiner Kallweit, Andrew Lunn, David S. Miller,
Eric Dumazet, Jakub Kicinski, netdev, Paolo Abeni,
Richard Cochran
On Wed, 16 Apr 2025 10:22:47 +0100
"Russell King (Oracle)" <linux@armlinux.org.uk> wrote:
> On Wed, Apr 16, 2025 at 10:48:49AM +0200, Kory Maincent wrote:
> > On Fri, 11 Apr 2025 22:26:37 +0100
> > Russell King <rmk+kernel@armlinux.org.uk> wrote:
> > > Provide core support for the Marvell PTP v2.1 implementations, which
> > > consist of a TAI (time application interface) and timestamping blocks.
> > > This hardware can be found in Marvell 88E151x PHYs, Armada 38x and
> > > Armada 37xx (mvneta), as well as Marvell DSA devices.
> > >
> > > Support for both arrival timestamps is supported, we use arrival 1 for
> > > PTP peer delay messages, and arrival 0 for all other messages.
> > >
> > > External event capture is also supported.
> > >
> > > PPS output and trigger generation is not supported.
> > >
> > > This core takes inspiration from the existing Marvell 88E6xxx DSA PTP
> > > code and DP83640 drivers. Like the original 88E6xxx DSA code, we
> > > use a delayed work to keep the cycle counter updated, and a separate
> > > delayed work for event capture.
> > >
> > > We expose the ptp clock aux work to allow users to support single and
> > > multi-port designs - where there is one Marvell TAI instance and a
> > > number of Marvell TS instances.
> >
> > ...
> >
> > > +#define MV_PTP_MSGTYPE_DELAY_RESP 9
> > > +
> > > +/* This defines which incoming or outgoing PTP frames are timestampped */
> > > +#define MV_PTP_MSD_ID_TS_EN (BIT(PTP_MSGTYPE_SYNC) | \
> > > + BIT(PTP_MSGTYPE_DELAY_REQ) | \
> > > + BIT(MV_PTP_MSGTYPE_DELAY_RESP))
> > > +/* Direct Sync messages to Arr0 and delay messages to Arr1 */
> > > +#define MV_PTP_TS_ARR_PTR (BIT(PTP_MSGTYPE_DELAY_REQ) | \
> > > + BIT(MV_PTP_MSGTYPE_DELAY_RESP))
> >
> > Why did you have chosen to use two queues with two separate behavior?
> > I have tried using only one queue and the PTP as master behaves correctly
> > without all these overrun. It is way better with one queue.
> > Maybe it was not the best approach if you want to use the two queues.
>
> First, both queues have the same behaviour.
Yes, I was not clear I was referring to the message type.
> Second, because they *aren't* queues as they can only stamp one message.
> The sync messages come from the master on a regular basis. The delay
> response messages come from the master in response to a delay request
> message, the timing of which is determined by the local slave.
>
> If the local end sends a delay request just at the point that the master
> sends a sync message causing the master to immediately follow the sync
> message with the delay response message, then we could get an overrun
> on a single queue - because we'll stamp the sync message and if we don't
> read the timestamp quickly enough, the stamp registers will be busy
> preventing the timestamp of the delay response being captured.
Have you already seen such cases?
> With the overruns that I've seen, they've always been on the second
> "queue" and have always been for a sequence number several in the past
> beyond the point that the overrun has been reported. However, the
> packet which the sequence number matches had already been received -
> and several others have also been received. I've been wondering if it's
> a hardware bug, or maybe it's something other bits of the kernel is
> doing wrong.
Yes, in any case, using two queues like that prevents the PTP master from
working properly on my board. I will try to investigate the issue.
Regards,
--
Köry Maincent, Bootlin
Embedded Linux and kernel engineering
https://bootlin.com
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH RFC net-next 0/5] Marvell PTP support
2025-04-16 10:51 ` Russell King (Oracle)
@ 2025-04-16 13:19 ` Kory Maincent
2025-04-16 14:13 ` Russell King (Oracle)
0 siblings, 1 reply; 28+ messages in thread
From: Kory Maincent @ 2025-04-16 13:19 UTC (permalink / raw)
To: Russell King (Oracle)
Cc: Andrew Lunn, Heiner Kallweit, Andrew Lunn, David S. Miller,
Eric Dumazet, Jakub Kicinski, Marcin Wojtas, netdev, Paolo Abeni,
Richard Cochran, Vladimir Oltean
On Wed, 16 Apr 2025 11:51:27 +0100
"Russell King (Oracle)" <linux@armlinux.org.uk> wrote:
> On Fri, Apr 11, 2025 at 10:26:15PM +0100, Russell King (Oracle) wrote:
> > Hi,
> >
> > This series is a work in progress, and represents the current state of
> > things, superseding Kory's patches which were based in a very old
> > version of my patches - and my patches were subsequently refactored
> > and further developed about five years ago. Due to them breaking
> > mvpp2 if merged, there was no point in posting them until such time
> > that the underlying issues with PTP were resolved - and they now have
> > been.
> >
> > Marvell re-uses their PTP IP in several of their products - PHYs,
> > switches and even some ethernet MACs contain the same IP. It really
> > doesn't make sense to duplicate the code in each of these use cases.
> >
> > Therefore, this series introduces a Marvell PTP core that can be
> > re-used - a TAI module, which handles the global parts of the PTP
> > core, and the TS module, which handles the per-port timestamping.
> >
> > I will note at this point that although the Armada 388 TRM states that
> > NETA contains the same IP, attempts to access the registers returns
> > zero, and it is not known if that is due to the board missing something
> > or whether it isn't actually implemented. I do have some early work
> > re-using this, but when I discovered that the TAI registers read as
> > zero and wouldn't accept writes, I haven't progressed that.
> >
> > Today, I have converted the mv88e6xxx DSA code to use the Marvell TAI
> > module from patch 1, and for the sake of getting the code out there,
> > I have included the "hacky" patches in this series - with the issues
> > with DSA VLANs that I reported this evening and subsequently
> > investigated, I've not had any spare time to properly prepare that
> > part of this series. (Being usurped from phylink by stmmac - for which
> > I have a big stack of patches that I can't get out because of being
> > usurped, and then again by Marvell PTP, and then again by DSA VLAN
> > stuff... yea, I'm feeling like I have zero time to do anything right
> > now.) The mv88e6xxx DSA code still needs to be converted to use the
> > Marvell TS part of patch 1, but I won't be able to test that after
> > Sunday, and I'm certainly not working on this over this weekend.
> >
> > Anyway, this is what it is - and this is likely the state of it for
> > a while yet, because I won't be able to sensibly access the hardware
> > for testing for an undefined period of time.
> >
> > The PHY parts seem to work, although not 100% reliably, with the
> > occasional overrun, particularly on the receive side. I'm not sure
> > whether this is down to a hardware bug or not, or MDIO driver bug,
> > because we certainly aren't missing timestamping a SKB. This has been
> > tested at L2 and L4.
> >
> > I'm not sure which packets we should be timestamping (remembering
> > that this is global config across all ports.)
> > https://chronos.uk/wordpress/wp-content/uploads/TechnicalBrief-IEEE1588v2PTP.pdf
> > suggests Sync, Delay_req and Delay_resp need to be timestamped,
> > possibly PDelay_req and PDelay_resp as well, but I haven't seen
> > those produced by PTPDv2 nor ptp4l.
> >
> > There's probably other stuff I should mention, but as I've been at
> > this into the evening for almost every day this week, I'm mentally
> > exhausted.
> >
> > Sorry also if this isn't coherent.
>
> I've just updated this series for the supported pins flags that was
> merged into net-next last night.
>
> Kory, if you have any changes you want me to review before sending
> out the updates, please send soon. Thanks.
Maybe using kdoc in marvell_ts.c. You can copy paste the ones I wrote on the v2
of your driver I posted.
Also don't forget to run checkpatch, patchwork reported errors on your series.
It was not an issue as it was a RFC but it will if you remove it.
Regards,
--
Köry Maincent, Bootlin
Embedded Linux and kernel engineering
https://bootlin.com
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH RFC net-next 0/5] Marvell PTP support
2025-04-16 13:19 ` Kory Maincent
@ 2025-04-16 14:13 ` Russell King (Oracle)
0 siblings, 0 replies; 28+ messages in thread
From: Russell King (Oracle) @ 2025-04-16 14:13 UTC (permalink / raw)
To: Kory Maincent
Cc: Andrew Lunn, Heiner Kallweit, Andrew Lunn, David S. Miller,
Eric Dumazet, Jakub Kicinski, Marcin Wojtas, netdev, Paolo Abeni,
Richard Cochran, Vladimir Oltean
On Wed, Apr 16, 2025 at 03:19:49PM +0200, Kory Maincent wrote:
> Maybe using kdoc in marvell_ts.c. You can copy paste the ones I wrote on
> the v2 of your driver I posted.
My position on kdoc is that it's worth kdocing interface functions, but
should never be used for internal functions. How many developers build
the kernel docs to read documentation divorced from their static
functions? Probably zero.
> Also don't forget to run checkpatch, patchwork reported errors on your series.
> It was not an issue as it was a RFC but it will if you remove it.
I'll fix *some* of them that make sense to fix, but not all of them.
Those I won't be fixing are:
CHECK: Please use a blank line after function/struct/union/enum
declarations
#465: FILE: drivers/net/phy/marvell_ptp.h:14:
+}
+#define marvell_phy_ptp_probe(x...) marvell_phy_ptp_dummy_probe()
No, it makes sense to keep these together.
Also:
ERROR: Missing Signed-off-by: line(s)
on the last two patches - I refer anyone back to the cover message
where I stated about their current state. In any case, in this form
they will be rejected even if they had a commit description and sob.
They at the very least need squashing together once stuff has settled
and I gave reasons why that hasn't happened yet.
Given that I can't test that anymore, nothing's going to change on
those last two patches, which means they will remain unsubmittable
for some time to come.
--
RMK's Patch system: https://www.armlinux.org.uk/developer/patches/
FTTP is here! 80Mbps down 10Mbps up. Decent connectivity at last!
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH RFC net-next 2/5] ptp: marvell: add core support for Marvell PTP v2.1
2025-04-16 9:22 ` Russell King (Oracle)
2025-04-16 13:14 ` Kory Maincent
@ 2025-06-13 15:19 ` Kory Maincent
2025-10-03 13:09 ` Casper Andersson
1 sibling, 1 reply; 28+ messages in thread
From: Kory Maincent @ 2025-06-13 15:19 UTC (permalink / raw)
To: Russell King (Oracle)
Cc: Andrew Lunn, Heiner Kallweit, Andrew Lunn, David S. Miller,
Eric Dumazet, Jakub Kicinski, netdev, Paolo Abeni,
Richard Cochran
Hello Russell,
Le Wed, 16 Apr 2025 10:22:47 +0100,
"Russell King (Oracle)" <linux@armlinux.org.uk> a écrit :
> On Wed, Apr 16, 2025 at 10:48:49AM +0200, Kory Maincent wrote:
> > On Fri, 11 Apr 2025 22:26:37 +0100
> > Russell King <rmk+kernel@armlinux.org.uk> wrote:
> > > Provide core support for the Marvell PTP v2.1 implementations, which
> > > consist of a TAI (time application interface) and timestamping blocks.
> > > This hardware can be found in Marvell 88E151x PHYs, Armada 38x and
> > > Armada 37xx (mvneta), as well as Marvell DSA devices.
> > >
> > > Support for both arrival timestamps is supported, we use arrival 1 for
> > > PTP peer delay messages, and arrival 0 for all other messages.
> > >
> > > External event capture is also supported.
> > >
> > > PPS output and trigger generation is not supported.
> > >
> > > This core takes inspiration from the existing Marvell 88E6xxx DSA PTP
> > > code and DP83640 drivers. Like the original 88E6xxx DSA code, we
> > > use a delayed work to keep the cycle counter updated, and a separate
> > > delayed work for event capture.
> > >
> > > We expose the ptp clock aux work to allow users to support single and
> > > multi-port designs - where there is one Marvell TAI instance and a
> > > number of Marvell TS instances.
> >
> > ...
> >
> > > +#define MV_PTP_MSGTYPE_DELAY_RESP 9
> > > +
> > > +/* This defines which incoming or outgoing PTP frames are timestampped */
> > > +#define MV_PTP_MSD_ID_TS_EN (BIT(PTP_MSGTYPE_SYNC) | \
> > > + BIT(PTP_MSGTYPE_DELAY_REQ) | \
> > > + BIT(MV_PTP_MSGTYPE_DELAY_RESP))
> > > +/* Direct Sync messages to Arr0 and delay messages to Arr1 */
> > > +#define MV_PTP_TS_ARR_PTR (BIT(PTP_MSGTYPE_DELAY_REQ) | \
> > > + BIT(MV_PTP_MSGTYPE_DELAY_RESP))
> >
> > Why did you have chosen to use two queues with two separate behavior?
> > I have tried using only one queue and the PTP as master behaves correctly
> > without all these overrun. It is way better with one queue.
> > Maybe it was not the best approach if you want to use the two queues.
>
> First, both queues have the same behaviour.
>
> Second, because they *aren't* queues as they can only stamp one message.
> The sync messages come from the master on a regular basis. The delay
> response messages come from the master in response to a delay request
> message, the timing of which is determined by the local slave.
>
> If the local end sends a delay request just at the point that the master
> sends a sync message causing the master to immediately follow the sync
> message with the delay response message, then we could get an overrun
> on a single queue - because we'll stamp the sync message and if we don't
> read the timestamp quickly enough, the stamp registers will be busy
> preventing the timestamp of the delay response being captured.
I just come back on this. I think you are wrong.
We are using different registers to save timestamp for departure or arrival
packets.
If there is a sync message and a delay request message (the delay response is
not timestamped) at the same time, the sync message timestamp will be save in
the departure registers and the delay request timestamp in the arrival registers
(or the contrary if the Marvell PTP is a follower). There is no possibility to
loose the timestamp on that case, therefore using two registers for arrival
packets is useless.
FYI, I just tested the patch series on the Espressobin which have a mv88e6xxx
and the PTP worked well.
Do you now If you will have time to send a v2 or should I take the lead on
this series?
> With the overruns that I've seen, they've always been on the second
> "queue" and have always been for a sequence number several in the past
> beyond the point that the overrun has been reported. However, the
> packet which the sequence number matches had already been received -
> and several others have also been received. I've been wondering if it's
> a hardware bug, or maybe it's something other bits of the kernel is
> doing wrong.
I think we should drop this as I don't see anything relevant to use two
"queues" except overrun cases.
Regards,
--
Köry Maincent, Bootlin
Embedded Linux and kernel engineering
https://bootlin.com
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH RFC net-next 2/5] ptp: marvell: add core support for Marvell PTP v2.1
2025-06-13 15:19 ` Kory Maincent
@ 2025-10-03 13:09 ` Casper Andersson
0 siblings, 0 replies; 28+ messages in thread
From: Casper Andersson @ 2025-10-03 13:09 UTC (permalink / raw)
To: Kory Maincent, Russell King (Oracle)
Cc: Andrew Lunn, Heiner Kallweit, Andrew Lunn, David S. Miller,
Eric Dumazet, Jakub Kicinski, netdev, Paolo Abeni,
Richard Cochran
Hi, I'm also late to the ball here.
On Fri Jun 13, 2025 at 5:19 PM CEST, Kory Maincent wrote:
> Hello Russell,
>
> Le Wed, 16 Apr 2025 10:22:47 +0100,
> "Russell King (Oracle)" <linux@armlinux.org.uk> a écrit :
>
>> On Wed, Apr 16, 2025 at 10:48:49AM +0200, Kory Maincent wrote:
>> > On Fri, 11 Apr 2025 22:26:37 +0100
>> > Russell King <rmk+kernel@armlinux.org.uk> wrote:
>> > > Provide core support for the Marvell PTP v2.1 implementations, which
>> > > consist of a TAI (time application interface) and timestamping blocks.
>> > > This hardware can be found in Marvell 88E151x PHYs, Armada 38x and
>> > > Armada 37xx (mvneta), as well as Marvell DSA devices.
>> > >
>> > > Support for both arrival timestamps is supported, we use arrival 1 for
>> > > PTP peer delay messages, and arrival 0 for all other messages.
>> > >
>> > > External event capture is also supported.
>> > >
>> > > PPS output and trigger generation is not supported.
>> > >
>> > > This core takes inspiration from the existing Marvell 88E6xxx DSA PTP
>> > > code and DP83640 drivers. Like the original 88E6xxx DSA code, we
>> > > use a delayed work to keep the cycle counter updated, and a separate
>> > > delayed work for event capture.
>> > >
>> > > We expose the ptp clock aux work to allow users to support single and
>> > > multi-port designs - where there is one Marvell TAI instance and a
>> > > number of Marvell TS instances.
>> >
>> > ...
>> >
>> > > +#define MV_PTP_MSGTYPE_DELAY_RESP 9
>> > > +
>> > > +/* This defines which incoming or outgoing PTP frames are timestampped */
>> > > +#define MV_PTP_MSD_ID_TS_EN (BIT(PTP_MSGTYPE_SYNC) | \
>> > > + BIT(PTP_MSGTYPE_DELAY_REQ) | \
>> > > + BIT(MV_PTP_MSGTYPE_DELAY_RESP))
>> > > +/* Direct Sync messages to Arr0 and delay messages to Arr1 */
>> > > +#define MV_PTP_TS_ARR_PTR (BIT(PTP_MSGTYPE_DELAY_REQ) | \
>> > > + BIT(MV_PTP_MSGTYPE_DELAY_RESP))
>> >
>> > Why did you have chosen to use two queues with two separate behavior?
>> > I have tried using only one queue and the PTP as master behaves correctly
>> > without all these overrun. It is way better with one queue.
>> > Maybe it was not the best approach if you want to use the two queues.
>>
>> First, both queues have the same behaviour.
>>
>> Second, because they *aren't* queues as they can only stamp one message.
>> The sync messages come from the master on a regular basis. The delay
>> response messages come from the master in response to a delay request
>> message, the timing of which is determined by the local slave.
>>
>> If the local end sends a delay request just at the point that the master
>> sends a sync message causing the master to immediately follow the sync
>> message with the delay response message, then we could get an overrun
>> on a single queue - because we'll stamp the sync message and if we don't
>> read the timestamp quickly enough, the stamp registers will be busy
>> preventing the timestamp of the delay response being captured.
>
> I just come back on this. I think you are wrong.
> We are using different registers to save timestamp for departure or arrival
> packets.
> If there is a sync message and a delay request message (the delay response is
> not timestamped) at the same time, the sync message timestamp will be save in
> the departure registers and the delay request timestamp in the arrival registers
> (or the contrary if the Marvell PTP is a follower). There is no possibility to
> loose the timestamp on that case, therefore using two registers for arrival
> packets is useless.
E2E timestamping should never have any collision on RX timestamps. But
P2P can. Sync, PdelayReq, and PdelayResp all need to be RX timestamped.
Running normally with ptp4l it worked pretty fine with PdelayResp on a
separate "queue". But Calnex Paragon-X (PTP tester) sends Sync and
PdelayReq almost simultaneously, causing collision. So there really
isn't a perfect solution to this. This was my experience from a while
back with Russell's original RFC from years ago.
Some variants of this PTP block (e.g. the 1512p and mv88e6390x I have
access to) have the ability to store the timestamp in the reserved2
field, which makes RX timestamps much simpler and less error-prone (but
they also support the full onestep offloading using Time Arrays which
requires a totally different implementation).
BR
Casper
>
> FYI, I just tested the patch series on the Espressobin which have a mv88e6xxx
> and the PTP worked well.
>
> Do you now If you will have time to send a v2 or should I take the lead on
> this series?
>
>> With the overruns that I've seen, they've always been on the second
>> "queue" and have always been for a sequence number several in the past
>> beyond the point that the overrun has been reported. However, the
>> packet which the sequence number matches had already been received -
>> and several others have also been received. I've been wondering if it's
>> a hardware bug, or maybe it's something other bits of the kernel is
>> doing wrong.
>
> I think we should drop this as I don't see anything relevant to use two
> "queues" except overrun cases.
>
> Regards,
^ permalink raw reply [flat|nested] 28+ messages in thread
end of thread, other threads:[~2025-10-03 13:09 UTC | newest]
Thread overview: 28+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-04-11 21:26 [PATCH RFC net-next 0/5] Marvell PTP support Russell King (Oracle)
2025-04-11 21:26 ` [PATCH RFC net-next 1/5] net: mvpp2: add support for hardware timestamps Russell King
2025-04-11 21:29 ` Russell King (Oracle)
2025-04-14 12:51 ` Kory Maincent
2025-04-14 14:43 ` Russell King (Oracle)
2025-04-14 15:02 ` Kory Maincent
2025-04-11 21:26 ` [PATCH RFC net-next 2/5] ptp: marvell: add core support for Marvell PTP v2.1 Russell King
2025-04-11 21:30 ` Russell King (Oracle)
2025-04-16 8:48 ` Kory Maincent
2025-04-16 9:22 ` Russell King (Oracle)
2025-04-16 13:14 ` Kory Maincent
2025-06-13 15:19 ` Kory Maincent
2025-10-03 13:09 ` Casper Andersson
2025-04-11 21:26 ` [PATCH RFC net-next 3/5] net: phy: add Marvell PHY PTP support Russell King
2025-04-14 12:33 ` Kory Maincent
2025-04-14 12:37 ` Russell King (Oracle)
2025-04-14 13:57 ` Russell King (Oracle)
2025-04-14 14:43 ` Kory Maincent
2025-04-14 14:55 ` Russell King (Oracle)
2025-04-14 15:21 ` Kory Maincent
2025-04-14 16:16 ` Kory Maincent
2025-04-14 16:35 ` Russell King (Oracle)
2025-04-11 21:26 ` [PATCH RFC net-next 4/5] mv88e6xxx: convert to marvell TAI Russell King (Oracle)
2025-04-11 21:26 ` [PATCH RFC net-next 5/5] mv88e6xxx: cleanup ptp tai Russell King (Oracle)
2025-04-11 21:30 ` [PATCH RFC net-next 0/5] Marvell PTP support Russell King (Oracle)
2025-04-16 10:51 ` Russell King (Oracle)
2025-04-16 13:19 ` Kory Maincent
2025-04-16 14:13 ` Russell King (Oracle)
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).