Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

amd-xgbe: Add PPS periodic output support

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>

authored by

Raju Rangoju and committed by
Jakub Kicinski
5b5ba63a b957665d

+151 -4
+1 -1
drivers/net/ethernet/amd/xgbe/Makefile
··· 3 3 4 4 amd-xgbe-objs := xgbe-main.o xgbe-drv.o xgbe-dev.o \ 5 5 xgbe-desc.o xgbe-ethtool.o xgbe-mdio.o \ 6 - xgbe-hwtstamp.o xgbe-ptp.o \ 6 + xgbe-hwtstamp.o xgbe-ptp.o xgbe-pps.o \ 7 7 xgbe-i2c.o xgbe-phy-v1.o xgbe-phy-v2.o \ 8 8 xgbe-platform.o 9 9
+20 -2
drivers/net/ethernet/amd/xgbe/xgbe-common.h
··· 227 227 #define MAC_TICSNR 0x0d5C 228 228 #define MAC_TECNR 0x0d60 229 229 #define MAC_TECSNR 0x0d64 230 - 230 + #define MAC_PPSCR 0x0d70 231 + #define MAC_PPS0_TTSR 0x0d80 232 + #define MAC_PPS0_TTNSR 0x0d84 233 + #define MAC_PPS0_INTERVAL 0x0d88 234 + #define MAC_PPS0_WIDTH 0x0d8C 231 235 #define MAC_QTFCR_INC 4 232 236 #define MAC_MACA_INC 4 233 237 #define MAC_HTR_INC 4 234 238 235 239 #define MAC_RQC2_INC 4 236 240 #define MAC_RQC2_Q_PER_REG 4 241 + 242 + /* PPS helpers */ 243 + #define PPSEN0 BIT(4) 244 + #define MAC_PPSx_TTSR(x) ((MAC_PPS0_TTSR) + ((x) * 0x10)) 245 + #define MAC_PPSx_TTNSR(x) ((MAC_PPS0_TTNSR) + ((x) * 0x10)) 246 + #define MAC_PPSx_INTERVAL(x) ((MAC_PPS0_INTERVAL) + ((x) * 0x10)) 247 + #define MAC_PPSx_WIDTH(x) ((MAC_PPS0_WIDTH) + ((x) * 0x10)) 248 + #define PPS_MAXIDX(x) ((((x) + 1) * 8) - 1) 249 + #define PPS_MINIDX(x) ((x) * 8) 250 + #define XGBE_PPSCMD_STOP 0x5 251 + #define XGBE_PPSCMD_START 0x2 252 + #define XGBE_PPSTARGET_PULSE 0x2 237 253 238 254 /* MAC register entry bit positions and sizes */ 239 255 #define MAC_HWF0R_ADDMACADRSEL_INDEX 18 ··· 512 496 #define MAC_VR_SNPSVER_WIDTH 8 513 497 #define MAC_VR_USERVER_INDEX 16 514 498 #define MAC_VR_USERVER_WIDTH 8 499 + #define MAC_PPSx_TTNSR_TRGTBUSY0_INDEX 31 500 + #define MAC_PPSx_TTNSR_TRGTBUSY0_WIDTH 1 515 501 516 - /* MMC register offsets */ 502 + /* MMC register offsets */ 517 503 #define MMC_CR 0x0800 518 504 #define MMC_RISR 0x0804 519 505 #define MMC_TISR 0x0808
+15
drivers/net/ethernet/amd/xgbe/xgbe-drv.c
··· 691 691 hw_feat->pps_out_num = XGMAC_GET_BITS(mac_hfr2, MAC_HWF2R, PPSOUTNUM); 692 692 hw_feat->aux_snap_num = XGMAC_GET_BITS(mac_hfr2, MAC_HWF2R, AUXSNAPNUM); 693 693 694 + /* Sanity check and warn if hardware reports more than supported */ 695 + if (hw_feat->pps_out_num > XGBE_MAX_PPS_OUT) { 696 + dev_warn(pdata->dev, 697 + "Hardware reports %u PPS outputs, limiting to %u\n", 698 + hw_feat->pps_out_num, XGBE_MAX_PPS_OUT); 699 + hw_feat->pps_out_num = XGBE_MAX_PPS_OUT; 700 + } 701 + 702 + if (hw_feat->aux_snap_num > XGBE_MAX_AUX_SNAP) { 703 + dev_warn(pdata->dev, 704 + "Hardware reports %u aux snapshot inputs, limiting to %u\n", 705 + hw_feat->aux_snap_num, XGBE_MAX_AUX_SNAP); 706 + hw_feat->aux_snap_num = XGBE_MAX_AUX_SNAP; 707 + } 708 + 694 709 /* Translate the Hash Table size into actual number */ 695 710 switch (hw_feat->hash_table_size) { 696 711 case 0:
+74
drivers/net/ethernet/amd/xgbe/xgbe-pps.c
··· 1 + // SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-3-Clause) 2 + /* 3 + * Copyright (c) 2014-2025, Advanced Micro Devices, Inc. 4 + * Copyright (c) 2014, Synopsys, Inc. 5 + * All rights reserved 6 + * 7 + * Author: Raju Rangoju <Raju.Rangoju@amd.com> 8 + */ 9 + 10 + #include "xgbe.h" 11 + #include "xgbe-common.h" 12 + 13 + static u32 get_pps_mask(unsigned int x) 14 + { 15 + return GENMASK(PPS_MAXIDX(x), PPS_MINIDX(x)); 16 + } 17 + 18 + static u32 get_pps_cmd(unsigned int x, u32 val) 19 + { 20 + return (val & GENMASK(3, 0)) << PPS_MINIDX(x); 21 + } 22 + 23 + static u32 get_target_mode_sel(unsigned int x, u32 val) 24 + { 25 + return (val & GENMASK(1, 0)) << (PPS_MAXIDX(x) - 2); 26 + } 27 + 28 + int xgbe_pps_config(struct xgbe_prv_data *pdata, 29 + struct xgbe_pps_config *cfg, int index, bool on) 30 + { 31 + unsigned int ppscr = 0; 32 + unsigned int tnsec; 33 + u64 period; 34 + 35 + /* Check if target time register is busy */ 36 + tnsec = XGMAC_IOREAD(pdata, MAC_PPSx_TTNSR(index)); 37 + if (XGMAC_GET_BITS(tnsec, MAC_PPSx_TTNSR, TRGTBUSY0)) 38 + return -EBUSY; 39 + 40 + ppscr = XGMAC_IOREAD(pdata, MAC_PPSCR); 41 + ppscr &= ~get_pps_mask(index); 42 + 43 + if (!on) { 44 + /* Disable PPS output */ 45 + ppscr |= get_pps_cmd(index, XGBE_PPSCMD_STOP); 46 + ppscr |= PPSEN0; 47 + XGMAC_IOWRITE(pdata, MAC_PPSCR, ppscr); 48 + 49 + return 0; 50 + } 51 + 52 + /* Configure start time */ 53 + XGMAC_IOWRITE(pdata, MAC_PPSx_TTSR(index), cfg->start.tv_sec); 54 + XGMAC_IOWRITE(pdata, MAC_PPSx_TTNSR(index), cfg->start.tv_nsec); 55 + 56 + period = cfg->period.tv_sec * NSEC_PER_SEC + cfg->period.tv_nsec; 57 + period = div_u64(period, XGBE_V2_TSTAMP_SSINC); 58 + 59 + if (period < 4) 60 + return -EINVAL; 61 + 62 + /* Configure interval and pulse width (50% duty cycle) */ 63 + XGMAC_IOWRITE(pdata, MAC_PPSx_INTERVAL(index), period - 1); 64 + XGMAC_IOWRITE(pdata, MAC_PPSx_WIDTH(index), (period >> 1) - 1); 65 + 66 + /* Enable PPS with pulse train mode */ 67 + ppscr |= get_pps_cmd(index, XGBE_PPSCMD_START); 68 + ppscr |= get_target_mode_sel(index, XGBE_PPSTARGET_PULSE); 69 + ppscr |= PPSEN0; 70 + 71 + XGMAC_IOWRITE(pdata, MAC_PPSCR, ppscr); 72 + 73 + return 0; 74 + }
+25 -1
drivers/net/ethernet/amd/xgbe/xgbe-ptp.c
··· 106 106 static int xgbe_enable(struct ptp_clock_info *info, 107 107 struct ptp_clock_request *request, int on) 108 108 { 109 - return -EOPNOTSUPP; 109 + struct xgbe_prv_data *pdata = container_of(info, struct xgbe_prv_data, 110 + ptp_clock_info); 111 + struct xgbe_pps_config *pps_cfg; 112 + unsigned long flags; 113 + int ret; 114 + 115 + dev_dbg(pdata->dev, "rq->type %d on %d\n", request->type, on); 116 + 117 + if (request->type != PTP_CLK_REQ_PEROUT) 118 + return -EOPNOTSUPP; 119 + 120 + pps_cfg = &pdata->pps[request->perout.index]; 121 + 122 + pps_cfg->start.tv_sec = request->perout.start.sec; 123 + pps_cfg->start.tv_nsec = request->perout.start.nsec; 124 + pps_cfg->period.tv_sec = request->perout.period.sec; 125 + pps_cfg->period.tv_nsec = request->perout.period.nsec; 126 + 127 + spin_lock_irqsave(&pdata->tstamp_lock, flags); 128 + ret = xgbe_pps_config(pdata, pps_cfg, request->perout.index, on); 129 + spin_unlock_irqrestore(&pdata->tstamp_lock, flags); 130 + 131 + return ret; 110 132 } 111 133 112 134 void xgbe_ptp_register(struct xgbe_prv_data *pdata) ··· 144 122 info->adjtime = xgbe_adjtime; 145 123 info->gettimex64 = xgbe_gettimex; 146 124 info->settime64 = xgbe_settime; 125 + info->n_per_out = pdata->hw_feat.pps_out_num; 126 + info->n_ext_ts = pdata->hw_feat.aux_snap_num; 147 127 info->enable = xgbe_enable; 148 128 149 129 clock = ptp_clock_register(info, pdata->dev);
+16
drivers/net/ethernet/amd/xgbe/xgbe.h
··· 142 142 #define XGBE_V2_TSTAMP_SNSINC 0 143 143 #define XGBE_V2_PTP_ACT_CLK_FREQ 1000000000 144 144 145 + /* Define maximum supported values */ 146 + #define XGBE_MAX_PPS_OUT 4 147 + #define XGBE_MAX_AUX_SNAP 4 148 + 145 149 /* Driver PMT macros */ 146 150 #define XGMAC_DRIVER_CONTEXT 1 147 151 #define XGMAC_IOCTL_CONTEXT 2 ··· 677 673 u64 rx_vxlan_csum_errors; 678 674 }; 679 675 676 + struct xgbe_pps_config { 677 + struct timespec64 start; 678 + struct timespec64 period; 679 + }; 680 + 680 681 struct xgbe_hw_if { 681 682 int (*tx_complete)(struct xgbe_ring_desc *); 682 683 ··· 1152 1143 struct sk_buff *tx_tstamp_skb; 1153 1144 u64 tx_tstamp; 1154 1145 1146 + /* Pulse Per Second output */ 1147 + struct xgbe_pps_config pps[XGBE_MAX_PPS_OUT]; 1148 + 1155 1149 /* DCB support */ 1156 1150 struct ieee_ets *ets; 1157 1151 struct ieee_pfc *pfc; ··· 1317 1305 int xgbe_init_ptp(struct xgbe_prv_data *pdata); 1318 1306 void xgbe_update_tstamp_time(struct xgbe_prv_data *pdata, unsigned int sec, 1319 1307 unsigned int nsec); 1308 + 1309 + int xgbe_pps_config(struct xgbe_prv_data *pdata, struct xgbe_pps_config *cfg, 1310 + int index, bool on); 1311 + 1320 1312 #ifdef CONFIG_DEBUG_FS 1321 1313 void xgbe_debugfs_init(struct xgbe_prv_data *); 1322 1314 void xgbe_debugfs_exit(struct xgbe_prv_data *);