685 lines
19 KiB
C
685 lines
19 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
||
/* Copyright(c) 2022 - 2024 Mucse Corporation. */
|
||
|
||
#include <linux/netdevice.h>
|
||
#include <linux/ptp_classify.h>
|
||
#include <linux/io.h>
|
||
#include <linux/delay.h>
|
||
#include <linux/clk.h>
|
||
|
||
#include "rnpgbe.h"
|
||
#include "rnpgbe_regs.h"
|
||
#include "rnpgbe_ptp.h"
|
||
#include "rnpgbe_mbx.h"
|
||
|
||
/* PTP and HW Timer ops */
|
||
static void config_hw_tstamping(void __iomem *ioaddr, u32 data)
|
||
{
|
||
writel(data, ioaddr + PTP_TCR);
|
||
}
|
||
|
||
static void config_sub_second_increment(void __iomem *ioaddr, u32 ptp_clock,
|
||
int gmac4, u32 *ssinc)
|
||
{
|
||
u32 value = readl(ioaddr + PTP_TCR);
|
||
unsigned long data;
|
||
u32 reg_value;
|
||
|
||
/* For GMAC3.x, 4.x versions, in "fine adjustement mode" set sub-second
|
||
* increment to twice the number of nanoseconds of a clock cycle.
|
||
* The calculation of the default_addend value by the caller will set it
|
||
* to mid-range = 2^31 when the remainder of this division is zero,
|
||
* which will make the accumulator overflow once every 2 ptp_clock
|
||
* cycles, adding twice the number of nanoseconds of a clock cycle :
|
||
* 2000000000ULL / ptp_clock.
|
||
*/
|
||
if (value & RNP_PTP_TCR_TSCFUPDT)
|
||
data = (2000000000ULL / ptp_clock);
|
||
else
|
||
data = (1000000000ULL / ptp_clock);
|
||
|
||
/* 0.465ns accuracy */
|
||
if (!(value & RNP_PTP_TCR_TSCTRLSSR))
|
||
data = (data * 1000) / 465;
|
||
|
||
data &= RNP_PTP_SSIR_SSINC_MASK;
|
||
|
||
reg_value = data;
|
||
if (gmac4)
|
||
reg_value <<= RNP_PTP_SSIR_SSINC_SHIFT;
|
||
|
||
writel(reg_value, ioaddr + PTP_SSIR);
|
||
|
||
if (ssinc)
|
||
*ssinc = data;
|
||
}
|
||
|
||
static int config_addend(void __iomem *ioaddr, u32 addend)
|
||
{
|
||
u32 value;
|
||
int limit;
|
||
|
||
writel(addend, ioaddr + PTP_TAR);
|
||
/* issue command to update the addend value */
|
||
value = readl(ioaddr + PTP_TCR);
|
||
value |= RNP_PTP_TCR_TSADDREG;
|
||
writel(value, ioaddr + PTP_TCR);
|
||
|
||
/* wait for present addend update to complete */
|
||
limit = 10;
|
||
while (limit--) {
|
||
if (!(readl(ioaddr + PTP_TCR) & RNP_PTP_TCR_TSADDREG))
|
||
break;
|
||
mdelay(10);
|
||
}
|
||
if (limit < 0)
|
||
return -EBUSY;
|
||
|
||
return 0;
|
||
}
|
||
|
||
static int init_systime(void __iomem *ioaddr, u32 sec, u32 nsec)
|
||
{
|
||
int limit;
|
||
u32 value;
|
||
|
||
writel(sec, ioaddr + PTP_STSUR);
|
||
writel(nsec, ioaddr + PTP_STNSUR);
|
||
/* issue command to initialize the system time value */
|
||
value = readl(ioaddr + PTP_TCR);
|
||
value |= RNP_PTP_TCR_TSINIT;
|
||
writel(value, ioaddr + PTP_TCR);
|
||
|
||
/* wait for present system time initialize to complete */
|
||
limit = 10;
|
||
while (limit--) {
|
||
if (!(readl(ioaddr + PTP_TCR) & RNP_PTP_TCR_TSINIT))
|
||
break;
|
||
mdelay(10);
|
||
}
|
||
if (limit < 0)
|
||
return -EBUSY;
|
||
|
||
return 0;
|
||
}
|
||
|
||
static void get_systime(void __iomem *ioaddr, u64 *systime)
|
||
{
|
||
u64 ns;
|
||
|
||
/* Get the TSSS value */
|
||
ns = readl(ioaddr + PTP_STNSR);
|
||
/* Get the TSS and convert sec time value to nanosecond */
|
||
ns += readl(ioaddr + PTP_STSR) * 1000000000ULL;
|
||
|
||
if (systime)
|
||
*systime = ns;
|
||
}
|
||
|
||
static void config_mac_interrupt_enable(void __iomem *ioaddr, bool on)
|
||
{
|
||
rnpgbe_wr_reg(ioaddr + RNP_MAC_INTERRUPT_ENABLE, on);
|
||
}
|
||
|
||
static int adjust_systime(void __iomem *ioaddr, u32 sec, u32 nsec, int add_sub,
|
||
int gmac4)
|
||
{
|
||
u32 value;
|
||
int limit;
|
||
|
||
if (add_sub) {
|
||
/* If the new sec value needs to be subtracted with
|
||
* the system time, then MAC_STSUR reg should be
|
||
* programmed with (2^32 – <new_sec_value>)
|
||
*/
|
||
if (gmac4)
|
||
sec = -sec;
|
||
|
||
value = readl(ioaddr + PTP_TCR);
|
||
if (value & RNP_PTP_TCR_TSCTRLSSR)
|
||
nsec = (RNP_PTP_DIGITAL_ROLLOVER_MODE - nsec);
|
||
else
|
||
nsec = (RNP_PTP_BINARY_ROLLOVER_MODE - nsec);
|
||
}
|
||
|
||
writel(sec, ioaddr + PTP_STSUR);
|
||
value = (add_sub << RNP_PTP_STNSUR_ADDSUB_SHIFT) | nsec;
|
||
writel(value, ioaddr + PTP_STNSUR);
|
||
|
||
/* issue command to initialize the system time value */
|
||
value = readl(ioaddr + PTP_TCR);
|
||
value |= RNP_PTP_TCR_TSUPDT;
|
||
writel(value, ioaddr + PTP_TCR);
|
||
|
||
/* wait for present system time adjust/update to complete */
|
||
limit = 10;
|
||
while (limit--) {
|
||
if (!(readl(ioaddr + PTP_TCR) & RNP_PTP_TCR_TSUPDT))
|
||
break;
|
||
mdelay(10);
|
||
}
|
||
if (limit < 0)
|
||
return -EBUSY;
|
||
|
||
return 0;
|
||
}
|
||
|
||
const struct rnpgbe_hwtimestamp mac_ptp = {
|
||
.config_hw_tstamping = config_hw_tstamping,
|
||
.config_mac_irq_enable = config_mac_interrupt_enable,
|
||
.init_systime = init_systime,
|
||
.config_sub_second_increment = config_sub_second_increment,
|
||
.config_addend = config_addend,
|
||
.adjust_systime = adjust_systime,
|
||
.get_systime = get_systime,
|
||
};
|
||
|
||
static int rnpgbe_ptp_adjfreq(struct ptp_clock_info *ptp, long scaled_ppm)
|
||
{
|
||
struct rnpgbe_adapter *pf =
|
||
container_of(ptp, struct rnpgbe_adapter, ptp_clock_ops);
|
||
unsigned long flags;
|
||
u32 addend;
|
||
|
||
if (!pf) {
|
||
printk(KERN_DEBUG "adapter_of contail is null\n");
|
||
return 0;
|
||
}
|
||
addend = adjust_by_scaled_ppm(pf->default_addend, scaled_ppm);
|
||
|
||
spin_lock_irqsave(&pf->ptp_lock, flags);
|
||
pf->hwts_ops->config_addend(pf->ptp_addr, addend);
|
||
spin_unlock_irqrestore(&pf->ptp_lock, flags);
|
||
|
||
return 0;
|
||
}
|
||
|
||
static int rnpgbe_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta)
|
||
{
|
||
struct rnpgbe_adapter *pf =
|
||
container_of(ptp, struct rnpgbe_adapter, ptp_clock_ops);
|
||
unsigned long flags;
|
||
u32 sec, nsec;
|
||
u32 quotient, reminder;
|
||
int neg_adj = 0;
|
||
|
||
if (delta < 0) {
|
||
neg_adj = 1;
|
||
delta = -delta;
|
||
}
|
||
|
||
if (delta == 0)
|
||
return 0;
|
||
|
||
quotient = div_u64_rem(delta, 1000000000ULL, &reminder);
|
||
sec = quotient;
|
||
nsec = reminder;
|
||
|
||
spin_lock_irqsave(&pf->ptp_lock, flags);
|
||
pf->hwts_ops->adjust_systime(pf->ptp_addr, sec, nsec, neg_adj,
|
||
pf->gmac4);
|
||
spin_unlock_irqrestore(&pf->ptp_lock, flags);
|
||
|
||
return 0;
|
||
}
|
||
|
||
static int rnpgbe_ptp_gettime(struct ptp_clock_info *ptp, struct timespec64 *ts)
|
||
{
|
||
struct rnpgbe_adapter *pf =
|
||
container_of(ptp, struct rnpgbe_adapter, ptp_clock_ops);
|
||
unsigned long flags;
|
||
u64 ns = 0;
|
||
|
||
spin_lock_irqsave(&pf->ptp_lock, flags);
|
||
|
||
pf->hwts_ops->get_systime(pf->ptp_addr, &ns);
|
||
|
||
spin_unlock_irqrestore(&pf->ptp_lock, flags);
|
||
|
||
*ts = ns_to_timespec64(ns);
|
||
|
||
return 0;
|
||
}
|
||
|
||
static int rnpgbe_ptp_settime(struct ptp_clock_info *ptp,
|
||
const struct timespec64 *ts)
|
||
{
|
||
struct rnpgbe_adapter *pf =
|
||
container_of(ptp, struct rnpgbe_adapter, ptp_clock_ops);
|
||
unsigned long flags;
|
||
|
||
spin_lock_irqsave(&pf->ptp_lock, flags);
|
||
pf->hwts_ops->init_systime(pf->ptp_addr, ts->tv_sec, ts->tv_nsec);
|
||
spin_unlock_irqrestore(&pf->ptp_lock, flags);
|
||
|
||
return 0;
|
||
}
|
||
|
||
static int rnpgbe_ptp_feature_enable(struct ptp_clock_info *ptp,
|
||
struct ptp_clock_request *rq, int on)
|
||
{
|
||
return -EOPNOTSUPP;
|
||
}
|
||
|
||
int rnpgbe_ptp_get_ts_config(struct rnpgbe_adapter *pf, struct ifreq *ifr)
|
||
{
|
||
struct hwtstamp_config *config = &pf->tstamp_config;
|
||
|
||
return copy_to_user(ifr->ifr_data, config, sizeof(*config)) ? -EFAULT :
|
||
0;
|
||
}
|
||
|
||
static int rnpgbe_ptp_setup_ptp(struct rnpgbe_adapter *pf, u32 value)
|
||
{
|
||
u32 sec_inc = 0;
|
||
u64 temp = 0;
|
||
struct timespec64 now;
|
||
|
||
/*For now just use extrnal clock(the kernel-system clock)*/
|
||
/* 1.Mask the Timestamp Trigger interrupt */
|
||
/* 2.enable time stamping */
|
||
/* 2.1 clear all bytes about time ctrl reg*/
|
||
|
||
pf->hwts_ops->config_hw_tstamping(pf->ptp_addr, value);
|
||
/* 3.Program the PTPclock frequency */
|
||
/* program Sub Second Increment reg
|
||
* we use kernel-system clock
|
||
*/
|
||
pf->hwts_ops->config_sub_second_increment(pf->ptp_addr,
|
||
pf->clk_ptp_rate, pf->gmac4, &sec_inc);
|
||
/* 4.If use fine correction approash then,
|
||
* Program MAC_Timestamp_Addend register
|
||
*/
|
||
if (sec_inc == 0) {
|
||
printk(KERN_DEBUG "%s:%d the sec_inc is zero this is a bug\n",
|
||
__func__, __LINE__);
|
||
return -EFAULT;
|
||
}
|
||
temp = div_u64(1000000000ULL, sec_inc);
|
||
/* Store sub second increment and flags for later use */
|
||
pf->sub_second_inc = sec_inc;
|
||
pf->systime_flags = value;
|
||
/* calculate default added value:
|
||
* formula is :
|
||
* addend = (2^32)/freq_div_ratio;
|
||
* where, freq_div_ratio = 1e9ns/sec_inc
|
||
*/
|
||
temp = (u64)(temp << 32);
|
||
|
||
if (pf->clk_ptp_rate == 0) {
|
||
pf->clk_ptp_rate = 1000;
|
||
printk(KERN_DEBUG "%s:%d clk_ptp_rate is zero\n", __func__,
|
||
__LINE__);
|
||
}
|
||
|
||
pf->default_addend = div_u64(temp, pf->clk_ptp_rate);
|
||
|
||
pf->hwts_ops->config_addend(pf->ptp_addr, pf->default_addend);
|
||
/* 5.Poll wait for the TCR Update Addend Register*/
|
||
/* 6.enabled Fine Update method */
|
||
/* 7.program the second and nanosecond register*/
|
||
/*TODO If we need to enable one-step timestamp */
|
||
|
||
/* initialize system time */
|
||
ktime_get_real_ts64(&now);
|
||
|
||
/* lower 32 bits of tv_sec are safe until y2106 */
|
||
pf->hwts_ops->init_systime(pf->ptp_addr, (u32)now.tv_sec, now.tv_nsec);
|
||
|
||
return 0;
|
||
}
|
||
|
||
int rnpgbe_ptp_set_ts_config(struct rnpgbe_adapter *pf, struct ifreq *ifr)
|
||
{
|
||
struct hwtstamp_config config;
|
||
u32 ptp_v2 = 0;
|
||
u32 tstamp_all = 0;
|
||
u32 ptp_over_ipv4_udp = 0;
|
||
u32 ptp_over_ipv6_udp = 0;
|
||
u32 ptp_over_ethernet = 0;
|
||
u32 snap_type_sel = 0;
|
||
u32 ts_master_en = 0;
|
||
u32 value = 0;
|
||
s32 ret = -1;
|
||
|
||
if (!(pf->flags2 & RNP_FLAG2_PTP_ENABLED)) {
|
||
pci_alert(pf->pdev, "No support for HW time stamping\n");
|
||
pf->ptp_tx_en = 0;
|
||
pf->ptp_tx_en = 0;
|
||
|
||
return -EOPNOTSUPP;
|
||
}
|
||
|
||
if (copy_from_user(&config, ifr->ifr_data, sizeof(config)))
|
||
return -EFAULT;
|
||
|
||
netdev_info(pf->netdev,
|
||
"%s config flags:0x%x, tx_type:0x%x, rx_filter:0x%x\n",
|
||
__func__, config.flags, config.tx_type, config.rx_filter);
|
||
/* reserved for future extensions */
|
||
if (config.flags)
|
||
return -EINVAL;
|
||
|
||
if (config.tx_type != HWTSTAMP_TX_OFF &&
|
||
config.tx_type != HWTSTAMP_TX_ON)
|
||
return -ERANGE;
|
||
|
||
switch (config.rx_filter) {
|
||
case HWTSTAMP_FILTER_NONE:
|
||
/* time stamp no incoming packet at all */
|
||
config.rx_filter = HWTSTAMP_FILTER_NONE;
|
||
break;
|
||
|
||
case HWTSTAMP_FILTER_PTP_V1_L4_EVENT:
|
||
/* PTP v1, UDP, any kind of event packet */
|
||
config.rx_filter = HWTSTAMP_FILTER_PTP_V1_L4_EVENT;
|
||
/* 'mac' hardware can support Sync, Pdelay_Req and
|
||
* Pdelay_resp by setting bit14 and bits17/16 to 01
|
||
* This leaves Delay_Req timestamps out.
|
||
* Enable all events *and* general purpose message
|
||
* timestamping
|
||
*/
|
||
snap_type_sel = RNP_PTP_TCR_SNAPTYPSEL_1;
|
||
ptp_over_ipv4_udp = RNP_PTP_TCR_TSIPV4ENA;
|
||
ptp_over_ipv6_udp = RNP_PTP_TCR_TSIPV6ENA;
|
||
break;
|
||
|
||
case HWTSTAMP_FILTER_PTP_V1_L4_SYNC:
|
||
/* PTP v1, UDP, Sync packet */
|
||
config.rx_filter = HWTSTAMP_FILTER_PTP_V1_L4_SYNC;
|
||
/* take time stamp for SYNC messages only */
|
||
|
||
ptp_over_ipv4_udp = RNP_PTP_TCR_TSIPV4ENA;
|
||
ptp_over_ipv6_udp = RNP_PTP_TCR_TSIPV6ENA;
|
||
break;
|
||
|
||
case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ:
|
||
/* PTP v1, UDP, Delay_req packet */
|
||
config.rx_filter = HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ;
|
||
/* take time stamp for Delay_Req messages only */
|
||
ts_master_en = RNP_PTP_TCR_TSMSTRENA;
|
||
|
||
ptp_over_ipv4_udp = RNP_PTP_TCR_TSIPV4ENA;
|
||
ptp_over_ipv6_udp = RNP_PTP_TCR_TSIPV6ENA;
|
||
break;
|
||
|
||
case HWTSTAMP_FILTER_PTP_V2_L4_EVENT:
|
||
/* PTP v2, UDP, any kind of event packet */
|
||
config.rx_filter = HWTSTAMP_FILTER_PTP_V2_L4_EVENT;
|
||
ptp_v2 = RNP_PTP_TCR_TSVER2ENA;
|
||
|
||
/* take time stamp for all event messages */
|
||
snap_type_sel = RNP_PTP_TCR_SNAPTYPSEL_1;
|
||
|
||
ptp_over_ipv4_udp = RNP_PTP_TCR_TSIPV4ENA;
|
||
ptp_over_ipv6_udp = RNP_PTP_TCR_TSIPV6ENA;
|
||
break;
|
||
|
||
case HWTSTAMP_FILTER_PTP_V2_L4_SYNC:
|
||
/* PTP v2, UDP, Sync packet */
|
||
config.rx_filter = HWTSTAMP_FILTER_PTP_V2_L4_SYNC;
|
||
ptp_v2 = RNP_PTP_TCR_TSVER2ENA;
|
||
/* take time stamp for SYNC messages only */
|
||
ptp_over_ipv4_udp = RNP_PTP_TCR_TSIPV4ENA;
|
||
ptp_over_ipv6_udp = RNP_PTP_TCR_TSIPV6ENA;
|
||
break;
|
||
|
||
case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ:
|
||
/* PTP v2, UDP, Delay_req packet */
|
||
config.rx_filter = HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ;
|
||
ptp_v2 = RNP_PTP_TCR_TSVER2ENA;
|
||
/* take time stamp for Delay_Req messages only */
|
||
ts_master_en = RNP_PTP_TCR_TSMSTRENA;
|
||
ptp_over_ipv4_udp = RNP_PTP_TCR_TSIPV4ENA;
|
||
ptp_over_ipv6_udp = RNP_PTP_TCR_TSIPV6ENA;
|
||
break;
|
||
|
||
case HWTSTAMP_FILTER_PTP_V2_EVENT:
|
||
/* PTP v2/802.AS1 any layer, any kind of event packet */
|
||
config.rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT;
|
||
ptp_v2 = RNP_PTP_TCR_TSVER2ENA;
|
||
snap_type_sel = RNP_PTP_TCR_SNAPTYPSEL_1;
|
||
ptp_over_ipv4_udp = RNP_PTP_TCR_TSIPV4ENA;
|
||
ptp_over_ipv6_udp = RNP_PTP_TCR_TSIPV6ENA;
|
||
ptp_over_ethernet = RNP_PTP_TCR_TSIPENA;
|
||
break;
|
||
|
||
case HWTSTAMP_FILTER_PTP_V2_SYNC:
|
||
/* PTP v2/802.AS1, any layer, Sync packet */
|
||
config.rx_filter = HWTSTAMP_FILTER_PTP_V2_SYNC;
|
||
ptp_v2 = RNP_PTP_TCR_TSVER2ENA;
|
||
/* take time stamp for SYNC messages only */
|
||
ptp_over_ipv4_udp = RNP_PTP_TCR_TSIPV4ENA;
|
||
ptp_over_ipv6_udp = RNP_PTP_TCR_TSIPV6ENA;
|
||
ptp_over_ethernet = RNP_PTP_TCR_TSIPENA;
|
||
break;
|
||
|
||
case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ:
|
||
/* PTP v2/802.AS1, any layer, Delay_req packet */
|
||
config.rx_filter = HWTSTAMP_FILTER_PTP_V2_DELAY_REQ;
|
||
ptp_v2 = RNP_PTP_TCR_TSVER2ENA;
|
||
/* take time stamp for Delay_Req messages only */
|
||
ts_master_en = RNP_PTP_TCR_TSMSTRENA;
|
||
|
||
ptp_over_ipv4_udp = RNP_PTP_TCR_TSIPV4ENA;
|
||
ptp_over_ipv6_udp = RNP_PTP_TCR_TSIPV6ENA;
|
||
ptp_over_ethernet = RNP_PTP_TCR_TSIPENA;
|
||
break;
|
||
|
||
#ifdef HWTSTAMP_FILTER_NTP_ALL
|
||
case HWTSTAMP_FILTER_NTP_ALL:
|
||
#endif
|
||
case HWTSTAMP_FILTER_ALL:
|
||
/* time stamp any incoming packet */
|
||
config.rx_filter = HWTSTAMP_FILTER_ALL;
|
||
tstamp_all = RNP_PTP_TCR_TSENALL;
|
||
break;
|
||
|
||
default:
|
||
return -ERANGE;
|
||
}
|
||
|
||
pf->ptp_rx_en = ((config.rx_filter == HWTSTAMP_FILTER_NONE) ? 0 : 1);
|
||
pf->ptp_tx_en = config.tx_type == HWTSTAMP_TX_ON;
|
||
|
||
netdev_info(pf->netdev,
|
||
"ptp config rx filter 0x%.2x tx_type 0x%.2x rx_en[%d] tx_en[%d]\n",
|
||
config.rx_filter, config.tx_type, pf->ptp_rx_en, pf->ptp_tx_en);
|
||
if (!pf->ptp_rx_en && !pf->ptp_tx_en) {
|
||
/*rx and tx is not use hardware ts so clear the ptp register */
|
||
pf->hwts_ops->config_hw_tstamping(pf->ptp_addr, 0);
|
||
} else {
|
||
value = (RNP_PTP_TCR_TSENA | RNP_PTP_TCR_TSCFUPDT |
|
||
RNP_PTP_TCR_TSCTRLSSR | tstamp_all | ptp_v2 |
|
||
ptp_over_ethernet | ptp_over_ipv6_udp |
|
||
ptp_over_ipv4_udp | ts_master_en | snap_type_sel);
|
||
|
||
ret = rnpgbe_ptp_setup_ptp(pf, value);
|
||
if (ret < 0)
|
||
return ret;
|
||
}
|
||
pf->ptp_config_value = value;
|
||
memcpy(&pf->tstamp_config, &config, sizeof(config));
|
||
|
||
return copy_to_user(ifr->ifr_data, &config, sizeof(config)) ? -EFAULT :
|
||
0;
|
||
}
|
||
|
||
/* structure describing a PTP hardware clock */
|
||
static struct ptp_clock_info rnpgbe_ptp_clock_ops = {
|
||
.owner = THIS_MODULE,
|
||
.name = "rnp ptp",
|
||
.max_adj = 50000000,
|
||
.n_alarm = 0,
|
||
.n_ext_ts = 0,
|
||
.n_per_out = 0, /* will be overwritten in stmmac_ptp_register */
|
||
.n_pins = 0, /*should be 0 if not set*/
|
||
.adjfine = rnpgbe_ptp_adjfreq,
|
||
.adjtime = rnpgbe_ptp_adjtime,
|
||
.gettime64 = rnpgbe_ptp_gettime,
|
||
.settime64 = rnpgbe_ptp_settime,
|
||
.enable = rnpgbe_ptp_feature_enable,
|
||
};
|
||
|
||
int rnpgbe_ptp_register(struct rnpgbe_adapter *pf)
|
||
{
|
||
pf->hwts_ops = &mac_ptp;
|
||
|
||
pf->ptp_tx_en = 0;
|
||
pf->ptp_rx_en = 0;
|
||
|
||
spin_lock_init(&pf->ptp_lock);
|
||
pf->flags2 |= RNP_FLAG2_PTP_ENABLED;
|
||
pf->ptp_clock_ops = rnpgbe_ptp_clock_ops;
|
||
|
||
/* default mac clock rate is 50Mhz */
|
||
pf->clk_ptp_rate = 50000000;
|
||
if (!pf->pdev)
|
||
printk(KERN_DEBUG "pdev dev is null\n");
|
||
|
||
pf->ptp_clock = ptp_clock_register(&pf->ptp_clock_ops, &pf->pdev->dev);
|
||
if (!pf->ptp_clock)
|
||
pci_err(pf->pdev, "ptp clock register failed\n");
|
||
|
||
if (IS_ERR(pf->ptp_clock)) {
|
||
pci_err(pf->pdev, "ptp_clock_register failed\n");
|
||
pf->ptp_clock = NULL;
|
||
} else {
|
||
pci_info(pf->pdev, "registered PTP clock\n");
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
void rnpgbe_ptp_unregister(struct rnpgbe_adapter *pf)
|
||
{
|
||
/*1. stop the ptp module*/
|
||
if (pf->ptp_clock) {
|
||
ptp_clock_unregister(pf->ptp_clock);
|
||
pf->ptp_clock = NULL;
|
||
pr_debug("Removed PTP HW clock successfully on %s\n",
|
||
"rnpgbe_ptp");
|
||
}
|
||
}
|
||
|
||
void rnpgbe_tx_hwtstamp_work(struct work_struct *work)
|
||
{
|
||
struct rnpgbe_adapter *adapter =
|
||
container_of(work, struct rnpgbe_adapter, tx_hwtstamp_work);
|
||
#ifdef FW_UART_SHOW_TSTAMPS
|
||
struct rnpgbe_hw *hw = &adapter->hw;
|
||
#endif
|
||
void __iomem *ioaddr = adapter->hw.hw_addr;
|
||
|
||
/* 1. read port belone timestatmp status reg */
|
||
/* 2. status enabled read nsec and sec reg*/
|
||
/* 3. */
|
||
u64 nanosec = 0, sec = 0;
|
||
|
||
if (!adapter->ptp_tx_skb) {
|
||
clear_bit_unlock(__RNP_PTP_TX_IN_PROGRESS, &adapter->state);
|
||
return;
|
||
}
|
||
|
||
if (rnpgbe_rd_reg(ioaddr + RNP_ETH_PTP_TX_TSVALUE_STATUS(0)) & 0x01) {
|
||
struct sk_buff *skb = adapter->ptp_tx_skb;
|
||
struct skb_shared_hwtstamps shhwtstamps;
|
||
u64 txstmp = 0;
|
||
/* read and add nsec, sec turn to nsec*/
|
||
|
||
nanosec = rnpgbe_rd_reg(ioaddr + RNP_ETH_PTP_TX_LTIMES(0));
|
||
sec = rnpgbe_rd_reg(ioaddr + RNP_ETH_PTP_TX_HTIMES(0));
|
||
/* when we read the timestamp finish need to notice the hardware
|
||
* that the timestamp need to update via set tx_hwts_clear-reg
|
||
* from high to low
|
||
*/
|
||
rnpgbe_wr_reg(ioaddr + RNP_ETH_PTP_TX_CLEAR(0),
|
||
PTP_GET_TX_HWTS_FINISH);
|
||
rnpgbe_wr_reg(ioaddr + RNP_ETH_PTP_TX_CLEAR(0),
|
||
PTP_GET_TX_HWTS_UPDATE);
|
||
|
||
txstmp = nanosec & PTP_HWTX_TIME_VALUE_MASK;
|
||
txstmp += (sec & PTP_HWTX_TIME_VALUE_MASK) * 1000000000ULL;
|
||
|
||
/* Clear the global tx_hwtstamp_skb pointer and force writes
|
||
* prior to notifying the stack of a Tx timestamp.
|
||
*/
|
||
memset(&shhwtstamps, 0, sizeof(shhwtstamps));
|
||
shhwtstamps.hwtstamp = ns_to_ktime(txstmp);
|
||
adapter->ptp_tx_skb = NULL;
|
||
/* force write prior to skb_tstamp_tx
|
||
* because the xmit will re used the point to store ptp skb
|
||
*/
|
||
wmb();
|
||
|
||
skb_tstamp_tx(skb, &shhwtstamps);
|
||
dev_consume_skb_any(skb);
|
||
clear_bit_unlock(__RNP_PTP_TX_IN_PROGRESS, &adapter->state);
|
||
/* send tstamps to hw */
|
||
#ifdef FW_UART_SHOW_TSTAMPS
|
||
rnpgbe_mbx_tstamps_show(hw, sec, nanosec);
|
||
#endif
|
||
} else if (time_after(jiffies,
|
||
adapter->tx_hwtstamp_start +
|
||
adapter->tx_timeout_factor * HZ)) {
|
||
/* this function will mark the skb drop*/
|
||
if (adapter->ptp_tx_skb)
|
||
dev_kfree_skb_any(adapter->ptp_tx_skb);
|
||
adapter->ptp_tx_skb = NULL;
|
||
adapter->tx_hwtstamp_timeouts++;
|
||
clear_bit_unlock(__RNP_PTP_TX_IN_PROGRESS, &adapter->state);
|
||
netdev_warn(adapter->netdev, "clearing Tx timestamp hang\n");
|
||
} else {
|
||
/* reschedule to check later */
|
||
schedule_work(&adapter->tx_hwtstamp_work);
|
||
}
|
||
}
|
||
|
||
void rnpgbe_ptp_get_rx_hwstamp(struct rnpgbe_adapter *adapter,
|
||
union rnpgbe_rx_desc *desc, struct sk_buff *skb)
|
||
{
|
||
u64 ns = 0;
|
||
u64 tsvalueh = 0, tsvaluel = 0;
|
||
struct skb_shared_hwtstamps *hwtstamps = NULL;
|
||
|
||
if (!skb || !adapter->ptp_rx_en) {
|
||
netdev_dbg(adapter->netdev,
|
||
"hwstamp skb is null or rx_en iszero %u\n",
|
||
adapter->ptp_rx_en);
|
||
return;
|
||
}
|
||
|
||
if (likely(!(desc->wb.cmd & RNP_RXD_STAT_PTP)))
|
||
return;
|
||
hwtstamps = skb_hwtstamps(skb);
|
||
/* because of rx hwstamp store before the mac head
|
||
* skb->head and skb->data is point to same location when call alloc_skb
|
||
* so we must move 16 bytes the skb->data to the mac head location
|
||
* but for the head point if we need move the skb->head need to be diss
|
||
*/
|
||
/* low8bytes is null high8bytes is timestamp
|
||
* high32bit is seconds low32bits is nanoseconds
|
||
*/
|
||
skb_copy_from_linear_data_offset(skb, RNP_RX_TIME_RESERVE, &tsvalueh,
|
||
RNP_RX_SEC_SIZE);
|
||
skb_copy_from_linear_data_offset(skb,
|
||
RNP_RX_TIME_RESERVE + RNP_RX_SEC_SIZE,
|
||
&tsvaluel, RNP_RX_NANOSEC_SIZE);
|
||
skb_pull(skb, RNP_RX_HWTS_OFFSET);
|
||
tsvalueh = ntohl(tsvalueh);
|
||
tsvaluel = ntohl(tsvaluel);
|
||
|
||
ns = tsvaluel & RNP_RX_NSEC_MASK;
|
||
ns += ((tsvalueh & RNP_RX_SEC_MASK) * 1000000000ULL);
|
||
|
||
netdev_dbg(adapter->netdev,
|
||
"ptp get hardware ts-sec %llu ts-nanosec %llu\n", tsvalueh,
|
||
tsvaluel);
|
||
hwtstamps->hwtstamp = ns_to_ktime(ns);
|
||
}
|
||
|
||
void rnpgbe_ptp_reset(struct rnpgbe_adapter *adapter)
|
||
{
|
||
rnpgbe_ptp_setup_ptp(adapter, adapter->ptp_config_value);
|
||
}
|