IXGBE_WRITE_FLUSH(hw);
 }
 
+/**
+ * ixgbe_ptp_setup_sdp_X550
+ * @adapter: private adapter structure
+ *
+ * Enable or disable a clock output signal on SDP 0 for X550 hardware.
+ *
+ * Use the target time feature to align the output signal on the next full
+ * second.
+ *
+ * This works by using the cycle counter shift and mult values in reverse, and
+ * assumes that the values we're shifting will not overflow.
+ */
+static void ixgbe_ptp_setup_sdp_X550(struct ixgbe_adapter *adapter)
+{
+       u32 esdp, tsauxc, freqout, trgttiml, trgttimh, rem, tssdp;
+       struct cyclecounter *cc = &adapter->hw_cc;
+       struct ixgbe_hw *hw = &adapter->hw;
+       u64 ns = 0, clock_edge = 0;
+       struct timespec64 ts;
+       unsigned long flags;
+
+       /* disable the pin first */
+       IXGBE_WRITE_REG(hw, IXGBE_TSAUXC, 0x0);
+       IXGBE_WRITE_FLUSH(hw);
+
+       if (!(adapter->flags2 & IXGBE_FLAG2_PTP_PPS_ENABLED))
+               return;
+
+       esdp = IXGBE_READ_REG(hw, IXGBE_ESDP);
+
+       /* enable the SDP0 pin as output, and connected to the
+        * native function for Timesync (ClockOut)
+        */
+       esdp |= IXGBE_ESDP_SDP0_DIR |
+               IXGBE_ESDP_SDP0_NATIVE;
+
+       /* enable the Clock Out feature on SDP0, and use Target Time 0 to
+        * enable generation of interrupts on the clock change.
+        */
+#define IXGBE_TSAUXC_DIS_TS_CLEAR 0x40000000
+       tsauxc = (IXGBE_TSAUXC_EN_CLK | IXGBE_TSAUXC_ST0 |
+                 IXGBE_TSAUXC_EN_TT0 | IXGBE_TSAUXC_SDP0_INT |
+                 IXGBE_TSAUXC_DIS_TS_CLEAR);
+
+       tssdp = (IXGBE_TSSDP_TS_SDP0_EN |
+                IXGBE_TSSDP_TS_SDP0_CLK0);
+
+       /* Determine the clock time period to use. This assumes that the
+        * cycle counter shift is small enough to avoid overflowing a 32bit
+        * value.
+        */
+       freqout = div_u64(NS_PER_HALF_SEC << cc->shift,  cc->mult);
+
+       /* Read the current clock time, and save the cycle counter value */
+       spin_lock_irqsave(&adapter->tmreg_lock, flags);
+       ns = timecounter_read(&adapter->hw_tc);
+       clock_edge = adapter->hw_tc.cycle_last;
+       spin_unlock_irqrestore(&adapter->tmreg_lock, flags);
+
+       /* Figure out how far past the next second we are */
+       div_u64_rem(ns, NS_PER_SEC, &rem);
+
+       /* Figure out how many nanoseconds to add to round the clock edge up
+        * to the next full second
+        */
+       rem = (NS_PER_SEC - rem);
+
+       /* Adjust the clock edge to align with the next full second. This
+        * assumes that the cycle counter shift is small enough to avoid
+        * overflowing when shifting the remainder.
+        */
+       clock_edge += div_u64((rem << cc->shift), cc->mult);
+
+       /* X550 hardware stores the time in 32bits of 'billions of cycles' and
+        * 32bits of 'cycles'. There's no guarantee that cycles represents
+        * nanoseconds. However, we can use the math from a timespec64 to
+        * convert into the hardware representation.
+        *
+        * See ixgbe_ptp_read_X550() for more details.
+        */
+       ts = ns_to_timespec64(clock_edge);
+       trgttiml = (u32)ts.tv_nsec;
+       trgttimh = (u32)ts.tv_sec;
+
+       IXGBE_WRITE_REG(hw, IXGBE_FREQOUT0, freqout);
+       IXGBE_WRITE_REG(hw, IXGBE_TRGTTIML0, trgttiml);
+       IXGBE_WRITE_REG(hw, IXGBE_TRGTTIMH0, trgttimh);
+
+       IXGBE_WRITE_REG(hw, IXGBE_ESDP, esdp);
+       IXGBE_WRITE_REG(hw, IXGBE_TSSDP, tssdp);
+       IXGBE_WRITE_REG(hw, IXGBE_TSAUXC, tsauxc);
+
+       IXGBE_WRITE_FLUSH(hw);
+}
+
 /**
  * ixgbe_ptp_read_X550 - read cycle counter value
  * @cc: cyclecounter structure
                adapter->ptp_caps.n_alarm = 0;
                adapter->ptp_caps.n_ext_ts = 0;
                adapter->ptp_caps.n_per_out = 0;
-               adapter->ptp_caps.pps = 0;
+               adapter->ptp_caps.pps = 1;
                adapter->ptp_caps.adjfreq = ixgbe_ptp_adjfreq_X550;
                adapter->ptp_caps.adjtime = ixgbe_ptp_adjtime;
                adapter->ptp_caps.gettimex64 = ixgbe_ptp_gettimex;
                adapter->ptp_caps.settime64 = ixgbe_ptp_settime;
                adapter->ptp_caps.enable = ixgbe_ptp_feature_enable;
-               adapter->ptp_setup_sdp = NULL;
+               adapter->ptp_setup_sdp = ixgbe_ptp_setup_sdp_X550;
                break;
        default:
                adapter->ptp_clock = NULL;
 
 #define IXGBE_AUXSTMPL1  0x08C44 /* Auxiliary Time Stamp 1 register Low - RO */
 #define IXGBE_AUXSTMPH1  0x08C48 /* Auxiliary Time Stamp 1 register High - RO */
 #define IXGBE_TSIM       0x08C68 /* TimeSync Interrupt Mask Register - RW */
+#define IXGBE_TSSDP      0x0003C /* TimeSync SDP Configuration Register - RW */
 
 /* Diagnostic Registers */
 #define IXGBE_RDSTATCTL   0x02C20
 #define IXGBE_RXDCTL_RLPML_EN   0x00008000
 #define IXGBE_RXDCTL_VME        0x40000000  /* VLAN mode enable */
 
-#define IXGBE_TSAUXC_EN_CLK   0x00000004
-#define IXGBE_TSAUXC_SYNCLK   0x00000008
-#define IXGBE_TSAUXC_SDP0_INT 0x00000040
+#define IXGBE_TSAUXC_EN_CLK            0x00000004
+#define IXGBE_TSAUXC_SYNCLK            0x00000008
+#define IXGBE_TSAUXC_SDP0_INT          0x00000040
+#define IXGBE_TSAUXC_EN_TT0            0x00000001
+#define IXGBE_TSAUXC_EN_TT1            0x00000002
+#define IXGBE_TSAUXC_ST0               0x00000010
 #define IXGBE_TSAUXC_DISABLE_SYSTIME   0x80000000
 
+#define IXGBE_TSSDP_TS_SDP0_SEL_MASK   0x000000C0
+#define IXGBE_TSSDP_TS_SDP0_CLK0       0x00000080
+#define IXGBE_TSSDP_TS_SDP0_EN         0x00000100
+
 #define IXGBE_TSYNCTXCTL_VALID         0x00000001 /* Tx timestamp valid */
 #define IXGBE_TSYNCTXCTL_ENABLED       0x00000010 /* Tx timestamping enabled */