Commit 4715f65f authored by Richard Cochran's avatar Richard Cochran Committed by David S. Miller
Browse files

net: Introduce a new MII time stamping interface.



Currently the stack supports time stamping in PHY devices.  However,
there are newer, non-PHY devices that can snoop an MII bus and provide
time stamps.  In order to support such devices, this patch introduces
a new interface to be used by both PHY and non-PHY devices.

In addition, the one and only user of the old PHY time stamping API is
converted to the new interface.

Signed-off-by: default avatarRichard Cochran <richardcochran@gmail.com>
Reviewed-by: default avatarAndrew Lunn <andrew@lunn.ch>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 12d0efb9
Loading
Loading
Loading
Loading
+24 −15
Original line number Diff line number Diff line
@@ -98,6 +98,7 @@ struct dp83640_private {
	struct list_head list;
	struct dp83640_clock *clock;
	struct phy_device *phydev;
	struct mii_timestamper mii_ts;
	struct delayed_work ts_work;
	int hwts_tx_en;
	int hwts_rx_en;
@@ -1229,9 +1230,10 @@ static int dp83640_config_intr(struct phy_device *phydev)
	}
}

static int dp83640_hwtstamp(struct phy_device *phydev, struct ifreq *ifr)
static int dp83640_hwtstamp(struct mii_timestamper *mii_ts, struct ifreq *ifr)
{
	struct dp83640_private *dp83640 = phydev->priv;
	struct dp83640_private *dp83640 =
		container_of(mii_ts, struct dp83640_private, mii_ts);
	struct hwtstamp_config cfg;
	u16 txcfg0, rxcfg0;

@@ -1307,8 +1309,8 @@ static int dp83640_hwtstamp(struct phy_device *phydev, struct ifreq *ifr)

	mutex_lock(&dp83640->clock->extreg_lock);

	ext_write(0, phydev, PAGE5, PTP_TXCFG0, txcfg0);
	ext_write(0, phydev, PAGE5, PTP_RXCFG0, rxcfg0);
	ext_write(0, dp83640->phydev, PAGE5, PTP_TXCFG0, txcfg0);
	ext_write(0, dp83640->phydev, PAGE5, PTP_RXCFG0, rxcfg0);

	mutex_unlock(&dp83640->clock->extreg_lock);

@@ -1338,10 +1340,11 @@ static void rx_timestamp_work(struct work_struct *work)
		schedule_delayed_work(&dp83640->ts_work, SKB_TIMESTAMP_TIMEOUT);
}

