tg3: Enable support for timesync gpio output
authorNithin Sujir <nsujir@broadcom.com>
Mon, 29 Jul 2013 20:58:38 +0000 (13:58 -0700)
committerDavid S. Miller <davem@davemloft.net>
Tue, 30 Jul 2013 06:38:52 +0000 (23:38 -0700)
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 <richardcochran@gmail.com>
Signed-off-by: Nithin Nayak Sujir <nsujir@broadcom.com>
Signed-off-by: Michael Chan <mchan@broadcom.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/broadcom/tg3.c
drivers/net/ethernet/broadcom/tg3.h

index 93517075778651e1cd2574874ae32b391ea7de4c..f077c8f5baa18f4c235567edef2b4736c931f3be 100644 (file)
@@ -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,
index cd63d1189aae9fdfbcceb5615d76b8d6d74704d2..2e0f3d34ea5d556f0429d016c008de2be95e9a0b 100644 (file)
 #define TG3_EAV_REF_CLCK_CTL           0x00006908
 #define  TG3_EAV_REF_CLCK_CTL_STOP      0x00000002
 #define  TG3_EAV_REF_CLCK_CTL_RESUME    0x00000004
+#define  TG3_EAV_CTL_TSYNC_GPIO_MASK    (0x3 << 16)
+#define  TG3_EAV_CTL_TSYNC_WDOG0        (1 << 17)
+
+#define TG3_EAV_WATCHDOG0_LSB          0x00006918
+#define TG3_EAV_WATCHDOG0_MSB          0x0000691c
+#define  TG3_EAV_WATCHDOG0_EN           (1 << 31)
+#define  TG3_EAV_WATCHDOG_MSB_MASK     0x7fffffff
+
 #define TG3_EAV_REF_CLK_CORRECT_CTL    0x00006928
 #define  TG3_EAV_REF_CLK_CORRECT_EN     (1 << 31)
 #define  TG3_EAV_REF_CLK_CORRECT_NEG    (1 << 30)
 
 #define TG3_EAV_REF_CLK_CORRECT_MASK   0xffffff
-/* 0x690c --> 0x7000 unused */
+
+/* 0x692c --> 0x7000 unused */
 
 /* NVRAM Control registers */
 #define NVRAM_CMD                      0x00007000