]> www.infradead.org Git - users/hch/misc.git/commitdiff
amd-xgbe: Add PPS periodic output support
authorRaju Rangoju <Raju.Rangoju@amd.com>
Tue, 9 Sep 2025 11:31:43 +0000 (17:01 +0530)
committerJakub Kicinski <kuba@kernel.org>
Sun, 14 Sep 2025 21:29:26 +0000 (14:29 -0700)
Add support for hardware PPS (Pulse Per Second) output to the
AMD XGBE driver. The implementation enables flexible periodic
output mode, exposing it via the PTP per_out interface.

The driver supports configuring PPS output using the standard
PTP subsystem, allowing precise periodic signal generation for
time synchronization applications.

The feature has been verified using the testptp tool and
oscilloscope.

Signed-off-by: Raju Rangoju <Raju.Rangoju@amd.com>
Link: https://patch.msgid.link/20250909113143.1364477-1-Raju.Rangoju@amd.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
drivers/net/ethernet/amd/xgbe/Makefile
drivers/net/ethernet/amd/xgbe/xgbe-common.h
drivers/net/ethernet/amd/xgbe/xgbe-drv.c
drivers/net/ethernet/amd/xgbe/xgbe-pps.c [new file with mode: 0644]
drivers/net/ethernet/amd/xgbe/xgbe-ptp.c
drivers/net/ethernet/amd/xgbe/xgbe.h

index 5b0ab6240cf23e247db62cd6adc911d140d27a0a..980e276522377baae9e730eb7b21c76f479fc810 100644 (file)
@@ -3,7 +3,7 @@ obj-$(CONFIG_AMD_XGBE) += amd-xgbe.o
 
 amd-xgbe-objs := xgbe-main.o xgbe-drv.o xgbe-dev.o \
                 xgbe-desc.o xgbe-ethtool.o xgbe-mdio.o \
-                xgbe-hwtstamp.o xgbe-ptp.o \
+                xgbe-hwtstamp.o xgbe-ptp.o xgbe-pps.o \
                 xgbe-i2c.o xgbe-phy-v1.o xgbe-phy-v2.o \
                 xgbe-platform.o
 
index 009fbc9b11cec3c81c32a6179dfd64db972f40e9..62b01de93db49a982c6fbde45d104f1c03448c70 100644 (file)
 #define MAC_TICSNR                      0x0d5C
 #define MAC_TECNR                       0x0d60
 #define MAC_TECSNR                      0x0d64
-
+#define MAC_PPSCR                      0x0d70
+#define MAC_PPS0_TTSR                  0x0d80
+#define MAC_PPS0_TTNSR                 0x0d84
+#define MAC_PPS0_INTERVAL              0x0d88
+#define MAC_PPS0_WIDTH                 0x0d8C
 #define MAC_QTFCR_INC                  4
 #define MAC_MACA_INC                   4
 #define MAC_HTR_INC                    4
 #define MAC_RQC2_INC                   4
 #define MAC_RQC2_Q_PER_REG             4
 
+/* PPS helpers */
+#define PPSEN0                         BIT(4)
+#define MAC_PPSx_TTSR(x)               ((MAC_PPS0_TTSR) + ((x) * 0x10))
+#define MAC_PPSx_TTNSR(x)              ((MAC_PPS0_TTNSR) + ((x) * 0x10))
+#define MAC_PPSx_INTERVAL(x)           ((MAC_PPS0_INTERVAL) + ((x) * 0x10))
+#define MAC_PPSx_WIDTH(x)              ((MAC_PPS0_WIDTH) + ((x) * 0x10))
+#define PPS_MAXIDX(x)                  ((((x) + 1) * 8) - 1)
+#define PPS_MINIDX(x)                  ((x) * 8)
+#define XGBE_PPSCMD_STOP               0x5
+#define XGBE_PPSCMD_START              0x2
+#define XGBE_PPSTARGET_PULSE           0x2
+
 /* MAC register entry bit positions and sizes */
 #define MAC_HWF0R_ADDMACADRSEL_INDEX   18
 #define MAC_HWF0R_ADDMACADRSEL_WIDTH   5
 #define MAC_VR_SNPSVER_WIDTH           8
 #define MAC_VR_USERVER_INDEX           16
 #define MAC_VR_USERVER_WIDTH           8
+#define MAC_PPSx_TTNSR_TRGTBUSY0_INDEX 31
+#define MAC_PPSx_TTNSR_TRGTBUSY0_WIDTH 1
 
