This patch adds support for the PTP clock found on the DP83640. Only the
basic clock operations have been implemented.

Signed-off-by: Richard Cochran <richard.coch...@omicron.at>
---
 drivers/net/phy/Kconfig   |   11 +++
 drivers/net/phy/dp83640.c |  158 ++++++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 168 insertions(+), 1 deletions(-)

diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig
index 430cab1..507c68a 100644
--- a/drivers/net/phy/Kconfig
+++ b/drivers/net/phy/Kconfig
@@ -79,9 +79,20 @@ config NATIONAL_PHY
 
 config DP83640_PHY
        tristate "Driver for the National Semiconductor DP83640 PHYTER"
+       depends on PTP_1588_CLOCK
+       depends on NETWORK_PHY_TIMESTAMPING
        ---help---
          Supports the DP83640 PHYTER with IEEE 1588 features.
 
+         This driver adds support for using the DP83640 as a PTP
+         clock. This clock is only useful if your PTP programs are
+         getting hardware time stamps on the PTP Ethernet packets
+         using the SO_TIMESTAMPING API.
+
+         In order for this to work, your MAC driver must also
+         implement the the skb_tx_timetamp() and skb_rx_timetamp()
+         functions.
+
 config STE10XP
        depends on PHYLIB
        tristate "Driver for STMicroelectronics STe10Xp PHYs"
diff --git a/drivers/net/phy/dp83640.c b/drivers/net/phy/dp83640.c
index a3217ea..21eadc3 100644
--- a/drivers/net/phy/dp83640.c
+++ b/drivers/net/phy/dp83640.c
@@ -26,6 +26,7 @@
 #include <linux/netdevice.h>
 #include <linux/phy.h>
 #include <linux/ptp_classify.h>
+#include <linux/ptp_clock_kernel.h>
 
 #include "dp83640_reg.h"
 
@@ -45,10 +46,13 @@ struct rxts {
 };
 
 struct dp83640_private {
+       struct phy_device *phydev;
        int hwts_tx_en;
        int hwts_rx_en;
        int layer;
        int version;
+       /* protects PTP_TDR register from concurrent access */
+       spinlock_t ptp_tdr_lock;
        /* protects extended registers from concurrent access */
        spinlock_t extreg_lock;
        int page;
@@ -60,6 +64,9 @@ struct dp83640_private {
 
 /* globals */
 
+static struct ptp_clock *dp83640_clock;
+DEFINE_SPINLOCK(clock_lock); /* protects the one and only dp83640_clock */
+
 static struct sock_filter ptp_filter[] = {
        PTP_FILTER
 };
@@ -99,6 +106,129 @@ static void ext_write(struct phy_device *phydev, int page, 
u32 regnum, u16 val)
        spin_unlock(&dp83640->extreg_lock);
 }
 
+static int tdr_write(struct dp83640_private *dp83640,
+                    struct timespec *ts, u16 cmd)
+{
+       struct phy_device *phydev = dp83640->phydev;
+
+       spin_lock(&dp83640->ptp_tdr_lock);
+
+       ext_write(phydev, PAGE4, PTP_TDR, ts->tv_nsec & 0xffff);/* ns[15:0] */
+       ext_write(phydev, PAGE4, PTP_TDR, ts->tv_nsec >> 16);   /* ns[31:16] */
+       ext_write(phydev, PAGE4, PTP_TDR, ts->tv_sec & 0xffff); /* sec[15:0] */
+       ext_write(phydev, PAGE4, PTP_TDR, ts->tv_sec >> 16);    /* sec[31:16] */
+
+       ext_write(phydev, PAGE4, PTP_CTL, cmd);
+
+       spin_unlock(&dp83640->ptp_tdr_lock);
+
+       return 0;
+}
+
+/* ptp clock methods */
+
+static int ptp_dp83640_adjfreq(void *priv, s32 ppb)
+{
+       struct dp83640_private *dp83640 = priv;
+       struct phy_device *phydev = dp83640->phydev;
+       u64 rate;
+       int neg_adj = 0;
+       u16 hi, lo;
+
+       if (!ppb)
+               return 0;
+
+       if (ppb < 0) {
+               neg_adj = 1;
+               ppb = -ppb;
+       }
+       rate = ppb;
+       rate <<= 26;
+       rate = div_u64(rate, 1953125);
+
+       hi = (rate >> 16) & PTP_RATE_HI_MASK;
+       if (neg_adj)
+               hi |= PTP_RATE_DIR;
+
+       lo = rate & 0xffff;
+
+       ext_write(phydev, PAGE4, PTP_RATEH, hi);
+       ext_write(phydev, PAGE4, PTP_RATEL, lo);
+
+       return 0;
+}
+
+static int ptp_dp83640_adjtime(void *priv, struct timespec *ts)
+{
+       return tdr_write(priv, ts, PTP_STEP_CLK);
+}
+
+static int ptp_dp83640_gettime(void *priv, struct timespec *ts)
+{
+       struct dp83640_private *dp83640 = priv;
+       struct phy_device *phydev = dp83640->phydev;
+       unsigned int val[4];
+
+       spin_lock(&dp83640->ptp_tdr_lock);
+
+       ext_write(phydev, PAGE4, PTP_CTL, PTP_RD_CLK);
+
+       val[0] = ext_read(phydev, PAGE4, PTP_TDR); /* ns[15:0] */
+       val[1] = ext_read(phydev, PAGE4, PTP_TDR); /* ns[31:16] */
+       val[2] = ext_read(phydev, PAGE4, PTP_TDR); /* sec[15:0] */
+       val[3] = ext_read(phydev, PAGE4, PTP_TDR); /* sec[31:16] */
+
+       spin_unlock(&dp83640->ptp_tdr_lock);
+
+       ts->tv_nsec = val[0] | (val[1] << 16);
+       ts->tv_sec  = val[2] | (val[3] << 16);
+
+       return 0;
+}
+
+static int ptp_dp83640_settime(void *priv, struct timespec *ts)
+{
+       return tdr_write(priv, ts, PTP_LOAD_CLK);
+}
+
+static int ptp_dp83640_gettimer(void *priv, int index, struct itimerspec *ts)
+{
+       /* We do not (yet) offer any ancillary features. */
+       return -EOPNOTSUPP;
+}
+
+static int ptp_dp83640_settimer(void *p, int i, int abs, struct itimerspec *ts)
+{
+       /* We do not (yet) offer any ancillary features. */
+       return -EOPNOTSUPP;
+}
+
+static int ptp_dp83640_enable(void *priv, struct ptp_clock_request *rq, int on)
+{
+       /* We do not (yet) offer any ancillary features. */
+       return -EOPNOTSUPP;
+}
+
+static struct ptp_clock_info ptp_dp83640_caps = {
+       .owner          = THIS_MODULE,
+       .name           = "dp83640 timer",
+       .max_adj        = 1953124,
+       .n_alarm        = 0,
+       .n_ext_ts       = 0,
+       .n_per_out      = 0,
+       .pps            = 0,
+       .priv           = NULL,
+       .adjfreq        = ptp_dp83640_adjfreq,
+       .adjtime        = ptp_dp83640_adjtime,
+       .gettime        = ptp_dp83640_gettime,
+       .settime        = ptp_dp83640_settime,
+       .gettimer       = ptp_dp83640_gettimer,
+       .settimer       = ptp_dp83640_settimer,
+       .enable         = ptp_dp83640_enable,
+};
+
+/* time stamping methods */
+
 static int expired(struct rxts *rxts)
 {
        return time_after(jiffies, rxts->tmo);
@@ -144,6 +274,7 @@ static int match(struct sk_buff *skb, unsigned int type, 
struct rxts *rxts)
 static int dp83640_probe(struct phy_device *phydev)
 {
        struct dp83640_private *dp83640;
+       unsigned long flags;
        int i;
 
        if (sk_chk_filter(ptp_filter, ARRAY_SIZE(ptp_filter))) {
@@ -155,8 +286,9 @@ static int dp83640_probe(struct phy_device *phydev)
        if (!dp83640)
                return -ENOMEM;
 
+       dp83640->phydev = phydev;
+       spin_lock_init(&dp83640->ptp_tdr_lock);
        spin_lock_init(&dp83640->extreg_lock);
-
        INIT_LIST_HEAD(&dp83640->rxts);
        INIT_LIST_HEAD(&dp83640->pool);
 
@@ -165,12 +297,36 @@ static int dp83640_probe(struct phy_device *phydev)
 
        phydev->priv = dp83640;
 
+       spin_lock_irqsave(&clock_lock, flags);
+
+       if (!dp83640_clock) {
+               ptp_dp83640_caps.priv = dp83640;
+               dp83640_clock = ptp_clock_register(&ptp_dp83640_caps);
+               if (IS_ERR(dp83640_clock)) {
+                       spin_unlock_irqrestore(&clock_lock, flags);
+                       kfree(dp83640);
+                       return PTR_ERR(dp83640_clock);
+               }
+       }
+       spin_unlock_irqrestore(&clock_lock, flags);
+
        return 0;
 }
 
 static void dp83640_remove(struct phy_device *phydev)
 {
        struct dp83640_private *dp83640 = phydev->priv;
+       unsigned long flags;
+
+       spin_lock_irqsave(&clock_lock, flags);
+
+       if (ptp_dp83640_caps.priv == dp83640) {
+               ptp_clock_unregister(dp83640_clock);
+               dp83640_clock = NULL;
+               ptp_dp83640_caps.priv = NULL;
+       }
+       spin_unlock_irqrestore(&clock_lock, flags);
+
        kfree(dp83640);
 }
 
-- 
1.6.3.3

_______________________________________________
Linuxppc-dev mailing list
Linuxppc-dev@lists.ozlabs.org
https://lists.ozlabs.org/listinfo/linuxppc-dev

Reply via email to