mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-08-05 16:54:27 +00:00

Adds complete support for hardware-based PTP (IEEE 1588) timestamping to the AMD XGBE driver. - Initialize and configure the MAC PTP registers based on link speed and reference clock. - Support both 50MHz and 125MHz PTP reference clocks. - Update the driver interface and version data to support PTP clock frequency selection. Signed-off-by: Raju Rangoju <Raju.Rangoju@amd.com> Link: https://patch.msgid.link/20250718185628.4038779-3-Raju.Rangoju@amd.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
401 lines
11 KiB
C
401 lines
11 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"
|
|
|
|
void xgbe_update_tstamp_time(struct xgbe_prv_data *pdata,
|
|
unsigned int sec, unsigned int nsec)
|
|
{
|
|
int count;
|
|
|
|
/* Set the time values and tell the device */
|
|
XGMAC_IOWRITE(pdata, MAC_STSUR, sec);
|
|
XGMAC_IOWRITE(pdata, MAC_STNUR, nsec);
|
|
|
|
/* issue command to update the system time value */
|
|
XGMAC_IOWRITE(pdata, MAC_TSCR,
|
|
XGMAC_IOREAD(pdata, MAC_TSCR) |
|
|
(1 << MAC_TSCR_TSUPDT_INDEX));
|
|
|
|
/* Wait for the time adjust/update to complete */
|
|
count = 10000;
|
|
while (--count && XGMAC_IOREAD_BITS(pdata, MAC_TSCR, TSUPDT))
|
|
udelay(5);
|
|
|
|
if (count < 0)
|
|
netdev_err(pdata->netdev,
|
|
"timed out updating system timestamp\n");
|
|
}
|
|
|
|
void xgbe_update_tstamp_addend(struct xgbe_prv_data *pdata,
|
|
unsigned int addend)
|
|
{
|
|
unsigned int count = 10000;
|
|
|
|
/* Set the addend register value and tell the device */
|
|
XGMAC_IOWRITE(pdata, MAC_TSAR, addend);
|
|
XGMAC_IOWRITE_BITS(pdata, MAC_TSCR, TSADDREG, 1);
|
|
|
|
/* Wait for addend update to complete */
|
|
while (--count && XGMAC_IOREAD_BITS(pdata, MAC_TSCR, TSADDREG))
|
|
udelay(5);
|
|
|
|
if (!count)
|
|
netdev_err(pdata->netdev,
|
|
"timed out updating timestamp addend register\n");
|
|
}
|
|
|
|
void xgbe_set_tstamp_time(struct xgbe_prv_data *pdata, unsigned int sec,
|
|
unsigned int nsec)
|
|
{
|
|
unsigned int count = 10000;
|
|
|
|
/* Set the time values and tell the device */
|
|
XGMAC_IOWRITE(pdata, MAC_STSUR, sec);
|
|
XGMAC_IOWRITE(pdata, MAC_STNUR, nsec);
|
|
XGMAC_IOWRITE_BITS(pdata, MAC_TSCR, TSINIT, 1);
|
|
|
|
/* Wait for time update to complete */
|
|
while (--count && XGMAC_IOREAD_BITS(pdata, MAC_TSCR, TSINIT))
|
|
udelay(5);
|
|
|
|
if (!count)
|
|
netdev_err(pdata->netdev, "timed out initializing timestamp\n");
|
|
}
|
|
|
|
u64 xgbe_get_tstamp_time(struct xgbe_prv_data *pdata)
|
|
{
|
|
u64 nsec;
|
|
|
|
nsec = XGMAC_IOREAD(pdata, MAC_STSR);
|
|
nsec *= NSEC_PER_SEC;
|
|
nsec += XGMAC_IOREAD(pdata, MAC_STNR);
|
|
|
|
return nsec;
|
|
}
|
|
|
|
u64 xgbe_get_tx_tstamp(struct xgbe_prv_data *pdata)
|
|
{
|
|
unsigned int tx_snr, tx_ssr;
|
|
u64 nsec;
|
|
|
|
if (pdata->vdata->tx_tstamp_workaround) {
|
|
tx_snr = XGMAC_IOREAD(pdata, MAC_TXSNR);
|
|
tx_ssr = XGMAC_IOREAD(pdata, MAC_TXSSR);
|
|
} else {
|
|
tx_ssr = XGMAC_IOREAD(pdata, MAC_TXSSR);
|
|
tx_snr = XGMAC_IOREAD(pdata, MAC_TXSNR);
|
|
}
|
|
|
|
if (XGMAC_GET_BITS(tx_snr, MAC_TXSNR, TXTSSTSMIS))
|
|
return 0;
|
|
|
|
nsec = tx_ssr;
|
|
nsec *= NSEC_PER_SEC;
|
|
nsec += tx_snr;
|
|
|
|
return nsec;
|
|
}
|
|
|
|
void xgbe_get_rx_tstamp(struct xgbe_packet_data *packet,
|
|
struct xgbe_ring_desc *rdesc)
|
|
{
|
|
u64 nsec;
|
|
|
|
if (XGMAC_GET_BITS_LE(rdesc->desc3, RX_CONTEXT_DESC3, TSA) &&
|
|
!XGMAC_GET_BITS_LE(rdesc->desc3, RX_CONTEXT_DESC3, TSD)) {
|
|
nsec = le32_to_cpu(rdesc->desc1);
|
|
nsec *= NSEC_PER_SEC;
|
|
nsec += le32_to_cpu(rdesc->desc0);
|
|
if (nsec != 0xffffffffffffffffULL) {
|
|
packet->rx_tstamp = nsec;
|
|
XGMAC_SET_BITS(packet->attributes, RX_PACKET_ATTRIBUTES,
|
|
RX_TSTAMP, 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
void xgbe_config_tstamp(struct xgbe_prv_data *pdata, unsigned int mac_tscr)
|
|
{
|
|
unsigned int value = 0;
|
|
|
|
value = XGMAC_IOREAD(pdata, MAC_TSCR);
|
|
value |= mac_tscr;
|
|
XGMAC_IOWRITE(pdata, MAC_TSCR, value);
|
|
}
|
|
|
|
void xgbe_tx_tstamp(struct work_struct *work)
|
|
{
|
|
struct xgbe_prv_data *pdata = container_of(work,
|
|
struct xgbe_prv_data,
|
|
tx_tstamp_work);
|
|
struct skb_shared_hwtstamps hwtstamps;
|
|
unsigned long flags;
|
|
|
|
spin_lock_irqsave(&pdata->tstamp_lock, flags);
|
|
if (!pdata->tx_tstamp_skb)
|
|
goto unlock;
|
|
|
|
if (pdata->tx_tstamp) {
|
|
memset(&hwtstamps, 0, sizeof(hwtstamps));
|
|
hwtstamps.hwtstamp = ns_to_ktime(pdata->tx_tstamp);
|
|
skb_tstamp_tx(pdata->tx_tstamp_skb, &hwtstamps);
|
|
}
|
|
|
|
dev_kfree_skb_any(pdata->tx_tstamp_skb);
|
|
|
|
pdata->tx_tstamp_skb = NULL;
|
|
|
|
unlock:
|
|
spin_unlock_irqrestore(&pdata->tstamp_lock, flags);
|
|
}
|
|
|
|
int xgbe_get_hwtstamp_settings(struct xgbe_prv_data *pdata, struct ifreq *ifreq)
|
|
{
|
|
if (copy_to_user(ifreq->ifr_data, &pdata->tstamp_config,
|
|
sizeof(pdata->tstamp_config)))
|
|
return -EFAULT;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int xgbe_set_hwtstamp_settings(struct xgbe_prv_data *pdata, struct ifreq *ifreq)
|
|
{
|
|
struct hwtstamp_config config;
|
|
unsigned int mac_tscr;
|
|
|
|
if (copy_from_user(&config, ifreq->ifr_data, sizeof(config)))
|
|
return -EFAULT;
|
|
|
|
mac_tscr = 0;
|
|
|
|
switch (config.tx_type) {
|
|
case HWTSTAMP_TX_OFF:
|
|
break;
|
|
|
|
case HWTSTAMP_TX_ON:
|
|
XGMAC_SET_BITS(mac_tscr, MAC_TSCR, TSENA, 1);
|
|
break;
|
|
|
|
default:
|
|
return -ERANGE;
|
|
}
|
|
|
|
switch (config.rx_filter) {
|
|
case HWTSTAMP_FILTER_NONE:
|
|
break;
|
|
|
|
case HWTSTAMP_FILTER_NTP_ALL:
|
|
case HWTSTAMP_FILTER_ALL:
|
|
XGMAC_SET_BITS(mac_tscr, MAC_TSCR, TSENALL, 1);
|
|
XGMAC_SET_BITS(mac_tscr, MAC_TSCR, TSENA, 1);
|
|
break;
|
|
|
|
/* PTP v2, UDP, any kind of event packet */
|
|
case HWTSTAMP_FILTER_PTP_V2_L4_EVENT:
|
|
XGMAC_SET_BITS(mac_tscr, MAC_TSCR, TSVER2ENA, 1);
|
|
fallthrough; /* to PTP v1, UDP, any kind of event packet */
|
|
case HWTSTAMP_FILTER_PTP_V1_L4_EVENT:
|
|
XGMAC_SET_BITS(mac_tscr, MAC_TSCR, TSIPV4ENA, 1);
|
|
XGMAC_SET_BITS(mac_tscr, MAC_TSCR, TSIPV6ENA, 1);
|
|
XGMAC_SET_BITS(mac_tscr, MAC_TSCR, SNAPTYPSEL, 1);
|
|
XGMAC_SET_BITS(mac_tscr, MAC_TSCR, TSENA, 1);
|
|
break;
|
|
/* PTP v2, UDP, Sync packet */
|
|
case HWTSTAMP_FILTER_PTP_V2_L4_SYNC:
|
|
XGMAC_SET_BITS(mac_tscr, MAC_TSCR, TSVER2ENA, 1);
|
|
fallthrough; /* to PTP v1, UDP, Sync packet */
|
|
case HWTSTAMP_FILTER_PTP_V1_L4_SYNC:
|
|
XGMAC_SET_BITS(mac_tscr, MAC_TSCR, TSIPV4ENA, 1);
|
|
XGMAC_SET_BITS(mac_tscr, MAC_TSCR, TSIPV6ENA, 1);
|
|
XGMAC_SET_BITS(mac_tscr, MAC_TSCR, TSEVNTENA, 1);
|
|
XGMAC_SET_BITS(mac_tscr, MAC_TSCR, TSENA, 1);
|
|
break;
|
|
|
|
/* PTP v2, UDP, Delay_req packet */
|
|
case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ:
|
|
XGMAC_SET_BITS(mac_tscr, MAC_TSCR, TSVER2ENA, 1);
|
|
fallthrough; /* to PTP v1, UDP, Delay_req packet */
|
|
case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ:
|
|
XGMAC_SET_BITS(mac_tscr, MAC_TSCR, TSIPV4ENA, 1);
|
|
XGMAC_SET_BITS(mac_tscr, MAC_TSCR, TSIPV6ENA, 1);
|
|
XGMAC_SET_BITS(mac_tscr, MAC_TSCR, TSEVNTENA, 1);
|
|
XGMAC_SET_BITS(mac_tscr, MAC_TSCR, TSMSTRENA, 1);
|
|
XGMAC_SET_BITS(mac_tscr, MAC_TSCR, TSENA, 1);
|
|
break;
|
|
|
|
/* 802.AS1, Ethernet, any kind of event packet */
|
|
case HWTSTAMP_FILTER_PTP_V2_L2_EVENT:
|
|
XGMAC_SET_BITS(mac_tscr, MAC_TSCR, AV8021ASMEN, 1);
|
|
XGMAC_SET_BITS(mac_tscr, MAC_TSCR, SNAPTYPSEL, 1);
|
|
XGMAC_SET_BITS(mac_tscr, MAC_TSCR, TSENA, 1);
|
|
break;
|
|
|
|
/* 802.AS1, Ethernet, Sync packet */
|
|
case HWTSTAMP_FILTER_PTP_V2_L2_SYNC:
|
|
XGMAC_SET_BITS(mac_tscr, MAC_TSCR, AV8021ASMEN, 1);
|
|
XGMAC_SET_BITS(mac_tscr, MAC_TSCR, TSEVNTENA, 1);
|
|
XGMAC_SET_BITS(mac_tscr, MAC_TSCR, TSENA, 1);
|
|
break;
|
|
|
|
/* 802.AS1, Ethernet, Delay_req packet */
|
|
case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ:
|
|
XGMAC_SET_BITS(mac_tscr, MAC_TSCR, AV8021ASMEN, 1);
|
|
XGMAC_SET_BITS(mac_tscr, MAC_TSCR, TSMSTRENA, 1);
|
|
XGMAC_SET_BITS(mac_tscr, MAC_TSCR, TSEVNTENA, 1);
|
|
XGMAC_SET_BITS(mac_tscr, MAC_TSCR, TSENA, 1);
|
|
break;
|
|
|
|
/* PTP v2/802.AS1, any layer, any kind of event packet */
|
|
case HWTSTAMP_FILTER_PTP_V2_EVENT:
|
|
XGMAC_SET_BITS(mac_tscr, MAC_TSCR, TSVER2ENA, 1);
|
|
XGMAC_SET_BITS(mac_tscr, MAC_TSCR, TSIPENA, 1);
|
|
XGMAC_SET_BITS(mac_tscr, MAC_TSCR, TSIPV4ENA, 1);
|
|
XGMAC_SET_BITS(mac_tscr, MAC_TSCR, TSIPV6ENA, 1);
|
|
XGMAC_SET_BITS(mac_tscr, MAC_TSCR, SNAPTYPSEL, 1);
|
|
XGMAC_SET_BITS(mac_tscr, MAC_TSCR, TSENA, 1);
|
|
break;
|
|
|
|
/* PTP v2/802.AS1, any layer, Sync packet */
|
|
case HWTSTAMP_FILTER_PTP_V2_SYNC:
|
|
XGMAC_SET_BITS(mac_tscr, MAC_TSCR, TSVER2ENA, 1);
|
|
XGMAC_SET_BITS(mac_tscr, MAC_TSCR, TSIPENA, 1);
|
|
XGMAC_SET_BITS(mac_tscr, MAC_TSCR, TSIPV4ENA, 1);
|
|
XGMAC_SET_BITS(mac_tscr, MAC_TSCR, TSIPV6ENA, 1);
|
|
XGMAC_SET_BITS(mac_tscr, MAC_TSCR, TSEVNTENA, 1);
|
|
XGMAC_SET_BITS(mac_tscr, MAC_TSCR, TSENA, 1);
|
|
break;
|
|
|
|
/* PTP v2/802.AS1, any layer, Delay_req packet */
|
|
case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ:
|
|
XGMAC_SET_BITS(mac_tscr, MAC_TSCR, TSVER2ENA, 1);
|
|
XGMAC_SET_BITS(mac_tscr, MAC_TSCR, TSIPENA, 1);
|
|
XGMAC_SET_BITS(mac_tscr, MAC_TSCR, TSIPV4ENA, 1);
|
|
XGMAC_SET_BITS(mac_tscr, MAC_TSCR, TSIPV6ENA, 1);
|
|
XGMAC_SET_BITS(mac_tscr, MAC_TSCR, TSMSTRENA, 1);
|
|
XGMAC_SET_BITS(mac_tscr, MAC_TSCR, TSEVNTENA, 1);
|
|
XGMAC_SET_BITS(mac_tscr, MAC_TSCR, TSENA, 1);
|
|
break;
|
|
|
|
default:
|
|
return -ERANGE;
|
|
}
|
|
|
|
xgbe_config_tstamp(pdata, mac_tscr);
|
|
|
|
memcpy(&pdata->tstamp_config, &config, sizeof(config));
|
|
|
|
return 0;
|
|
}
|
|
|
|
void xgbe_prep_tx_tstamp(struct xgbe_prv_data *pdata,
|
|
struct sk_buff *skb,
|
|
struct xgbe_packet_data *packet)
|
|
{
|
|
unsigned long flags;
|
|
|
|
if (XGMAC_GET_BITS(packet->attributes, TX_PACKET_ATTRIBUTES, PTP)) {
|
|
spin_lock_irqsave(&pdata->tstamp_lock, flags);
|
|
if (pdata->tx_tstamp_skb) {
|
|
/* Another timestamp in progress, ignore this one */
|
|
XGMAC_SET_BITS(packet->attributes,
|
|
TX_PACKET_ATTRIBUTES, PTP, 0);
|
|
} else {
|
|
pdata->tx_tstamp_skb = skb_get(skb);
|
|
skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS;
|
|
}
|
|
spin_unlock_irqrestore(&pdata->tstamp_lock, flags);
|
|
}
|
|
|
|
skb_tx_timestamp(skb);
|
|
}
|
|
|
|
int xgbe_init_ptp(struct xgbe_prv_data *pdata)
|
|
{
|
|
unsigned int mac_tscr = 0;
|
|
struct timespec64 now;
|
|
u64 dividend;
|
|
|
|
/* Register Settings to be done based on the link speed. */
|
|
switch (pdata->phy.speed) {
|
|
case SPEED_1000:
|
|
XGMAC_IOWRITE(pdata, MAC_TICNR, MAC_TICNR_1G_INITVAL);
|
|
XGMAC_IOWRITE(pdata, MAC_TECNR, MAC_TECNR_1G_INITVAL);
|
|
break;
|
|
case SPEED_2500:
|
|
case SPEED_10000:
|
|
XGMAC_IOWRITE_BITS(pdata, MAC_TICSNR, TSICSNS,
|
|
MAC_TICSNR_10G_INITVAL);
|
|
XGMAC_IOWRITE(pdata, MAC_TECNR, MAC_TECNR_10G_INITVAL);
|
|
XGMAC_IOWRITE_BITS(pdata, MAC_TECSNR, TSECSNS,
|
|
MAC_TECSNR_10G_INITVAL);
|
|
break;
|
|
case SPEED_UNKNOWN:
|
|
default:
|
|
break;
|
|
}
|
|
|
|
/* Enable IEEE1588 PTP clock. */
|
|
XGMAC_SET_BITS(mac_tscr, MAC_TSCR, TSENA, 1);
|
|
|
|
/* Overwrite earlier timestamps */
|
|
XGMAC_SET_BITS(mac_tscr, MAC_TSCR, TXTSSTSM, 1);
|
|
|
|
/* Set one nano-second accuracy */
|
|
XGMAC_SET_BITS(mac_tscr, MAC_TSCR, TSCTRLSSR, 1);
|
|
|
|
/* Set fine timestamp update */
|
|
XGMAC_SET_BITS(mac_tscr, MAC_TSCR, TSCFUPDT, 1);
|
|
|
|
xgbe_config_tstamp(pdata, mac_tscr);
|
|
|
|
/* Exit if timestamping is not enabled */
|
|
if (!XGMAC_GET_BITS(mac_tscr, MAC_TSCR, TSENA))
|
|
return -EOPNOTSUPP;
|
|
|
|
if (pdata->vdata->tstamp_ptp_clock_freq) {
|
|
/* Initialize time registers based on
|
|
* 125MHz PTP Clock Frequency
|
|
*/
|
|
XGMAC_IOWRITE_BITS(pdata, MAC_SSIR, SSINC,
|
|
XGBE_V2_TSTAMP_SSINC);
|
|
XGMAC_IOWRITE_BITS(pdata, MAC_SSIR, SNSINC,
|
|
XGBE_V2_TSTAMP_SNSINC);
|
|
} else {
|
|
/* Initialize time registers based on
|
|
* 50MHz PTP Clock Frequency
|
|
*/
|
|
XGMAC_IOWRITE_BITS(pdata, MAC_SSIR, SSINC, XGBE_TSTAMP_SSINC);
|
|
XGMAC_IOWRITE_BITS(pdata, MAC_SSIR, SNSINC, XGBE_TSTAMP_SNSINC);
|
|
}
|
|
|
|
/* Calculate the addend:
|
|
* addend = 2^32 / (PTP ref clock / (PTP clock based on SSINC))
|
|
* = (2^32 * (PTP clock based on SSINC)) / PTP ref clock
|
|
*/
|
|
if (pdata->vdata->tstamp_ptp_clock_freq)
|
|
dividend = XGBE_V2_PTP_ACT_CLK_FREQ;
|
|
else
|
|
dividend = XGBE_PTP_ACT_CLK_FREQ;
|
|
|
|
dividend = (u64)(dividend << 32);
|
|
pdata->tstamp_addend = div_u64(dividend, pdata->ptpclk_rate);
|
|
|
|
xgbe_update_tstamp_addend(pdata, pdata->tstamp_addend);
|
|
|
|
dma_wmb();
|
|
/* initialize system time */
|
|
ktime_get_real_ts64(&now);
|
|
|
|
/* lower 32 bits of tv_sec are safe until y2106 */
|
|
xgbe_set_tstamp_time(pdata, (u32)now.tv_sec, now.tv_nsec);
|
|
|
|
return 0;
|
|
}
|