[PATCH 12/12] ptp: Added a clock driver for the National Semiconductor PHYTER.

Grant Likely grant.likely at secretlab.ca
Wed Jun 16 04:49:13 EST 2010


On Tue, Jun 15, 2010 at 10:10 AM, Richard Cochran
<richardcochran at gmail.com> wrote:
> 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.cochran at 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

Won't this break things for existing DP83640 users?

>        ---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 */

Why only one?  Is it not possible to have 2 of these PHYs in a system?

> +
>  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,

ditto here, can leave the 0s and nulls out.

> +       .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
>
>
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
>



-- 
Grant Likely, B.Sc., P.Eng.
Secret Lab Technologies Ltd.


More information about the devicetree-discuss mailing list