On Tue, Jun 15, 2010 at 10:10 AM, Richard Cochran <richardcoch...@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.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
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-ker...@lists.infradead.org > http://lists.infradead.org/mailman/listinfo/linux-arm-kernel > -- Grant Likely, B.Sc., P.Eng. Secret Lab Technologies Ltd. _______________________________________________ Linuxppc-dev mailing list Linuxppc-dev@lists.ozlabs.org https://lists.ozlabs.org/listinfo/linuxppc-dev