From 92e6457d4cf68ef69bc4f98330c93e198df06a43 Mon Sep 17 00:00:00 2001 From: Nithin Sujir Date: Mon, 29 Jul 2013 13:58:38 -0700 Subject: tg3: Enable support for timesync gpio output The PTP_CAPABLE tg3 devices have a gpio output that is toggled when the free running counter matches a watchdog value. This patch adds support to set the watchdog and enable this feature. Since the output is controlled via bits in the EAV_REF_CLCK_CTL register, we have to read-modify-write it when we stop/resume. Cc: Richard Cochran Signed-off-by: Nithin Nayak Sujir Signed-off-by: Michael Chan Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/tg3.c | 61 +++++++++++++++++++++++++++++++++++-- 1 file changed, 58 insertions(+), 3 deletions(-) (limited to 'drivers/net/ethernet/broadcom/tg3.c') diff --git a/drivers/net/ethernet/broadcom/tg3.c b/drivers/net/ethernet/broadcom/tg3.c index 9351707..f077c8f 100644 --- a/drivers/net/ethernet/broadcom/tg3.c +++ b/drivers/net/ethernet/broadcom/tg3.c @@ -6093,10 +6093,12 @@ static u64 tg3_refclk_read(struct tg3 *tp) /* tp->lock must be held */ static void tg3_refclk_write(struct tg3 *tp, u64 newval) { - tw32(TG3_EAV_REF_CLCK_CTL, TG3_EAV_REF_CLCK_CTL_STOP); + u32 clock_ctl = tr32(TG3_EAV_REF_CLCK_CTL); + + tw32(TG3_EAV_REF_CLCK_CTL, clock_ctl | TG3_EAV_REF_CLCK_CTL_STOP); tw32(TG3_EAV_REF_CLCK_LSB, newval & 0xffffffff); tw32(TG3_EAV_REF_CLCK_MSB, newval >> 32); - tw32_f(TG3_EAV_REF_CLCK_CTL, TG3_EAV_REF_CLCK_CTL_RESUME); + tw32_f(TG3_EAV_REF_CLCK_CTL, clock_ctl | TG3_EAV_REF_CLCK_CTL_RESUME); } static inline void tg3_full_lock(struct tg3 *tp, int irq_sync); @@ -6212,6 +6214,59 @@ static int tg3_ptp_settime(struct ptp_clock_info *ptp, static int tg3_ptp_enable(struct ptp_clock_info *ptp, struct ptp_clock_request *rq, int on) { + struct tg3 *tp = container_of(ptp, struct tg3, ptp_info); + u32 clock_ctl; + int rval = 0; + + switch (rq->type) { + case PTP_CLK_REQ_PEROUT: + if (rq->perout.index != 0) + return -EINVAL; + + tg3_full_lock(tp, 0); + clock_ctl = tr32(TG3_EAV_REF_CLCK_CTL); + clock_ctl &= ~TG3_EAV_CTL_TSYNC_GPIO_MASK; + + if (on) { + u64 nsec; + + nsec = rq->perout.start.sec * 1000000000ULL + + rq->perout.start.nsec; + + if (rq->perout.period.sec || rq->perout.period.nsec) { + netdev_warn(tp->dev, + "Device supports only a one-shot timesync output, period must be 0\n"); + rval = -EINVAL; + goto err_out; + } + + if (nsec & (1ULL << 63)) { + netdev_warn(tp->dev, + "Start value (nsec) is over limit. Maximum size of start is only 63 bits\n"); + rval = -EINVAL; + goto err_out; + } + + tw32(TG3_EAV_WATCHDOG0_LSB, (nsec & 0xffffffff)); + tw32(TG3_EAV_WATCHDOG0_MSB, + TG3_EAV_WATCHDOG0_EN | + ((nsec >> 32) & TG3_EAV_WATCHDOG_MSB_MASK)); + + tw32(TG3_EAV_REF_CLCK_CTL, + clock_ctl | TG3_EAV_CTL_TSYNC_WDOG0); + } else { + tw32(TG3_EAV_WATCHDOG0_MSB, 0); + tw32(TG3_EAV_REF_CLCK_CTL, clock_ctl); + } + +err_out: + tg3_full_unlock(tp); + return rval; + + default: + break; + } + return -EOPNOTSUPP; } @@ -6221,7 +6276,7 @@ static const struct ptp_clock_info tg3_ptp_caps = { .max_adj = 250000000, .n_alarm = 0, .n_ext_ts = 0, - .n_per_out = 0, + .n_per_out = 1, .pps = 0, .adjfreq = tg3_ptp_adjfreq, .adjtime = tg3_ptp_adjtime, -- cgit v1.1