@@ -31,6 +31,7 @@
#include <linux/phy_led_triggers.h>
#include <linux/pse-pd/pse.h>
#include <linux/property.h>
+#include <linux/ptp_clock_kernel.h>
#include <linux/rtnetlink.h>
#include <linux/sfp.h>
#include <linux/skbuff.h>
@@ -1936,6 +1937,12 @@ void phy_detach(struct phy_device *phydev)
phy_suspend(phydev);
if (dev) {
+ /* Disable timestamp if selected */
+ if (ptp_clock_phydev(dev->hwtstamp.ptp) == phydev) {
+ dev->hwtstamp.ptp = NULL;
+ dev->hwtstamp.qualifier = HWTSTAMP_PROVIDER_QUALIFIER_PRECISE;
+ }
+
phydev->attached_dev->phydev = NULL;
phydev->attached_dev = NULL;
}
@@ -19,6 +19,11 @@ enum hwtstamp_source {
HWTSTAMP_SOURCE_PHYLIB,
};
+struct hwtstamp_provider {
+ struct ptp_clock *ptp;
+ enum hwtstamp_provider_qualifier qualifier;
+};
+
/**
* struct kernel_hwtstamp_config - Kernel copy of struct hwtstamp_config
*
@@ -43,6 +48,7 @@ struct kernel_hwtstamp_config {
struct ifreq *ifr;
bool copied_to_user;
enum hwtstamp_source source;
+ enum hwtstamp_provider_qualifier qualifier;
};
static inline void hwtstamp_config_to_kernel(struct kernel_hwtstamp_config *kernel_cfg,
@@ -47,6 +47,7 @@
#include <uapi/linux/if_bonding.h>
#include <uapi/linux/pkt_cls.h>
#include <uapi/linux/netdev.h>
+#include <linux/net_tstamp.h>
#include <linux/hashtable.h>
#include <linux/rbtree.h>
#include <net/net_trackers.h>
@@ -2107,6 +2108,8 @@ enum netdev_reg_state {
* @dpll_pin: Pointer to the SyncE source pin of a DPLL subsystem,
* where the clock is recovered.
*
+ * @hwtstamp: Tracks which PTP performs hardware packet time stamping.
+ *
* FIXME: cleanup struct net_device such that network protocol info
* moves out.
*/
@@ -2478,6 +2481,8 @@ struct net_device {
/** @page_pools: page pools created for this netdevice */
struct hlist_head page_pools;
#endif
+
+ struct hwtstamp_provider hwtstamp;
};
#define to_net_dev(d) container_of(d, struct net_device, dev)
@@ -13,6 +13,17 @@
#include <linux/types.h>
#include <linux/socket.h> /* for SO_TIMESTAMPING */
+/*
+ * Possible type of htstamp provider. Mainly "precise" the default one
+ * is for IEEE 1588 quality and "approx" is for NICs DMA point.
+ */
+enum hwtstamp_provider_qualifier {
+ HWTSTAMP_PROVIDER_QUALIFIER_PRECISE,
+ HWTSTAMP_PROVIDER_QUALIFIER_APPROX,
+
+ HWTSTAMP_PROVIDER_QUALIFIER_CNT,
+};
+
/* SO_TIMESTAMPING flags */
enum {
SOF_TIMESTAMPING_TX_HARDWARE = (1<<0),
@@ -6,6 +6,7 @@
#include <linux/rtnetlink.h>
#include <linux/net_tstamp.h>
#include <linux/phylib_stubs.h>
+#include <linux/ptp_clock_kernel.h>
#include <linux/wireless.h>
#include <linux/if_bridge.h>
#include <net/dsa_stubs.h>
@@ -270,6 +271,20 @@ static int dev_eth_ioctl(struct net_device *dev,
int dev_get_hwtstamp_phylib(struct net_device *dev,
struct kernel_hwtstamp_config *cfg)
{
+ cfg->qualifier = dev->hwtstamp.qualifier;
+
+ if (dev->hwtstamp.ptp) {
+ struct ptp_clock *ptp = dev->hwtstamp.ptp;
+
+ if (ptp_clock_from_phylib(ptp))
+ return phy_hwtstamp_get(ptp_clock_phydev(ptp), cfg);
+
+ if (ptp_clock_from_netdev(ptp))
+ return dev->netdev_ops->ndo_hwtstamp_get(dev, cfg);
+
+ return -EOPNOTSUPP;
+ }
+
if (phy_is_default_hwtstamp(dev->phydev))
return phy_hwtstamp_get(dev->phydev, cfg);
@@ -327,11 +342,31 @@ 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_is_default_hwtstamp(dev->phydev);
struct kernel_hwtstamp_config old_cfg = {};
+ struct phy_device *phydev;
bool changed = false;
+ bool phy_ts;
int err;
+ cfg->qualifier = dev->hwtstamp.qualifier;
+
+ if (dev->hwtstamp.ptp) {
+ struct ptp_clock *ptp = dev->hwtstamp.ptp;
+
+ if (ptp_clock_from_phylib(ptp)) {
+ phy_ts = true;
+ phydev = ptp_clock_phydev(ptp);
+ } else if (ptp_clock_from_netdev(ptp)) {
+ phy_ts = false;
+ } else {
+ return -EOPNOTSUPP;
+ }
+ } else {
+ phy_ts = phy_is_default_hwtstamp(dev->phydev);
+ if (phy_ts)
+ phydev = dev->phydev;
+ }
+
cfg->source = phy_ts ? HWTSTAMP_SOURCE_PHYLIB : HWTSTAMP_SOURCE_NETDEV;
if (phy_ts && (dev->priv_flags & IFF_SEE_ALL_HWTSTAMP_REQUESTS)) {
@@ -353,7 +388,7 @@ int dev_set_hwtstamp_phylib(struct net_device *dev,
changed = kernel_hwtstamp_config_changed(&old_cfg, cfg);
if (phy_ts) {
- err = phy_hwtstamp_set(dev->phydev, cfg, extack);
+ err = phy_hwtstamp_set(phydev, cfg, extack);
if (err) {
if (changed)
ops->ndo_hwtstamp_set(dev, &old_cfg, NULL);
@@ -9,6 +9,7 @@
#include <linux/ptp_classify.h>
#include <linux/skbuff.h>
#include <linux/export.h>
+#include <linux/ptp_clock_kernel.h>
static unsigned int classify(const struct sk_buff *skb)
{
@@ -22,18 +23,31 @@ static unsigned int classify(const struct sk_buff *skb)
void skb_clone_tx_timestamp(struct sk_buff *skb)
{
struct mii_timestamper *mii_ts;
+ struct phy_device *phydev;
struct sk_buff *clone;
unsigned int type;
- if (!skb->sk || !skb->dev ||
- !phy_is_default_hwtstamp(skb->dev->phydev))
+ if (!skb->sk || !skb->dev)
return;
+ if (skb->dev->hwtstamp.ptp) {
+ if (!ptp_clock_from_phylib(skb->dev->hwtstamp.ptp))
+ return;
+
+ phydev = ptp_clock_phydev(skb->dev->hwtstamp.ptp);
+
+ } else {
+ if (!phy_is_default_hwtstamp(phydev))
+ return;
+
+ phydev = skb->dev->phydev;
+ }
+
type = classify(skb);
if (type == PTP_CLASS_NONE)
return;
- mii_ts = skb->dev->phydev->mii_ts;
+ mii_ts = phydev->mii_ts;
if (likely(mii_ts->txtstamp)) {
clone = skb_clone_sk(skb);
if (!clone)
@@ -46,11 +60,24 @@ EXPORT_SYMBOL_GPL(skb_clone_tx_timestamp);
bool skb_defer_rx_timestamp(struct sk_buff *skb)
{
struct mii_timestamper *mii_ts;
+ struct phy_device *phydev;
unsigned int type;
- if (!skb->dev || !phy_is_default_hwtstamp(skb->dev->phydev))
+ if (!skb->dev)
return false;
+ if (skb->dev->hwtstamp.ptp) {
+ if (!ptp_clock_from_phylib(skb->dev->hwtstamp.ptp))
+ return false;
+
+ phydev = ptp_clock_phydev(skb->dev->hwtstamp.ptp);
+ } else {
+ if (!phy_is_default_hwtstamp(phydev))
+ return false;
+
+ phydev = skb->dev->phydev;
+ }
+
if (skb_headroom(skb) < ETH_HLEN)
return false;
@@ -63,7 +90,7 @@ bool skb_defer_rx_timestamp(struct sk_buff *skb)
if (type == PTP_CLASS_NONE)
return false;
- mii_ts = skb->dev->phydev->mii_ts;
+ mii_ts = phydev->mii_ts;
if (likely(mii_ts->rxtstamp))
return mii_ts->rxtstamp(mii_ts, skb, type);