Commit 5b5ba63a authored by Raju Rangoju's avatar Raju Rangoju Committed by Jakub Kicinski
Browse files

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: default avatarRaju Rangoju <Raju.Rangoju@amd.com>
Link: https://patch.msgid.link/20250909113143.1364477-1-Raju.Rangoju@amd.com


Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parent b957665d
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -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

+20 −2
Original line number Diff line number Diff line
@@ -227,7 +227,11 @@
#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
@@ -235,6 +239,18 @@
#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
@@ -496,6 +512,8 @@
#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 */
#define MMC_CR				0x0800
+15 −0
Original line number Diff line number Diff line
@@ -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:
+74 −0
Original line number Diff line number Diff line
// 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;
}
+25 −1
Original line number Diff line number Diff line
@@ -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)
{
	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);
Loading