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

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
...@@ -98,6 +98,7 @@ struct dp83640_private { ...@@ -98,6 +98,7 @@ struct dp83640_private {
struct list_head list; struct list_head list;
struct dp83640_clock *clock; struct dp83640_clock *clock;
struct phy_device *phydev; struct phy_device *phydev;
struct mii_timestamper mii_ts;
struct delayed_work ts_work; struct delayed_work ts_work;
int hwts_tx_en; int hwts_tx_en;
int hwts_rx_en; int hwts_rx_en;
...@@ -1229,9 +1230,10 @@ static int dp83640_config_intr(struct phy_device *phydev) ...@@ -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; struct hwtstamp_config cfg;
u16 txcfg0, rxcfg0; u16 txcfg0, rxcfg0;
...@@ -1307,8 +1309,8 @@ static int dp83640_hwtstamp(struct phy_device *phydev, struct ifreq *ifr) ...@@ -1307,8 +1309,8 @@ static int dp83640_hwtstamp(struct phy_device *phydev, struct ifreq *ifr)
mutex_lock(&dp83640->clock->extreg_lock); mutex_lock(&dp83640->clock->extreg_lock);
ext_write(0, phydev, PAGE5, PTP_TXCFG0, txcfg0); ext_write(0, dp83640->phydev, PAGE5, PTP_TXCFG0, txcfg0);
ext_write(0, phydev, PAGE5, PTP_RXCFG0, rxcfg0); ext_write(0, dp83640->phydev, PAGE5, PTP_RXCFG0, rxcfg0);
mutex_unlock(&dp83640->clock->extreg_lock); mutex_unlock(&dp83640->clock->extreg_lock);
...@@ -1338,10 +1340,11 @@ static void rx_timestamp_work(struct work_struct *work) ...@@ -1338,10 +1340,11 @@ static void rx_timestamp_work(struct work_struct *work)
schedule_delayed_work(&dp83640->ts_work, SKB_TIMESTAMP_TIMEOUT); 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 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 dp83640_skb_info *skb_info = (struct dp83640_skb_info *)skb->cb;
struct list_head *this, *next; struct list_head *this, *next;
struct rxts *rxts; struct rxts *rxts;
...@@ -1387,11 +1390,12 @@ static bool dp83640_rxtstamp(struct phy_device *phydev, ...@@ -1387,11 +1390,12 @@ static bool dp83640_rxtstamp(struct phy_device *phydev,
return true; 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 sk_buff *skb, int type)
{ {
struct dp83640_skb_info *skb_info = (struct dp83640_skb_info *)skb->cb; 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) { switch (dp83640->hwts_tx_en) {
...@@ -1414,9 +1418,11 @@ static void dp83640_txtstamp(struct phy_device *phydev, ...@@ -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 = info->so_timestamping =
SOF_TIMESTAMPING_TX_HARDWARE | SOF_TIMESTAMPING_TX_HARDWARE |
...@@ -1454,13 +1460,18 @@ static int dp83640_probe(struct phy_device *phydev) ...@@ -1454,13 +1460,18 @@ static int dp83640_probe(struct phy_device *phydev)
goto no_memory; goto no_memory;
dp83640->phydev = phydev; 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->rxts);
INIT_LIST_HEAD(&dp83640->rxpool); INIT_LIST_HEAD(&dp83640->rxpool);
for (i = 0; i < MAX_RXTS; i++) for (i = 0; i < MAX_RXTS; i++)
list_add(&dp83640->rx_pool_data[i].list, &dp83640->rxpool); list_add(&dp83640->rx_pool_data[i].list, &dp83640->rxpool);
phydev->mii_ts = &dp83640->mii_ts;
phydev->priv = dp83640; phydev->priv = dp83640;
spin_lock_init(&dp83640->rx_lock); spin_lock_init(&dp83640->rx_lock);
...@@ -1501,6 +1512,8 @@ static void dp83640_remove(struct phy_device *phydev) ...@@ -1501,6 +1512,8 @@ static void dp83640_remove(struct phy_device *phydev)
if (phydev->mdio.addr == BROADCAST_ADDR) if (phydev->mdio.addr == BROADCAST_ADDR)
return; return;
phydev->mii_ts = NULL;
enable_status_frames(phydev, false); enable_status_frames(phydev, false);
cancel_delayed_work_sync(&dp83640->ts_work); cancel_delayed_work_sync(&dp83640->ts_work);
...@@ -1537,10 +1550,6 @@ static struct phy_driver dp83640_driver = { ...@@ -1537,10 +1550,6 @@ static struct phy_driver dp83640_driver = {
.config_init = dp83640_config_init, .config_init = dp83640_config_init,
.ack_interrupt = dp83640_ack_interrupt, .ack_interrupt = dp83640_ack_interrupt,
.config_intr = dp83640_config_intr, .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) static int __init dp83640_init(void)
......
...@@ -422,8 +422,8 @@ int phy_mii_ioctl(struct phy_device *phydev, struct ifreq *ifr, int cmd) ...@@ -422,8 +422,8 @@ int phy_mii_ioctl(struct phy_device *phydev, struct ifreq *ifr, int cmd)
return 0; return 0;
case SIOCSHWTSTAMP: case SIOCSHWTSTAMP:
if (phydev->drv && phydev->drv->hwtstamp) if (phydev->mii_ts && phydev->mii_ts->hwtstamp)
return phydev->drv->hwtstamp(phydev, ifr); return phydev->mii_ts->hwtstamp(phydev->mii_ts, ifr);
/* fall through */ /* fall through */
default: default:
......
...@@ -919,6 +919,8 @@ static void phy_link_change(struct phy_device *phydev, bool up, bool do_carrier) ...@@ -919,6 +919,8 @@ static void phy_link_change(struct phy_device *phydev, bool up, bool do_carrier)
netif_carrier_off(netdev); netif_carrier_off(netdev);
} }
phydev->adjust_link(netdev); phydev->adjust_link(netdev);
if (phydev->mii_ts && phydev->mii_ts->link_state)
phydev->mii_ts->link_state(phydev->mii_ts, phydev);
} }
/** /**
......
/* 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
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
#include <linux/linkmode.h> #include <linux/linkmode.h>
#include <linux/mdio.h> #include <linux/mdio.h>
#include <linux/mii.h> #include <linux/mii.h>
#include <linux/mii_timestamper.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/timer.h> #include <linux/timer.h>
#include <linux/workqueue.h> #include <linux/workqueue.h>
...@@ -441,6 +442,7 @@ struct phy_device { ...@@ -441,6 +442,7 @@ struct phy_device {
struct sfp_bus *sfp_bus; struct sfp_bus *sfp_bus;
struct phylink *phylink; struct phylink *phylink;
struct net_device *attached_dev; struct net_device *attached_dev;
struct mii_timestamper *mii_ts;
u8 mdix; u8 mdix;
u8 mdix_ctrl; u8 mdix_ctrl;
...@@ -546,29 +548,6 @@ struct phy_driver { ...@@ -546,29 +548,6 @@ struct phy_driver {
*/ */
int (*match_phy_device)(struct phy_device *phydev); 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 /* 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 * enable Wake on LAN, so set_wol is provided to be called in the
* ethernet driver's set_wol function. */ * ethernet driver's set_wol function. */
...@@ -942,7 +921,7 @@ static inline bool phy_polling_mode(struct phy_device *phydev) ...@@ -942,7 +921,7 @@ static inline bool phy_polling_mode(struct phy_device *phydev)
*/ */
static inline bool phy_has_hwtstamp(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) ...@@ -951,7 +930,7 @@ static inline bool phy_has_hwtstamp(struct phy_device *phydev)
*/ */
static inline bool phy_has_rxtstamp(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) ...@@ -961,7 +940,7 @@ static inline bool phy_has_rxtstamp(struct phy_device *phydev)
*/ */
static inline bool phy_has_tsinfo(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) ...@@ -970,30 +949,30 @@ static inline bool phy_has_tsinfo(struct phy_device *phydev)
*/ */
static inline bool phy_has_txtstamp(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) 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, static inline bool phy_rxtstamp(struct phy_device *phydev, struct sk_buff *skb,
int type) 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, static inline int phy_ts_info(struct phy_device *phydev,
struct ethtool_ts_info *tsinfo) 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, static inline void phy_txtstamp(struct phy_device *phydev, struct sk_buff *skb,
int type) int type)
{ {
phydev->drv->txtstamp(phydev, skb, type); phydev->mii_ts->txtstamp(phydev->mii_ts, skb, type);
} }
/** /**
......
...@@ -13,7 +13,7 @@ ...@@ -13,7 +13,7 @@
static unsigned int classify(const struct sk_buff *skb) static unsigned int classify(const struct sk_buff *skb)
{ {
if (likely(skb->dev && skb->dev->phydev && if (likely(skb->dev && skb->dev->phydev &&
skb->dev->phydev->drv)) skb->dev->phydev->mii_ts))
return ptp_classify_raw(skb); return ptp_classify_raw(skb);
else else
return PTP_CLASS_NONE; return PTP_CLASS_NONE;
...@@ -21,7 +21,7 @@ static unsigned int classify(const struct sk_buff *skb) ...@@ -21,7 +21,7 @@ static unsigned int classify(const struct sk_buff *skb)
void skb_clone_tx_timestamp(struct sk_buff *skb) void skb_clone_tx_timestamp(struct sk_buff *skb)
{ {
struct phy_device *phydev; struct mii_timestamper *mii_ts;
struct sk_buff *clone; struct sk_buff *clone;
unsigned int type; unsigned int type;
...@@ -32,22 +32,22 @@ void skb_clone_tx_timestamp(struct sk_buff *skb) ...@@ -32,22 +32,22 @@ void skb_clone_tx_timestamp(struct sk_buff *skb)
if (type == PTP_CLASS_NONE) if (type == PTP_CLASS_NONE)
return; return;
phydev = skb->dev->phydev; mii_ts = skb->dev->phydev->mii_ts;
if (likely(phydev->drv->txtstamp)) { if (likely(mii_ts->txtstamp)) {
clone = skb_clone_sk(skb); clone = skb_clone_sk(skb);
if (!clone) if (!clone)
return; return;
phydev->drv->txtstamp(phydev, clone, type); mii_ts->txtstamp(mii_ts, clone, type);
} }
} }
EXPORT_SYMBOL_GPL(skb_clone_tx_timestamp); EXPORT_SYMBOL_GPL(skb_clone_tx_timestamp);
bool skb_defer_rx_timestamp(struct sk_buff *skb) bool skb_defer_rx_timestamp(struct sk_buff *skb)
{ {
struct phy_device *phydev; struct mii_timestamper *mii_ts;
unsigned int type; unsigned int type;
if (!skb->dev || !skb->dev->phydev || !skb->dev->phydev->drv) if (!skb->dev || !skb->dev->phydev || !skb->dev->phydev->mii_ts)
return false; return false;
if (skb_headroom(skb) < ETH_HLEN) if (skb_headroom(skb) < ETH_HLEN)
...@@ -62,9 +62,9 @@ bool skb_defer_rx_timestamp(struct sk_buff *skb) ...@@ -62,9 +62,9 @@ bool skb_defer_rx_timestamp(struct sk_buff *skb)
if (type == PTP_CLASS_NONE) if (type == PTP_CLASS_NONE)
return false; return false;
phydev = skb->dev->phydev; mii_ts = skb->dev->phydev->mii_ts;
if (likely(phydev->drv->rxtstamp)) if (likely(mii_ts->rxtstamp))
return phydev->drv->rxtstamp(phydev, skb, type); return mii_ts->rxtstamp(mii_ts, skb, type);
return false; return false;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment