@@ -931,6 +931,9 @@ struct bcm_ptp_private *bcm_ptp_probe(struct phy_device *phydev)
return ERR_CAST(clock);
priv->ptp_clock = clock;
+ /* Timestamp selected by default to keep legacy API */
+ phydev->default_timestamp = true;
+
priv->phydev = phydev;
bcm_ptp_init(priv);
@@ -1450,6 +1450,9 @@ static int dp83640_probe(struct phy_device *phydev)
phydev->mii_ts = &dp83640->mii_ts;
phydev->priv = dp83640;
+ /* Timestamp selected by default to keep legacy API */
+ phydev->default_timestamp = true;
+
spin_lock_init(&dp83640->rx_lock);
skb_queue_head_init(&dp83640->rx_queue);
skb_queue_head_init(&dp83640->tx_queue);
@@ -3136,6 +3136,9 @@ static void lan8814_ptp_init(struct phy_device *phydev)
ptp_priv->mii_ts.ts_info = lan8814_ts_info;
phydev->mii_ts = &ptp_priv->mii_ts;
+
+ /* Timestamp selected by default to keep legacy API */
+ phydev->default_timestamp = true;
}
static int lan8814_ptp_probe_once(struct phy_device *phydev)
@@ -4564,6 +4567,9 @@ static int lan8841_probe(struct phy_device *phydev)
phydev->mii_ts = &ptp_priv->mii_ts;
+ /* Timestamp selected by default to keep legacy API */
+ phydev->default_timestamp = true;
+
return 0;
}
@@ -1570,6 +1570,8 @@ int vsc8584_ptp_probe(struct phy_device *phydev)
return PTR_ERR(vsc8531->load_save);
}
+ /* Timestamp selected by default to keep legacy API */
+ phydev->default_timestamp = true;
vsc8531->ptp->phydev = phydev;
return 0;
@@ -1658,6 +1658,9 @@ static int nxp_c45_probe(struct phy_device *phydev)
priv->mii_ts.ts_info = nxp_c45_ts_info;
phydev->mii_ts = &priv->mii_ts;
ret = nxp_c45_init_ptp_clock(priv);
+
+ /* Timestamp selected by default to keep legacy API */
+ phydev->default_timestamp = true;
} else {
phydev_dbg(phydev, "PTP support not enabled even if the phy supports it");
}
@@ -1411,6 +1411,26 @@ int phy_sfp_probe(struct phy_device *phydev,
}
EXPORT_SYMBOL(phy_sfp_probe);
+/**
+ * phy_set_timestamp - set the default selected timestamping device
+ * @dev: Pointer to net_device
+ * @phydev: Pointer to phy_device
+ *
+ * This is used to set default timestamping device taking into account
+ * the new API choice, which is selecting the timestamping from MAC by
+ * default if the phydev does not have default_timestamp flag enabled.
+ */
+static void phy_set_timestamp(struct net_device *dev, struct phy_device *phydev)
+{
+ const struct ethtool_ops *ops = dev->ethtool_ops;
+
+ if (!phy_has_tsinfo(phydev))
+ return;
+
+ if (!ops->get_ts_info || phydev->default_timestamp)
+ dev->ts_layer = PHY_TIMESTAMPING;
+}
+
/**
* phy_attach_direct - attach a network device to a given PHY device pointer
* @dev: network device to attach
@@ -1484,6 +1504,7 @@ int phy_attach_direct(struct net_device *dev, struct phy_device *phydev,
phydev->phy_link_change = phy_link_change;
if (dev) {
+ phy_set_timestamp(dev, phydev);
phydev->attached_dev = dev;
dev->phydev = phydev;
@@ -1812,6 +1833,22 @@ void phy_detach(struct phy_device *phydev)
phy_suspend(phydev);
if (dev) {
+ const struct ethtool_ops *ops = dev->ethtool_ops;
+ struct ethtool_ts_info ts_info = {0};
+
+ if (ops->get_ts_info) {
+ ops->get_ts_info(dev, &ts_info);
+ if ((ts_info.so_timestamping &
+ SOF_TIMESTAMPING_HARDWARE_MASK) ==
+ SOF_TIMESTAMPING_HARDWARE_MASK)
+ dev->ts_layer = MAC_TIMESTAMPING;
+ else if ((ts_info.so_timestamping &
+ SOF_TIMESTAMPING_SOFTWARE_MASK) ==
+ SOF_TIMESTAMPING_SOFTWARE_MASK)
+ dev->ts_layer = SOFTWARE_TIMESTAMPING;
+ } else {
+ dev->ts_layer = NO_TIMESTAMPING;
+ }
phydev->attached_dev->phydev = NULL;
phydev->attached_dev = NULL;
}
@@ -47,6 +47,7 @@
#include <uapi/linux/if_bonding.h>
#include <uapi/linux/pkt_cls.h>
#include <uapi/linux/netdev.h>
+#include <uapi/linux/net_tstamp.h>
#include <linux/hashtable.h>
#include <linux/rbtree.h>
#include <net/net_trackers.h>
@@ -2054,6 +2055,8 @@ enum netdev_ml_priv_type {
*
* @dpll_pin: Pointer to the SyncE source pin of a DPLL subsystem,
* where the clock is recovered.
+ * @ts_layer: Tracks which network device
+ * performs packet time stamping.
*
* FIXME: cleanup struct net_device such that network protocol info
* moves out.
@@ -2415,6 +2418,8 @@ struct net_device {
#if IS_ENABLED(CONFIG_DPLL)
struct dpll_pin *dpll_pin;
#endif
+
+ enum timestamping_layer ts_layer;
};
#define to_net_dev(d) container_of(d, struct net_device, dev)
@@ -604,6 +604,8 @@ struct macsec_ops;
* handling shall be postponed until PHY has resumed
* @irq_rerun: Flag indicating interrupts occurred while PHY was suspended,
* requiring a rerun of the interrupt handler after resume
+ * @default_timestamp: Flag indicating whether we are using the phy
+ * timestamp as the default one
* @interface: enum phy_interface_t value
* @skb: Netlink message for cable diagnostics
* @nest: Netlink nest used for cable diagnostics
@@ -667,6 +669,8 @@ struct phy_device {
unsigned irq_suspended:1;
unsigned irq_rerun:1;
+ unsigned default_timestamp:1;
+
int rate_matching;
enum phy_state state;
@@ -10204,6 +10204,9 @@ int register_netdevice(struct net_device *dev)
dev->rtnl_link_state == RTNL_LINK_INITIALIZED)
rtmsg_ifinfo(RTM_NEWLINK, dev, ~0U, GFP_KERNEL, 0, NULL);
+ if (dev->ethtool_ops->get_ts_info)
+ dev->ts_layer = MAC_TIMESTAMPING;
+
out:
return ret;
@@ -259,9 +259,7 @@ static int dev_eth_ioctl(struct net_device *dev,
* @dev: Network device
* @cfg: Timestamping configuration structure
*
- * Helper for enforcing a common policy that phylib timestamping, if available,
- * should take precedence in front of hardware timestamping provided by the
- * netdev.
+ * Helper for calling the selected hardware provider timestamping.
*
* Note: phy_mii_ioctl() only handles SIOCSHWTSTAMP (not SIOCGHWTSTAMP), and
* there only exists a phydev->mii_ts->hwtstamp() method. So this will return
@@ -271,10 +269,14 @@ static int dev_eth_ioctl(struct net_device *dev,
static int dev_get_hwtstamp_phylib(struct net_device *dev,
struct kernel_hwtstamp_config *cfg)
{
- if (phy_has_hwtstamp(dev->phydev))
+ enum timestamping_layer ts_layer = dev->ts_layer;
+
+ if (ts_layer == PHY_TIMESTAMPING)
return phy_hwtstamp_get(dev->phydev, cfg);
+ else if (ts_layer == MAC_TIMESTAMPING)
+ return dev->netdev_ops->ndo_hwtstamp_get(dev, cfg);
- return dev->netdev_ops->ndo_hwtstamp_get(dev, cfg);
+ return -EOPNOTSUPP;
}
static int dev_get_hwtstamp(struct net_device *dev, struct ifreq *ifr)
@@ -315,9 +317,8 @@ static int dev_get_hwtstamp(struct net_device *dev, struct ifreq *ifr)
* @cfg: Timestamping configuration structure
* @extack: Netlink extended ack message structure, for error reporting
*
- * Helper for enforcing a common policy that phylib timestamping, if available,
- * should take precedence in front of hardware timestamping provided by the
- * netdev. If the netdev driver needs to perform specific actions even for PHY
+ * Helper for calling the selected hardware provider timestamping.
+ * If the netdev driver needs to perform specific actions even for PHY
* timestamping to work properly (a switch port must trap the timestamped
* frames and not forward them), it must set IFF_SEE_ALL_HWTSTAMP_REQUESTS in
* dev->priv_flags.
@@ -327,20 +328,26 @@ int dev_set_hwtstamp_phylib(struct net_device *dev,
struct netlink_ext_ack *extack)
{
const struct net_device_ops *ops = dev->netdev_ops;
- bool phy_ts = phy_has_hwtstamp(dev->phydev);
+ enum timestamping_layer ts_layer = dev->ts_layer;
struct kernel_hwtstamp_config old_cfg = {};
bool changed = false;
int err;
- cfg->source = phy_ts ? PHY_TIMESTAMPING : NET_TIMESTAMPING;
+ cfg->source = ts_layer;
+
+ if (ts_layer != PHY_TIMESTAMPING &&
+ ts_layer != MAC_TIMESTAMPING)
+ return -EOPNOTSUPP;
- if (phy_ts && (dev->priv_flags & IFF_SEE_ALL_HWTSTAMP_REQUESTS)) {
+ if (ts_layer == PHY_TIMESTAMPING &&
+ dev->priv_flags & IFF_SEE_ALL_HWTSTAMP_REQUESTS) {
err = ops->ndo_hwtstamp_get(dev, &old_cfg);
if (err)
return err;
}
- if (!phy_ts || (dev->priv_flags & IFF_SEE_ALL_HWTSTAMP_REQUESTS)) {
+ if (ts_layer == MAC_TIMESTAMPING ||
+ dev->priv_flags & IFF_SEE_ALL_HWTSTAMP_REQUESTS) {
err = ops->ndo_hwtstamp_set(dev, cfg, extack);
if (err) {
if (extack->_msg)
@@ -349,10 +356,11 @@ int dev_set_hwtstamp_phylib(struct net_device *dev,
}
}
- if (phy_ts && (dev->priv_flags & IFF_SEE_ALL_HWTSTAMP_REQUESTS))
+ if (ts_layer == PHY_TIMESTAMPING &&
+ dev->priv_flags & IFF_SEE_ALL_HWTSTAMP_REQUESTS)
changed = kernel_hwtstamp_config_changed(&old_cfg, cfg);
- if (phy_ts) {
+ if (ts_layer == PHY_TIMESTAMPING) {
err = phy_hwtstamp_set(dev->phydev, cfg, extack);
if (err) {
if (changed)
@@ -21,6 +21,7 @@ static unsigned int classify(const struct sk_buff *skb)
void skb_clone_tx_timestamp(struct sk_buff *skb)
{
+ enum timestamping_layer ts_layer;
struct mii_timestamper *mii_ts;
struct sk_buff *clone;
unsigned int type;
@@ -28,6 +29,10 @@ void skb_clone_tx_timestamp(struct sk_buff *skb)
if (!skb->sk)
return;
+ ts_layer = skb->dev->ts_layer;
+ if (ts_layer != PHY_TIMESTAMPING)
+ return;
+
type = classify(skb);
if (type == PTP_CLASS_NONE)
return;
@@ -44,12 +49,17 @@ EXPORT_SYMBOL_GPL(skb_clone_tx_timestamp);
bool skb_defer_rx_timestamp(struct sk_buff *skb)
{
+ enum timestamping_layer ts_layer;
struct mii_timestamper *mii_ts;
unsigned int type;
if (!skb->dev || !skb->dev->phydev || !skb->dev->phydev->mii_ts)
return false;
+ ts_layer = skb->dev->ts_layer;
+ if (ts_layer != PHY_TIMESTAMPING)
+ return false;
+
if (skb_headroom(skb) < ETH_HLEN)
return false;
@@ -633,13 +633,28 @@ int __ethtool_get_ts_info(struct net_device *dev, struct ethtool_ts_info *info)
{
const struct ethtool_ops *ops = dev->ethtool_ops;
struct phy_device *phydev = dev->phydev;
+ enum timestamping_layer ts_layer;
+ int ret;
memset(info, 0, sizeof(*info));
info->cmd = ETHTOOL_GET_TS_INFO;
- if (phy_has_tsinfo(phydev))
+ ts_layer = dev->ts_layer;
+ if (ts_layer == SOFTWARE_TIMESTAMPING) {
+ ret = ops->get_ts_info(dev, info);
+ if (ret)
+ return ret;
+ info->so_timestamping &= ~SOF_TIMESTAMPING_HARDWARE_MASK;
+ info->phc_index = -1;
+ info->rx_filters = 0;
+ info->tx_types = 0;
+ return 0;
+ }
+
+ if (ts_layer == PHY_TIMESTAMPING)
return phy_ts_info(phydev, info);
- if (ops->get_ts_info)
+
+ if (ts_layer == MAC_TIMESTAMPING)
return ops->get_ts_info(dev, info);
info->so_timestamping = SOF_TIMESTAMPING_RX_SOFTWARE |