mirror of
https://github.com/torvalds/linux.git
synced 2026-05-02 13:32:40 -04:00
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>
75 lines
1.8 KiB
C
75 lines
1.8 KiB
C
// 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;
|
|
}
|