-/* MMC register offsets */
+ /* MMC register offsets */
 #define MMC_CR                         0x0800
 #define MMC_RISR                       0x0804
 #define MMC_TISR                       0x0808
index 2e9b95a94f89fbfdbeebe68114b899399965516b..f0989aa01855d0fe431d99c6b57d931444375279 100644 (file)
@@ -691,6 +691,21 @@ void xgbe_get_all_hw_features(struct xgbe_prv_data *pdata)
        hw_feat->pps_out_num  = XGMAC_GET_BITS(mac_hfr2, MAC_HWF2R, PPSOUTNUM);
        hw_feat->aux_snap_num = XGMAC_GET_BITS(mac_hfr2, MAC_HWF2R, AUXSNAPNUM);
 
+       /* Sanity check and warn if hardware reports more than supported */
+       if (hw_feat->pps_out_num > XGBE_MAX_PPS_OUT) {
+               dev_warn(pdata->dev,
+                        "Hardware reports %u PPS outputs, limiting to %u\n",
+                        hw_feat->pps_out_num, XGBE_MAX_PPS_OUT);
+               hw_feat->pps_out_num = XGBE_MAX_PPS_OUT;
+       }
+
+       if (hw_feat->aux_snap_num > XGBE_MAX_AUX_SNAP) {
+               dev_warn(pdata->dev,
+                        "Hardware reports %u aux snapshot inputs, limiting to %u\n",
+                        hw_feat->aux_snap_num, XGBE_MAX_AUX_SNAP);
+               hw_feat->aux_snap_num = XGBE_MAX_AUX_SNAP;
+       }
+
        /* Translate the Hash Table size into actual number */
        switch (hw_feat->hash_table_size) {
        case 0:
diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-pps.c b/drivers/net/ethernet/amd/xgbe/xgbe-pps.c
new file mode 100644 (file)
index 0000000..6d03ae7
--- /dev/null
@@ -0,0 +1,74 @@
+// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-3-Clause)
+/*
+ * Copyright (c) 2014-2025, Advanced Micro Devices, Inc.
+ * Copyright (c) 2014, Synopsys, Inc.
+ * All rights reserved
+ *
+ * Author: Raju Rangoju <Raju.Rangoju@amd.com>
+ */
+
+#include "xgbe.h"
+#include "xgbe-common.h"
+
+static u32 get_pps_mask(unsigned int x)
+{
+       return GENMASK(PPS_MAXIDX(x), PPS_MINIDX(x));
+}
+
+static u32 get_pps_cmd(unsigned int x, u32 val)
+{
+       return (val & GENMASK(3, 0)) << PPS_MINIDX(x);
+}
+
+static u32 get_target_mode_sel(unsigned int x, u32 val)
+{
+       return (val & GENMASK(1, 0)) << (PPS_MAXIDX(x) - 2);
+}
+
+int xgbe_pps_config(struct xgbe_prv_data *pdata,
+                   struct xgbe_pps_config *cfg, int index, bool on)
+{
+       unsigned int ppscr = 0;
+       unsigned int tnsec;
+       u64 period;
+
+       /* Check if target time register is busy */
+       tnsec = XGMAC_IOREAD(pdata, MAC_PPSx_TTNSR(index));
+       if (XGMAC_GET_BITS(tnsec, MAC_PPSx_TTNSR, TRGTBUSY0))
+               return -EBUSY;
+
+       ppscr = XGMAC_IOREAD(pdata, MAC_PPSCR);
+       ppscr &= ~get_pps_mask(index);
+
+       if (!on) {
+               /* Disable PPS output */
+               ppscr |= get_pps_cmd(index, XGBE_PPSCMD_STOP);
+               ppscr |= PPSEN0;
+               XGMAC_IOWRITE(pdata, MAC_PPSCR, ppscr);
+
+               return 0;
+       }
+
+       /* Configure start time */
+       XGMAC_IOWRITE(pdata, MAC_PPSx_TTSR(index), cfg->start.tv_sec);
+       XGMAC_IOWRITE(pdata, MAC_PPSx_TTNSR(index), cfg->start.tv_nsec);
+
+       period = cfg->period.tv_sec * NSEC_PER_SEC + cfg->period.tv_nsec;
+       period = div_u64(period, XGBE_V2_TSTAMP_SSINC);
+
+       if (period < 4)
+               return -EINVAL;
+
+       /* Configure interval and pulse width (50% duty cycle) */
+       XGMAC_IOWRITE(pdata, MAC_PPSx_INTERVAL(index), period - 1);
+       XGMAC_IOWRITE(pdata, MAC_PPSx_WIDTH(index), (period >> 1) - 1);
+
+       /* Enable PPS with pulse train mode */
+       ppscr |= get_pps_cmd(index, XGBE_PPSCMD_START);
+       ppscr |= get_target_mode_sel(index, XGBE_PPSTARGET_PULSE);
+       ppscr |= PPSEN0;
+
+       XGMAC_IOWRITE(pdata, MAC_PPSCR, ppscr);
+
+       return 0;
+}
index 3658afc7801d7cc46ad1850194f9ea4a1b2775b1..0e0b8ec3b50414236d853c972c24861cef97a1ec 100644 (file)
@@ -106,7 +106,29 @@ static int xgbe_settime(struct ptp_clock_info *info,
 static int xgbe_enable(struct ptp_clock_info *info,
                       struct ptp_clock_request *request, int on)
 {
-       return -EOPNOTSUPP;
+       struct xgbe_prv_data *pdata = container_of(info, struct xgbe_prv_data,
+                                                  ptp_clock_info);
+       struct xgbe_pps_config *pps_cfg;
+       unsigned long flags;
+       int ret;
+
+       dev_dbg(pdata->dev, "rq->type %d on %d\n", request->type, on);
+
+       if (request->type != PTP_CLK_REQ_PEROUT)
+               return -EOPNOTSUPP;
+
+       pps_cfg = &pdata->pps[request->perout.index];
+
+       pps_cfg->start.tv_sec = request->perout.start.sec;
+       pps_cfg->start.tv_nsec = request->perout.start.nsec;
+       pps_cfg->period.tv_sec = request->perout.period.sec;
+       pps_cfg->period.tv_nsec = request->perout.period.nsec;
+
+       spin_lock_irqsave(&pdata->tstamp_lock, flags);
+       ret = xgbe_pps_config(pdata, pps_cfg, request->perout.index, on);
+       spin_unlock_irqrestore(&pdata->tstamp_lock, flags);
+
+       return ret;
 }
 
 void xgbe_ptp_register(struct xgbe_prv_data *pdata)
@@ -122,6 +144,8 @@ void xgbe_ptp_register(struct xgbe_prv_data *pdata)
        info->adjtime = xgbe_adjtime;
        info->gettimex64 = xgbe_gettimex;
        info->settime64 = xgbe_settime;
+       info->n_per_out = pdata->hw_feat.pps_out_num;
+       info->n_ext_ts = pdata->hw_feat.aux_snap_num;
        info->enable = xgbe_enable;
 
        clock = ptp_clock_register(info, pdata->dev);
index 0fa80a238ac5d0648f791bf111ee34892a5dd66d..e8bbb68059013a1f277cffb5d120eb018019ba2b 100644 (file)
 #define XGBE_V2_TSTAMP_SNSINC  0
 #define XGBE_V2_PTP_ACT_CLK_FREQ       1000000000
 
+/* Define maximum supported values */
+#define XGBE_MAX_PPS_OUT       4
+#define XGBE_MAX_AUX_SNAP      4
+
 /* Driver PMT macros */
 #define XGMAC_DRIVER_CONTEXT   1
 #define XGMAC_IOCTL_CONTEXT    2
@@ -673,6 +677,11 @@ struct xgbe_ext_stats {
        u64 rx_vxlan_csum_errors;
 };
 
+struct xgbe_pps_config {
+       struct timespec64 start;
+       struct timespec64 period;
+};
+
 struct xgbe_hw_if {
        int (*tx_complete)(struct xgbe_ring_desc *);
 
@@ -1143,6 +1152,9 @@ struct xgbe_prv_data {
        struct sk_buff *tx_tstamp_skb;
        u64 tx_tstamp;
 
+       /* Pulse Per Second output */
+       struct xgbe_pps_config pps[XGBE_MAX_PPS_OUT];
+
        /* DCB support */
        struct ieee_ets *ets;
        struct ieee_pfc *pfc;
@@ -1305,6 +1317,10 @@ void xgbe_prep_tx_tstamp(struct xgbe_prv_data *pdata,
 int xgbe_init_ptp(struct xgbe_prv_data *pdata);
 void xgbe_update_tstamp_time(struct xgbe_prv_data *pdata, unsigned int sec,
                             unsigned int nsec);
+
+int xgbe_pps_config(struct xgbe_prv_data *pdata, struct xgbe_pps_config *cfg,
+                   int index, bool on);
+
 #ifdef CONFIG_DEBUG_FS
 void xgbe_debugfs_init(struct xgbe_prv_data *);
 void xgbe_debugfs_exit(struct xgbe_prv_data *);