// 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 */ #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; }