[PATCH 12/12] ptp: Added a clock driver for the National Semiconductor PHYTER.
Grant Likely
grant.likely at secretlab.ca
Tue Jun 15 14:49:13 EDT 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 linux-arm-kernel
mailing list