static bool dp83640_rxtstamp(struct phy_device *phydev,
static bool dp83640_rxtstamp(struct mii_timestamper *mii_ts,
			     struct sk_buff *skb, int type)
{
	struct dp83640_private *dp83640 = phydev->priv;
	struct dp83640_private *dp83640 =
		container_of(mii_ts, struct dp83640_private, mii_ts);
	struct dp83640_skb_info *skb_info = (struct dp83640_skb_info *)skb->cb;
	struct list_head *this, *next;
	struct rxts *rxts;
@@ -1387,11 +1390,12 @@ static bool dp83640_rxtstamp(struct phy_device *phydev,
	return true;
}

static void dp83640_txtstamp(struct phy_device *phydev,
static void dp83640_txtstamp(struct mii_timestamper *mii_ts,
			     struct sk_buff *skb, int type)
{
	struct dp83640_skb_info *skb_info = (struct dp83640_skb_info *)skb->cb;
	struct dp83640_private *dp83640 = phydev->priv;
	struct dp83640_private *dp83640 =
		container_of(mii_ts, struct dp83640_private, mii_ts);

	switch (dp83640->hwts_tx_en) {

@@ -1414,9 +1418,11 @@ static void dp83640_txtstamp(struct phy_device *phydev,
	}
}

static int dp83640_ts_info(struct phy_device *dev, struct ethtool_ts_info *info)
static int dp83640_ts_info(struct mii_timestamper *mii_ts,
			   struct ethtool_ts_info *info)
{
	struct dp83640_private *dp83640 = dev->priv;
	struct dp83640_private *dp83640 =
		container_of(mii_ts, struct dp83640_private, mii_ts);

	info->so_timestamping =
		SOF_TIMESTAMPING_TX_HARDWARE |
@@ -1454,13 +1460,18 @@ static int dp83640_probe(struct phy_device *phydev)
		goto no_memory;

	dp83640->phydev = phydev;
	INIT_DELAYED_WORK(&dp83640->ts_work, rx_timestamp_work);
	dp83640->mii_ts.rxtstamp = dp83640_rxtstamp;
	dp83640->mii_ts.txtstamp = dp83640_txtstamp;
	dp83640->mii_ts.hwtstamp = dp83640_hwtstamp;
	dp83640->mii_ts.ts_info  = dp83640_ts_info;

	INIT_DELAYED_WORK(&dp83640->ts_work, rx_timestamp_work);
	INIT_LIST_HEAD(&dp83640->rxts);
	INIT_LIST_HEAD(&dp83640->rxpool);
	for (i = 0; i < MAX_RXTS; i++)
		list_add(&dp83640->rx_pool_data[i].list, &dp83640->rxpool);

	phydev->mii_ts = &dp83640->mii_ts;
	phydev->priv = dp83640;

	spin_lock_init(&dp83640->rx_lock);
@@ -1501,6 +1512,8 @@ static void dp83640_remove(struct phy_device *phydev)
	if (phydev->mdio.addr == BROADCAST_ADDR)
		return;

	phydev->mii_ts = NULL;

	enable_status_frames(phydev, false);
	cancel_delayed_work_sync(&dp83640->ts_work);

@@ -1537,10 +1550,6 @@ static struct phy_driver dp83640_driver = {
	.config_init	= dp83640_config_init,
	.ack_interrupt  = dp83640_ack_interrupt,
	.config_intr    = dp83640_config_intr,
	.ts_info	= dp83640_ts_info,
	.hwtstamp	= dp83640_hwtstamp,
	.rxtstamp	= dp83640_rxtstamp,
	.txtstamp	= dp83640_txtstamp,
};

static int __init dp83640_init(void)
+2 −2
Original line number Diff line number Diff line
@@ -422,8 +422,8 @@ int phy_mii_ioctl(struct phy_device *phydev, struct ifreq *ifr, int cmd)
		return 0;

	case SIOCSHWTSTAMP:
		if (phydev->drv && phydev->drv->hwtstamp)
			return phydev->drv->hwtstamp(phydev, ifr);
		if (phydev->mii_ts && phydev->mii_ts->hwtstamp)
			return phydev->mii_ts->hwtstamp(phydev->mii_ts, ifr);
		/* fall through */

	default:
+2 −0
Original line number Diff line number Diff line
@@ -919,6 +919,8 @@ static void phy_link_change(struct phy_device *phydev, bool up, bool do_carrier)
			netif_carrier_off(netdev);
	}
	phydev->adjust_link(netdev);
	if (phydev->mii_ts && phydev->mii_ts->link_state)
		phydev->mii_ts->link_state(phydev->mii_ts, phydev);
}

/**
+58 −0
Original line number Diff line number Diff line
/* SPDX-License-Identifier: GPL-2.0 */
/*
 * Support for generic time stamping devices on MII buses.
 * Copyright (C) 2018 Richard Cochran <richardcochran@gmail.com>
 */
#ifndef _LINUX_MII_TIMESTAMPER_H
#define _LINUX_MII_TIMESTAMPER_H

#include <linux/device.h>
#include <linux/ethtool.h>
#include <linux/skbuff.h>

struct phy_device;

/**
 * struct mii_timestamper - Callback interface to MII time stamping devices.
 *
 * @rxtstamp:	Requests a Rx timestamp for 'skb'.  If the skb is accepted,
 *		the MII time stamping device promises to deliver it using
 *		netif_rx() as soon as a timestamp becomes available. One of
 *		the PTP_CLASS_ values is passed in 'type'.  The function
 *		must return true if the skb is accepted for delivery.
 *
 * @txtstamp:	Requests a Tx timestamp for 'skb'.  The MII time stamping
 *		device promises to deliver it using skb_complete_tx_timestamp()
 *		as soon as a timestamp becomes available. One of the PTP_CLASS_
 *		values is passed in 'type'.
 *
 * @hwtstamp:	Handles SIOCSHWTSTAMP ioctl for hardware time stamping.
 *
 * @link_state: Allows the device to respond to changes in the link
 *		state.  The caller invokes this function while holding
 *		the phy_device mutex.
 *
 * @ts_info:	Handles ethtool queries for hardware time stamping.
 *
 * Drivers for PHY time stamping devices should embed their
 * mii_timestamper within a private structure, obtaining a reference
 * to it using container_of().
 */
struct mii_timestamper {
	bool (*rxtstamp)(struct mii_timestamper *mii_ts,
			 struct sk_buff *skb, int type);

	void (*txtstamp)(struct mii_timestamper *mii_ts,
			 struct sk_buff *skb, int type);

	int  (*hwtstamp)(struct mii_timestamper *mii_ts,
			 struct ifreq *ifreq);

	void (*link_state)(struct mii_timestamper *mii_ts,
			   struct phy_device *phydev);

	int  (*ts_info)(struct mii_timestamper *mii_ts,
			struct ethtool_ts_info *ts_info);
};

#endif
+10 −31
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
#include <linux/linkmode.h>
#include <linux/mdio.h>
#include <linux/mii.h>
#include <linux/mii_timestamper.h>
#include <linux/module.h>
#include <linux/timer.h>
#include <linux/workqueue.h>
@@ -441,6 +442,7 @@ struct phy_device {
	struct sfp_bus *sfp_bus;
	struct phylink *phylink;
	struct net_device *attached_dev;
	struct mii_timestamper *mii_ts;

	u8 mdix;
	u8 mdix_ctrl;
@@ -546,29 +548,6 @@ struct phy_driver {
	 */
	int (*match_phy_device)(struct phy_device *phydev);

	/* Handles ethtool queries for hardware time stamping. */
	int (*ts_info)(struct phy_device *phydev, struct ethtool_ts_info *ti);

	/* Handles SIOCSHWTSTAMP ioctl for hardware time stamping. */
	int  (*hwtstamp)(struct phy_device *phydev, struct ifreq *ifr);

	/*
	 * Requests a Rx timestamp for 'skb'. If the skb is accepted,
	 * the phy driver promises to deliver it using netif_rx() as
	 * soon as a timestamp becomes available. One of the
	 * PTP_CLASS_ values is passed in 'type'. The function must
	 * return true if the skb is accepted for delivery.
	 */
	bool (*rxtstamp)(struct phy_device *dev, struct sk_buff *skb, int type);

	/*
	 * Requests a Tx timestamp for 'skb'. The phy driver promises
	 * to deliver it using skb_complete_tx_timestamp() as soon as a
	 * timestamp becomes available. One of the PTP_CLASS_ values
	 * is passed in 'type'.
	 */
	void (*txtstamp)(struct phy_device *dev, struct sk_buff *skb, int type);

	/* Some devices (e.g. qnap TS-119P II) require PHY register changes to
	 * enable Wake on LAN, so set_wol is provided to be called in the
	 * ethernet driver's set_wol function. */
@@ -942,7 +921,7 @@ static inline bool phy_polling_mode(struct phy_device *phydev)
 */
static inline bool phy_has_hwtstamp(struct phy_device *phydev)
{
	return phydev && phydev->drv && phydev->drv->hwtstamp;
	return phydev && phydev->mii_ts && phydev->mii_ts->hwtstamp;
}

/**
@@ -951,7 +930,7 @@ static inline bool phy_has_hwtstamp(struct phy_device *phydev)
 */
static inline bool phy_has_rxtstamp(struct phy_device *phydev)
{
	return phydev && phydev->drv && phydev->drv->rxtstamp;
	return phydev && phydev->mii_ts && phydev->mii_ts->rxtstamp;
}

/**
@@ -961,7 +940,7 @@ static inline bool phy_has_rxtstamp(struct phy_device *phydev)
 */
static inline bool phy_has_tsinfo(struct phy_device *phydev)
{
	return phydev && phydev->drv && phydev->drv->ts_info;
	return phydev && phydev->mii_ts && phydev->mii_ts->ts_info;
}

/**
@@ -970,30 +949,30 @@ static inline bool phy_has_tsinfo(struct phy_device *phydev)
 */
static inline bool phy_has_txtstamp(struct phy_device *phydev)
{
	return phydev && phydev->drv && phydev->drv->txtstamp;
	return phydev && phydev->mii_ts && phydev->mii_ts->txtstamp;
}

static inline int phy_hwtstamp(struct phy_device *phydev, struct ifreq *ifr)
{
	return phydev->drv->hwtstamp(phydev, ifr);
	return phydev->mii_ts->hwtstamp(phydev->mii_ts, ifr);
}

static inline bool phy_rxtstamp(struct phy_device *phydev, struct sk_buff *skb,
				int type)
{
	return phydev->drv->rxtstamp(phydev, skb, type);
	return phydev->mii_ts->rxtstamp(phydev->mii_ts, skb, type);
}

static inline int phy_ts_info(struct phy_device *phydev,
			      struct ethtool_ts_info *tsinfo)
{
	return phydev->drv->ts_info(phydev, tsinfo);
	return phydev->mii_ts->ts_info(phydev->mii_ts, tsinfo);
}

static inline void phy_txtstamp(struct phy_device *phydev, struct sk_buff *skb,
				int type)
{
	phydev->drv->txtstamp(phydev, skb, type);
	phydev->mii_ts->txtstamp(phydev->mii_ts, skb, type);
}

/**
Loading