[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <Z_mJ_VSZLLInYiu0@shell.armlinux.org.uk>
Date: Fri, 11 Apr 2025 22:30:37 +0100
From: "Russell King (Oracle)" <linux@...linux.org.uk>
To: Andrew Lunn <andrew@...n.ch>, Heiner Kallweit <hkallweit1@...il.com>
Cc: Andrew Lunn <andrew+netdev@...n.ch>,
"David S. Miller" <davem@...emloft.net>,
Eric Dumazet <edumazet@...gle.com>,
Jakub Kicinski <kuba@...nel.org>,
Kory Maincent <kory.maincent@...tlin.com>, netdev@...r.kernel.org,
Paolo Abeni <pabeni@...hat.com>,
Richard Cochran <richardcochran@...il.com>
Subject: Re: [PATCH RFC net-next 2/5] ptp: marvell: add core support for
Marvell PTP v2.1
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@...linux.org.uk>
> Signed-off-by: Russell King (Oracle) <rmk+kernel@...linux.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!
Powered by blists - more mailing lists