2026-01-29 22:25:33 +08:00

805 lines
23 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 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/iopoll.h>
#include <linux/delay.h>
#include <linux/clk.h>
#include "rnpm.h"
#include "rnpm_regs.h"
#include "rnpm_ptp.h"
/* PTP and HW Timer ops */
static void config_hw_tstamping(void __iomem *ioaddr, u8 port, u32 data)
{
rnpm_wr_reg(ioaddr + RNPM_MAC_TS_CTRL(port), data);
}
static void config_sub_second_increment(void __iomem *ioaddr, u8 port,
u32 ptp_clock, u32 *ssinc)
{
u32 value = rnpm_rd_reg(ioaddr + RNPM_MAC_TS_CTRL(port));
unsigned long data;
u32 reg_value;
/* 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 (ptp_clock == 0) {
ptp_dbg("%s:%dthis is a bug that the syskernel clock is zero\n",
__func__, __LINE__);
return;
}
if (value & RNPM_PTP_TCR_TSCFUPDT)
data = (2000000000ULL / ptp_clock);
else
data = (1000000000ULL / ptp_clock);
/* 0.465ns accuracy */
if (!(value & RNPM_PTP_TCR_TSCTRLSSR))
data = (data * 1000) / 465;
data &= RNPM_PTP_SSIR_SSINC_MASK;
reg_value = data;
reg_value <<= RNPM_PTP_SSIR_SSINC_SHIFT;
rnpm_wr_reg(ioaddr + RNPM_MAC_SUB_SECOND_INCREMENT(port), reg_value);
if (ssinc)
*ssinc = data;
}
static int config_addend(void __iomem *ioaddr, u8 port, u32 addend)
{
u32 value;
int limit;
rnpm_wr_reg(ioaddr + RNPM_MAC_TS_ADDEND(port), addend);
/* issue command to update the addend value */
value = rnpm_rd_reg(ioaddr + RNPM_MAC_TS_CTRL(port));
value |= RNPM_PTP_TCR_TSADDREG;
rnpm_wr_reg(ioaddr + RNPM_MAC_TS_CTRL(port), value);
/* wait for present addend update to complete */
limit = 10;
while (limit--) {
if (!(rnpm_rd_reg(ioaddr + RNPM_MAC_TS_CTRL(port)) &
RNPM_PTP_TCR_TSADDREG))
break;
mdelay(10);
}
if (limit < 0)
return -EBUSY;
return 0;
}
static int init_systime(void __iomem *ioaddr, u8 port, u32 sec, u32 nsec)
{
u32 value;
u32 target;
int timeout = 0;
//printk("init systime: %x\n", sec);
rnpm_wr_reg(ioaddr + RNPM_MAC_SYS_TIME_SEC_UPDATE(port), sec);
rnpm_wr_reg(ioaddr + RNPM_MAC_SYS_TIME_NANOSEC_UPDATE(port), nsec);
/* issue command to initialize the system time value */
value = rnpm_rd_reg(ioaddr + RNPM_MAC_TS_CTRL(port));
value |= RNPM_PTP_TCR_TSINIT;
rnpm_wr_reg(ioaddr + RNPM_MAC_TS_CTRL(port), value);
while (timeout <= 20) {
target = rnpm_rd_reg(ioaddr + RNPM_MAC_TS_CTRL(port));
if (!(value & RNPM_PTP_TCR_TSINIT))
break;
usleep_range(5000, 10000);
timeout++;
}
if (timeout < 20)
return 0;
else
return -1;
/* wait for present system time initialize to complete */
//return readl_poll_timeout(ioaddr + RNPM_MAC_TS_CTRL(port), value,
// !(value & RNPM_PTP_TCR_TSINIT),
// 10000, 100000);
}
static void get_systime(void __iomem *ioaddr, u8 port, u64 *systime)
{
u64 ns;
/* Get the TSSS value */
ns = rnpm_rd_reg(ioaddr + RNPM_MAC_SYS_TIME_NANOSEC_CFG(port));
/* Get the TSS and convert sec time value to nanosecond */
ns += rnpm_rd_reg(ioaddr + RNPM_MAC_SYS_TIME_SEC_CFG(port)) *
1000000000ULL;
if (systime)
*systime = ns;
}
static void config_mac_interrupt_enable(void __iomem *ioaddr, u8 port, bool on)
{
rnpm_wr_reg(ioaddr + RNPM_MAC_INTERRUPT_ENABLE(port), on);
}
struct rnpm_hwtimestamp {
void (*config_hw_tstamping)(void __iomem *ioaddr, u8 port, u32 data);
void (*config_sub_second_increment)(void __iomem *ioaddr, u8 port,
u32 ptp_clock, u32 *ssinc);
void (*config_mac_irq_enable)(void __iomem *ioaddr, u8 port, bool on);
int (*init_systime)(void __iomem *ioaddr, u8 port, u32 sec, u32 nsec);
int (*config_addend)(void __iomem *ioaddr, u8 port, u32 addend);
int (*adjust_systime)(void __iomem *ioaddr, u8 port, u32 sec, u32 nsec,
int add_sub);
void (*get_systime)(void __iomem *ioaddr, u8 port, u64 *systime);
};
static int adjust_systime(void __iomem *ioaddr, u8 port, u32 sec, u32 nsec,
int add_sub)
{
u32 value;
int limit;
if (add_sub) {
/* If the new sec value needs to be subtracted with
* the system time, then RNPM_MAC_STSUR reg should be
* programmed with (2^32 <new_sec_value>)
*/
sec = -sec;
value = rnpm_rd_reg(ioaddr + RNPM_MAC_TS_CTRL(port));
if (value & RNPM_PTP_TCR_TSCTRLSSR)
nsec = (PTP_DIGITAL_ROLLOVER_MODE - nsec);
else
nsec = (PTP_BINARY_ROLLOVER_MODE - nsec);
}
//printk("adjust %x\n", sec);
rnpm_wr_reg(ioaddr + RNPM_MAC_SYS_TIME_SEC_UPDATE(port), sec);
value = (add_sub << PTP_STNSUR_ADDSUB_SHIFT) | nsec;
rnpm_wr_reg(ioaddr + RNPM_MAC_SYS_TIME_NANOSEC_UPDATE(port), value);
/* issue command to initialize the system time value */
value = rnpm_rd_reg(ioaddr + RNPM_MAC_TS_CTRL(port));
value |= RNPM_PTP_TCR_TSUPDT;
rnpm_wr_reg(ioaddr + RNPM_MAC_TS_CTRL(port), value);
/* wait for present system time adjust/update to complete */
limit = 10;
while (limit--) {
if (!(rnpm_rd_reg(ioaddr + RNPM_MAC_TS_CTRL(port)) &
RNPM_PTP_TCR_TSUPDT))
break;
mdelay(10);
}
if (limit < 0)
return -EBUSY;
return 0;
}
const struct rnpm_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 rnpm_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm)
{
struct rnpm_adapter *pf =
container_of(ptp, struct rnpm_adapter, ptp_clock_ops);
unsigned long flags;
u32 diff, addend;
int neg_adj = 0;
u64 adj;
u8 port = pf->port;
if (pf == NULL) {
ptp_dbg("adapter_of contail is null\n");
return 0;
}
if (scaled_ppm < 0) {
neg_adj = 1;
scaled_ppm = -scaled_ppm;
}
addend = pf->default_addend;
adj = addend;
adj *= (u64)scaled_ppm;
diff = div64_u64(adj, 1000000ULL << 16);
addend = neg_adj ? (addend - diff) : (addend + diff);
spin_lock_irqsave(&pf->ptp_lock, flags);
pf->hwts_ops->config_addend(pf->hw.hw_addr, port, addend);
spin_unlock_irqrestore(&pf->ptp_lock, flags);
return 0;
}
static int rnpm_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta)
{
struct rnpm_adapter *pf =
container_of(ptp, struct rnpm_adapter, ptp_clock_ops);
unsigned long flags;
u32 sec, nsec;
u32 quotient, reminder;
int neg_adj = 0;
u8 port = pf->port;
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->hw.hw_addr, port, sec, nsec, neg_adj);
spin_unlock_irqrestore(&pf->ptp_lock, flags);
return 0;
}
static int rnpm_ptp_gettime(struct ptp_clock_info *ptp, struct timespec64 *ts)
{
struct rnpm_adapter *pf =
container_of(ptp, struct rnpm_adapter, ptp_clock_ops);
unsigned long flags;
u64 ns = 0;
u8 port = pf->port;
spin_lock_irqsave(&pf->ptp_lock, flags);
pf->hwts_ops->get_systime(pf->hw.hw_addr, port, &ns);
spin_unlock_irqrestore(&pf->ptp_lock, flags);
*ts = ns_to_timespec64(ns);
return 0;
}
static int rnpm_ptp_settime(struct ptp_clock_info *ptp,
const struct timespec64 *ts)
{
struct rnpm_adapter *pf =
container_of(ptp, struct rnpm_adapter, ptp_clock_ops);
unsigned long flags;
u8 port = pf->port;
spin_lock_irqsave(&pf->ptp_lock, flags);
pf->hwts_ops->init_systime(pf->hw.hw_addr, port, ts->tv_sec,
ts->tv_nsec);
spin_unlock_irqrestore(&pf->ptp_lock, flags);
return 0;
}
static int rnpm_ptp_feature_enable(struct ptp_clock_info *ptp,
struct ptp_clock_request *rq, int on)
{
/*TODO add support for enable the option 1588 feature PPS Auxiliary */
return -EOPNOTSUPP;
}
int rnpm_ptp_get_ts_config(struct rnpm_adapter *pf, struct ifreq *ifr)
{
struct hwtstamp_config *config = &pf->tstamp_config;
return copy_to_user(ifr->ifr_data, config, sizeof(*config)) ? -EFAULT :
0;
}
int rnpm_ptp_setup_ptp(struct rnpm_adapter *pf, u32 value)
{
u32 sec_inc = 0;
u64 temp = 0;
struct timespec64 now;
u8 port = pf->port;
/*For now just use extrnal clock(the kernel-system clock)*/
//value |= RNPM_PTP_TCR_ESTI;
/* 1.Mask the Timestamp Trigger interrupt */
pf->hwts_ops->config_mac_irq_enable(pf->hw.hw_addr, port, false);
/* 2.enable time stamping */
/* 2.1 clear all bytes about time ctrl reg*/
pf->hwts_ops->config_hw_tstamping(pf->hw.hw_addr, port, 0);
pf->hwts_ops->config_hw_tstamping(pf->hw.hw_addr, port, value);
/* 3.Program the PTPclock frequency */
/* program Sub Second Increment reg
* we use kernel-system clock
*/
pf->hwts_ops->config_sub_second_increment(pf->hw.hw_addr, port,
pf->clk_ptp_rate, &sec_inc);
/* 4.If use fine correction approash then,
* Program MAC_Timestamp_Addend register
*/
if (sec_inc == 0) {
ptp_dbg("%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;
ptp_dbg("%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->hw.hw_addr, port, 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->hw.hw_addr, port, (u32)now.tv_sec,
now.tv_nsec);
pf->hwts_ops->config_mac_irq_enable(pf->hw.hw_addr, port, true);
return 0;
}
int rnpm_ptp_set_ts_config(struct rnpm_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 ts_event_en = 0;
u32 value = 0;
s32 ret = -1;
u8 port = pf->port;
if (!(pf->flags2 & RNPM_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 = RNPM_PTP_TCR_SNAPTYPSEL_1;
ptp_over_ipv4_udp = RNPM_PTP_TCR_TSIPV4ENA;
ptp_over_ipv6_udp = RNPM_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 */
ts_event_en = RNPM_PTP_TCR_TSEVNTENA;
ptp_over_ipv4_udp = RNPM_PTP_TCR_TSIPV4ENA;
ptp_over_ipv6_udp = RNPM_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 = RNPM_PTP_TCR_TSMSTRENA;
ts_event_en = RNPM_PTP_TCR_TSEVNTENA;
ptp_over_ipv4_udp = RNPM_PTP_TCR_TSIPV4ENA;
ptp_over_ipv6_udp = RNPM_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 = RNPM_PTP_TCR_TSVER2ENA;
/* take time stamp for all event messages */
snap_type_sel = RNPM_PTP_TCR_SNAPTYPSEL_1;
ptp_over_ipv4_udp = RNPM_PTP_TCR_TSIPV4ENA;
ptp_over_ipv6_udp = RNPM_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 = RNPM_PTP_TCR_TSVER2ENA;
/* take time stamp for SYNC messages only */
ts_event_en = RNPM_PTP_TCR_TSEVNTENA;
ptp_over_ipv4_udp = RNPM_PTP_TCR_TSIPV4ENA;
ptp_over_ipv6_udp = RNPM_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 = RNPM_PTP_TCR_TSVER2ENA;
/* take time stamp for Delay_Req messages only */
ts_master_en = RNPM_PTP_TCR_TSMSTRENA;
ts_event_en = RNPM_PTP_TCR_TSEVNTENA;
ptp_over_ipv4_udp = RNPM_PTP_TCR_TSIPV4ENA;
ptp_over_ipv6_udp = RNPM_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 = RNPM_PTP_TCR_TSVER2ENA;
snap_type_sel = RNPM_PTP_TCR_SNAPTYPSEL_1;
// ts_event_en = RNPM_PTP_TCR_TSEVNTENA;
ptp_over_ipv4_udp = RNPM_PTP_TCR_TSIPV4ENA;
ptp_over_ipv6_udp = RNPM_PTP_TCR_TSIPV6ENA;
ptp_over_ethernet = RNPM_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 = RNPM_PTP_TCR_TSVER2ENA;
/* take time stamp for SYNC messages only */
ts_event_en = RNPM_PTP_TCR_TSEVNTENA;
ptp_over_ipv4_udp = RNPM_PTP_TCR_TSIPV4ENA;
ptp_over_ipv6_udp = RNPM_PTP_TCR_TSIPV6ENA;
ptp_over_ethernet = RNPM_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 = RNPM_PTP_TCR_TSVER2ENA;
/* take time stamp for Delay_Req messages only */
ts_master_en = RNPM_PTP_TCR_TSMSTRENA;
ts_event_en = RNPM_PTP_TCR_TSEVNTENA;
ptp_over_ipv4_udp = RNPM_PTP_TCR_TSIPV4ENA;
ptp_over_ipv6_udp = RNPM_PTP_TCR_TSIPV6ENA;
ptp_over_ethernet = RNPM_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 = RNPM_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->hw.hw_addr, port, 0);
else {
value = (RNPM_PTP_TCR_TSENA | RNPM_PTP_TCR_TSCFUPDT |
RNPM_PTP_TCR_TSCTRLSSR | tstamp_all | ptp_v2 |
ptp_over_ethernet | ptp_over_ipv6_udp |
ptp_over_ipv4_udp | ts_event_en | ts_master_en |
snap_type_sel);
ret = rnpm_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 rnpm_ptp_clock_ops = {
.owner = THIS_MODULE,
.name = "rnpm ptp",
.max_adj = 500000000,
.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 = rnpm_ptp_adjfine,
.adjtime = rnpm_ptp_adjtime,
.gettime64 = rnpm_ptp_gettime,
.settime64 = rnpm_ptp_settime,
.enable = rnpm_ptp_feature_enable,
};
int rnpm_ptp_register(struct rnpm_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 |= RNPM_FLAG2_PTP_ENABLED;
pf->ptp_clock_ops = rnpm_ptp_clock_ops;
/*default mac clock rate is 50Mhz */
pf->clk_ptp_rate = 50000000; //50Mhz
if (pf->pdev == NULL)
ptp_dbg("pdev dev is null\n");
pf->ptp_clock = ptp_clock_register(&pf->ptp_clock_ops, &pf->pdev->dev);
if (pf->ptp_clock == NULL)
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 rnpm_ptp_unregister(struct rnpm_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",
"rnpm_ptp");
}
}
#if defined(DEBUG_PTP_HARD_SOFTWAY_RX) || defined(DEBUG_PTP_HARD_SOFTWAY_TX)
static u64 rnpm_get_software_ts(void)
{
struct timespec64 ts;
ktime_get_real_ts64(&ts);
return (ts.tv_nsec + ts.tv_sec * 1000000000ULL);
}
#endif
#if defined(DEBUG_PTP_TX_TIMESTAMP) || defined(DEBUG_PTP_RX_TIMESTAMP)
#define TIME_ZONE_CHINA (8)
char *asctime(const struct tm *timeptr)
{
static const char wday_name[][4] = { "Sun", "Mon", "Tue", "Wed",
"Thu", "Fri", "Sat" };
static const char mon_name[][4] = { "Jan", "Feb", "Mar", "Apr",
"May", "Jun", "Jul", "Aug",
"Sep", "Oct", "Nov", "Dec" };
static char result[26];
sprintf(result, "%.3s %.3s%3d %.2d:%.2d:%.2d %ld\n",
wday_name[timeptr->tm_wday], mon_name[timeptr->tm_mon],
timeptr->tm_mday, timeptr->tm_hour + TIME_ZONE_CHINA,
timeptr->tm_min, timeptr->tm_sec, 1900 + timeptr->tm_year);
return result;
}
static void rnpm_print_human_timestamp(uint64_t ns, uint8_t *direct)
{
struct timespec64 ts;
struct tm tms;
ktime_t ktm = ns_to_ktime(ns);
ts = ktime_to_timespec64(ktm);
//time64_to_tm(ts.tv_sec, ts.tv_nsec / 1000000000ULL, &tms);
//ptp_dbg("[%s] %s ------\n", direct, asctime(&tms));
ptp_dbg("[%s] %s ------\n", direct, ns);
}
#endif
void rnpm_tx_hwtstamp_work(struct work_struct *work)
{
struct rnpm_adapter *adapter =
container_of(work, struct rnpm_adapter, tx_hwtstamp_work);
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;
u8 port = adapter->port;
if (!adapter->ptp_tx_skb)
return;
if (rnpm_rd_reg(ioaddr + RNPM_ETH_PTP_TX_TSVALUE_STATUS(port)) & 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 = rnpm_rd_reg(ioaddr + RNPM_ETH_PTP_TX_LTIMES(port));
sec = rnpm_rd_reg(ioaddr + RNPM_ETH_PTP_TX_HTIMES(port));
/* 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
*/
//printk("port %d call clean ptp %llx %llx\n", port, nanosec, sec);
rnpm_wr_reg(ioaddr + RNPM_ETH_PTP_TX_CLEAR(port),
PTP_GET_TX_HWTS_FINISH);
rnpm_wr_reg(ioaddr + RNPM_ETH_PTP_TX_CLEAR(port),
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;
clear_bit_unlock(__RNPM_PTP_TX_IN_PROGRESS, &adapter->state);
#ifdef DEBUG_PTP_TX_TIMESTAMP
rnpm_print_human_timestamp(txstmp, "TX");
#endif
/* Force memory writes to complete before letting h/w
* know there are new descriptors to fetch. (Only
* applicable for weak-ordered memory model archs,
* such as IA-64).
*/
wmb();
/* force write prior to skb_tstamp_tx
* because the xmit will re used the point to store ptp skb
*/
skb_tstamp_tx(skb, &shhwtstamps);
dev_consume_skb_any(skb);
} 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(__RNPM_PTP_TX_IN_PROGRESS, &adapter->state);
netdev_warn(adapter->netdev, "clearing Tx timestamp hang\n");
} else {
/* reschedule to check later */
#ifdef DEBUG_PTP_HARD_SOFTWAY_TX
struct skb_shared_hwtstamps shhwtstamp;
u64 ns = 0;
ns = rnpm_get_software_ts();
shhwtstamp.hwtstamp = ns_to_ktime(ns);
if (adapter->ptp_tx_skb) {
skb_tstamp_tx(adapter->ptp_tx_skb, &shhwtstamp);
dev_consume_skb_any(adapter->ptp_tx_skb);
adapter->ptp_tx_skb = NULL;
}
#else
schedule_work(&adapter->tx_hwtstamp_work);
#endif
}
}
void rnpm_ptp_get_rx_hwstamp(struct rnpm_adapter *adapter,
union rnpm_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 is zero %u\n",
adapter->ptp_rx_en);
return;
}
#ifdef DEBUG_PTP_HARD_SOFTWAY_RX
ns = rnpm_get_software_ts();
#else
if (likely(!((desc->wb.cmd) & RNPM_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, RNPM_RX_TIME_RESERVE, &tsvalueh,
RNPM_RX_SEC_SIZE);
skb_copy_from_linear_data_offset(
skb, RNPM_RX_TIME_RESERVE + RNPM_RX_SEC_SIZE, &tsvaluel,
RNPM_RX_NANOSEC_SIZE);
skb_pull(skb, RNPM_RX_HWTS_OFFSET);
tsvalueh = ntohl(tsvalueh);
tsvaluel = ntohl(tsvaluel);
ns = tsvaluel & RNPM_RX_NSEC_MASK;
ns += ((tsvalueh & RNPM_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);
#endif
#ifdef DEBUG_PTP_RX_TIMESTAMP
rnpm_print_human_timestamp(ns, "RX");
#endif
}
void rnpm_ptp_reset(struct rnpm_adapter *adapter)
{
rnpm_ptp_setup_ptp(adapter, adapter->ptp_config_value);
}