#include <linux/ptp_classify.h>
#include <linux/clocksource.h>
#include <linux/pci.h>
#include "wx_type.h"
#include "wx_ptp.h"
#include "wx_hw.h"
#define WX_INCVAL_10GB 0xCCCCCC
#define WX_INCVAL_1GB 0x800000
#define WX_INCVAL_100 0xA00000
#define WX_INCVAL_10 0xC7F380
#define WX_INCVAL_EM 0x2000000
#define WX_INCVAL_AML 0xA00000
#define WX_INCVAL_SHIFT_10GB 20
#define WX_INCVAL_SHIFT_1GB 18
#define WX_INCVAL_SHIFT_100 15
#define WX_INCVAL_SHIFT_10 12
#define WX_INCVAL_SHIFT_EM 22
#define WX_INCVAL_SHIFT_AML 21
#define WX_OVERFLOW_PERIOD (HZ * 30)
#define WX_PTP_TX_TIMEOUT (HZ)
#define WX_1588_PPS_WIDTH_EM 120
#define WX_NS_PER_SEC 1000000000ULL
static u64 wx_ptp_timecounter_cyc2time(struct wx *wx, u64 timestamp)
{
unsigned int seq;
u64 ns;
do {
seq = read_seqbegin(&wx->hw_tc_lock);
ns = timecounter_cyc2time(&wx->hw_tc, timestamp);
} while (read_seqretry(&wx->hw_tc_lock, seq));
return ns;
}
static u64 wx_ptp_readtime(struct wx *wx, struct ptp_system_timestamp *sts)
{
u32 timeh1, timeh2, timel;
timeh1 = rd32ptp(wx, WX_TSC_1588_SYSTIMH);
ptp_read_system_prets(sts);
timel = rd32ptp(wx, WX_TSC_1588_SYSTIML);
ptp_read_system_postts(sts);
timeh2 = rd32ptp(wx, WX_TSC_1588_SYSTIMH);
if (timeh1 != timeh2) {
ptp_read_system_prets(sts);
timel = rd32ptp(wx, WX_TSC_1588_SYSTIML);
ptp_read_system_prets(sts);
}
return (u64)timel | (u64)timeh2 << 32;
}
static int wx_ptp_adjfine(struct ptp_clock_info *ptp, long ppb)
{
struct wx *wx = container_of(ptp, struct wx, ptp_caps);
u64 incval, mask;
smp_mb();
incval = READ_ONCE(wx->base_incval);
incval = adjust_by_scaled_ppm(incval, ppb);
mask = (wx->mac.type == wx_mac_em) ? 0x7FFFFFF : 0xFFFFFF;
incval &= mask;
if (wx->mac.type != wx_mac_em)
incval |= 2 << 24;
wr32ptp(wx, WX_TSC_1588_INC, incval);
return 0;
}
static int wx_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta)
{
struct wx *wx = container_of(ptp, struct wx, ptp_caps);
unsigned long flags;
write_seqlock_irqsave(&wx->hw_tc_lock, flags);
timecounter_adjtime(&wx->hw_tc, delta);
write_sequnlock_irqrestore(&wx->hw_tc_lock, flags);
if (wx->ptp_setup_sdp)
wx->ptp_setup_sdp(wx);
return 0;
}
static int wx_ptp_gettimex64(struct ptp_clock_info *ptp,
struct timespec64 *ts,
struct ptp_system_timestamp *sts)
{
struct wx *wx = container_of(ptp, struct wx, ptp_caps);
u64 ns, stamp;
stamp = wx_ptp_readtime(wx, sts);
ns = wx_ptp_timecounter_cyc2time(wx, stamp);
*ts = ns_to_timespec64(ns);
return 0;
}
static int wx_ptp_settime64(struct ptp_clock_info *ptp,
const struct timespec64 *ts)
{
struct wx *wx = container_of(ptp, struct wx, ptp_caps);
unsigned long flags;
u64 ns;
ns = timespec64_to_ns(ts);
write_seqlock_irqsave(&wx->hw_tc_lock, flags);
timecounter_init(&wx->hw_tc, &wx->hw_cc, ns);
write_sequnlock_irqrestore(&wx->hw_tc_lock, flags);
if (wx->ptp_setup_sdp)
wx->ptp_setup_sdp(wx);
return 0;
}
static void wx_ptp_clear_tx_timestamp(struct wx *wx)
{
rd32ptp(wx, WX_TSC_1588_STMPH);
if (wx->ptp_tx_skb) {
dev_kfree_skb_any(wx->ptp_tx_skb);
wx->ptp_tx_skb = NULL;
}
clear_bit_unlock(WX_STATE_PTP_TX_IN_PROGRESS, wx->state);
}
static void wx_ptp_convert_to_hwtstamp(struct wx *wx,
struct skb_shared_hwtstamps *hwtstamp,
u64 timestamp)
{
u64 ns;
ns = wx_ptp_timecounter_cyc2time(wx, timestamp);
hwtstamp->hwtstamp = ns_to_ktime(ns);
}
static void wx_ptp_tx_hwtstamp(struct wx *wx)
{
struct skb_shared_hwtstamps shhwtstamps;
struct sk_buff *skb = wx->ptp_tx_skb;
u64 regval = 0;
regval |= (u64)rd32ptp(wx, WX_TSC_1588_STMPL);
regval |= (u64)rd32ptp(wx, WX_TSC_1588_STMPH) << 32;
wx_ptp_convert_to_hwtstamp(wx, &shhwtstamps, regval);
wx->ptp_tx_skb = NULL;
clear_bit_unlock(WX_STATE_PTP_TX_IN_PROGRESS, wx->state);
skb_tstamp_tx(skb, &shhwtstamps);
dev_kfree_skb_any(skb);
wx->tx_hwtstamp_pkts++;
}
static int wx_ptp_tx_hwtstamp_work(struct wx *wx)
{
u32 tsynctxctl;
if (!wx->ptp_tx_skb) {
wx_ptp_clear_tx_timestamp(wx);
return 0;
}
tsynctxctl = rd32ptp(wx, WX_TSC_1588_CTL);
if (tsynctxctl & WX_TSC_1588_CTL_VALID) {
wx_ptp_tx_hwtstamp(wx);
return 0;
}
return -1;
}
static void wx_ptp_overflow_check(struct wx *wx)
{
bool timeout = time_is_before_jiffies(wx->last_overflow_check +
WX_OVERFLOW_PERIOD);
unsigned long flags;
if (timeout) {
write_seqlock_irqsave(&wx->hw_tc_lock, flags);
timecounter_read(&wx->hw_tc);
write_sequnlock_irqrestore(&wx->hw_tc_lock, flags);
wx->last_overflow_check = jiffies;
}
}
static void wx_ptp_rx_hang(struct wx *wx)
{
struct wx_ring *rx_ring;
unsigned long rx_event;
u32 tsyncrxctl;
int n;
tsyncrxctl = rd32(wx, WX_PSR_1588_CTL);
if (!(tsyncrxctl & WX_PSR_1588_CTL_VALID)) {
wx->last_rx_ptp_check = jiffies;
return;
}
rx_event = wx->last_rx_ptp_check;
for (n = 0; n < wx->num_rx_queues; n++) {
rx_ring = wx->rx_ring[n];
if (time_after(rx_ring->last_rx_timestamp, rx_event))
rx_event = rx_ring->last_rx_timestamp;
}
if (time_is_before_jiffies(rx_event + 5 * HZ)) {
rd32(wx, WX_PSR_1588_STMPH);
wx->last_rx_ptp_check = jiffies;
wx->rx_hwtstamp_cleared++;
dev_warn(&wx->pdev->dev, "clearing RX Timestamp hang");
}
}
static void wx_ptp_tx_hang(struct wx *wx)
{
bool timeout = time_is_before_jiffies(wx->ptp_tx_start +
WX_PTP_TX_TIMEOUT);
if (!wx->ptp_tx_skb)
return;
if (!test_bit(WX_STATE_PTP_TX_IN_PROGRESS, wx->state))
return;
if (timeout) {
wx_ptp_clear_tx_timestamp(wx);
wx->tx_hwtstamp_timeouts++;
dev_warn(&wx->pdev->dev, "clearing Tx timestamp hang\n");
}
}
static long wx_ptp_do_aux_work(struct ptp_clock_info *ptp)
{
struct wx *wx = container_of(ptp, struct wx, ptp_caps);
int ts_done;
ts_done = wx_ptp_tx_hwtstamp_work(wx);
wx_ptp_overflow_check(wx);
if (unlikely(test_bit(WX_FLAG_RX_HWTSTAMP_IN_REGISTER,
wx->flags)))
wx_ptp_rx_hang(wx);
wx_ptp_tx_hang(wx);
return ts_done ? 1 : HZ;
}
static u64 wx_ptp_trigger_calc(struct wx *wx)
{
struct cyclecounter *cc = &wx->hw_cc;
unsigned long flags;
u64 ns = 0;
u32 rem;
write_seqlock_irqsave(&wx->hw_tc_lock, flags);
ns = timecounter_read(&wx->hw_tc);
wx->pps_edge_start = wx->hw_tc.cycle_last;
write_sequnlock_irqrestore(&wx->hw_tc_lock, flags);
wx->pps_edge_end = wx->pps_edge_start;
div_u64_rem(ns, WX_NS_PER_SEC, &rem);
rem = (WX_NS_PER_SEC - rem);
wx->pps_edge_start += div_u64(((u64)rem << cc->shift), cc->mult);
wx->pps_edge_end += div_u64(((u64)(rem + wx->pps_width) <<
cc->shift), cc->mult);
return (ns + rem);
}
static int wx_ptp_setup_sdp(struct wx *wx)
{
struct cyclecounter *cc = &wx->hw_cc;
u32 tsauxc;
u64 nsec;
if (wx->pps_width >= WX_NS_PER_SEC) {
wx_err(wx, "PTP pps width cannot be longer than 1s!\n");
return -EINVAL;
}
wr32ptp(wx, WX_TSC_1588_AUX_CTL, 0);
WX_WRITE_FLUSH(wx);
if (!test_bit(WX_FLAG_PTP_PPS_ENABLED, wx->flags)) {
if (wx->pps_enabled) {
wx->pps_enabled = false;
wx_set_pps(wx, false, 0, 0);
}
return 0;
}
wx->pps_enabled = true;
nsec = wx_ptp_trigger_calc(wx);
wx_set_pps(wx, wx->pps_enabled, nsec, wx->pps_edge_start);
tsauxc = WX_TSC_1588_AUX_CTL_PLSG | WX_TSC_1588_AUX_CTL_EN_TT0 |
WX_TSC_1588_AUX_CTL_EN_TT1 | WX_TSC_1588_AUX_CTL_EN_TS0;
wr32ptp(wx, WX_TSC_1588_TRGT_L(0), (u32)wx->pps_edge_start);
wr32ptp(wx, WX_TSC_1588_TRGT_H(0), (u32)(wx->pps_edge_start >> 32));
wr32ptp(wx, WX_TSC_1588_TRGT_L(1), (u32)wx->pps_edge_end);
wr32ptp(wx, WX_TSC_1588_TRGT_H(1), (u32)(wx->pps_edge_end >> 32));
wr32ptp(wx, WX_TSC_1588_SDP(0),
WX_TSC_1588_SDP_FUN_SEL_TT0 | WX_TSC_1588_SDP_OUT_LEVEL_H);
wr32ptp(wx, WX_TSC_1588_SDP(1), WX_TSC_1588_SDP_FUN_SEL_TS0);
wr32ptp(wx, WX_TSC_1588_AUX_CTL, tsauxc);
wr32ptp(wx, WX_TSC_1588_INT_EN, WX_TSC_1588_INT_EN_TT1);
WX_WRITE_FLUSH(wx);
wx->sec_to_cc = div_u64(((u64)WX_NS_PER_SEC << cc->shift), cc->mult);
return 0;
}
static int wx_ptp_feature_enable(struct ptp_clock_info *ptp,
struct ptp_clock_request *rq, int on)
{
struct wx *wx = container_of(ptp, struct wx, ptp_caps);
if (rq->type != PTP_CLK_REQ_PEROUT || !wx->ptp_setup_sdp)
return -EOPNOTSUPP;
if (rq->perout.flags & ~(PTP_PEROUT_DUTY_CYCLE |
PTP_PEROUT_PHASE))
return -EOPNOTSUPP;
if (rq->perout.phase.sec || rq->perout.phase.nsec) {
wx_err(wx, "Absolute start time not supported.\n");
return -EINVAL;
}
if (rq->perout.period.sec != 1 || rq->perout.period.nsec) {
wx_err(wx, "Only 1pps is supported.\n");
return -EINVAL;
}
if (rq->perout.flags & PTP_PEROUT_DUTY_CYCLE) {
struct timespec64 ts_on;
ts_on.tv_sec = rq->perout.on.sec;
ts_on.tv_nsec = rq->perout.on.nsec;
wx->pps_width = timespec64_to_ns(&ts_on);
} else {
wx->pps_width = 120000000;
}
if (on)
set_bit(WX_FLAG_PTP_PPS_ENABLED, wx->flags);
else
clear_bit(WX_FLAG_PTP_PPS_ENABLED, wx->flags);
return wx->ptp_setup_sdp(wx);
}
void wx_ptp_check_pps_event(struct wx *wx)
{
u32 tsauxc, int_status;
if (!wx->ptp_clock)
return;
int_status = rd32ptp(wx, WX_TSC_1588_INT_ST);
if (int_status & WX_TSC_1588_INT_ST_TT1) {
wr32ptp(wx, WX_TSC_1588_AUX_CTL, 0);
WX_WRITE_FLUSH(wx);
wx_ptp_trigger_calc(wx);
tsauxc = WX_TSC_1588_AUX_CTL_PLSG | WX_TSC_1588_AUX_CTL_EN_TT0 |
WX_TSC_1588_AUX_CTL_EN_TT1 | WX_TSC_1588_AUX_CTL_EN_TS0;
wr32ptp(wx, WX_TSC_1588_TRGT_L(0), (u32)wx->pps_edge_start);
wr32ptp(wx, WX_TSC_1588_TRGT_H(0), (u32)(wx->pps_edge_start >> 32));
wr32ptp(wx, WX_TSC_1588_TRGT_L(1), (u32)wx->pps_edge_end);
wr32ptp(wx, WX_TSC_1588_TRGT_H(1), (u32)(wx->pps_edge_end >> 32));
wr32ptp(wx, WX_TSC_1588_AUX_CTL, tsauxc);
WX_WRITE_FLUSH(wx);
}
}
EXPORT_SYMBOL(wx_ptp_check_pps_event);
static long wx_ptp_create_clock(struct wx *wx)
{
struct net_device *netdev = wx->netdev;
long err;
if (!IS_ERR_OR_NULL(wx->ptp_clock))
return 0;
snprintf(wx->ptp_caps.name, sizeof(wx->ptp_caps.name),
"%s", netdev->name);
wx->ptp_caps.owner = THIS_MODULE;
wx->ptp_caps.n_alarm = 0;
wx->ptp_caps.n_ext_ts = 0;
wx->ptp_caps.pps = 0;
wx->ptp_caps.adjfine = wx_ptp_adjfine;
wx->ptp_caps.adjtime = wx_ptp_adjtime;
wx->ptp_caps.gettimex64 = wx_ptp_gettimex64;
wx->ptp_caps.settime64 = wx_ptp_settime64;
wx->ptp_caps.do_aux_work = wx_ptp_do_aux_work;
switch (wx->mac.type) {
case wx_mac_aml:
case wx_mac_aml40:
wx->ptp_caps.max_adj = 250000000;
wx->ptp_caps.n_per_out = 1;
wx->ptp_setup_sdp = wx_ptp_setup_sdp;
wx->ptp_caps.enable = wx_ptp_feature_enable;
break;
case wx_mac_sp:
wx->ptp_caps.max_adj = 250000000;
wx->ptp_caps.n_per_out = 0;
wx->ptp_setup_sdp = NULL;
break;
case wx_mac_em:
wx->ptp_caps.max_adj = 500000000;
wx->ptp_caps.n_per_out = 1;
wx->ptp_setup_sdp = wx_ptp_setup_sdp;
wx->ptp_caps.enable = wx_ptp_feature_enable;
break;
default:
return -EOPNOTSUPP;
}
wx->ptp_clock = ptp_clock_register(&wx->ptp_caps, &wx->pdev->dev);
if (IS_ERR(wx->ptp_clock)) {
err = PTR_ERR(wx->ptp_clock);
wx->ptp_clock = NULL;
wx_err(wx, "ptp clock register failed\n");
return err;
} else if (wx->ptp_clock) {
dev_info(&wx->pdev->dev, "registered PHC device on %s\n",
netdev->name);
}
wx->tstamp_config.rx_filter = HWTSTAMP_FILTER_NONE;
wx->tstamp_config.tx_type = HWTSTAMP_TX_OFF;
return 0;
}
static int wx_ptp_set_timestamp_mode(struct wx *wx,
struct kernel_hwtstamp_config *config)
{
u32 tsync_tx_ctl = WX_TSC_1588_CTL_ENABLED;
u32 tsync_rx_ctl = WX_PSR_1588_CTL_ENABLED;
DECLARE_BITMAP(flags, WX_PF_FLAGS_NBITS);
u32 tsync_rx_mtrl = PTP_EV_PORT << 16;
bool is_l2 = false;
u32 regval;
memcpy(flags, wx->flags, sizeof(wx->flags));
switch (config->tx_type) {
case HWTSTAMP_TX_OFF:
tsync_tx_ctl = 0;
break;
case HWTSTAMP_TX_ON:
break;
default:
return -ERANGE;
}
switch (config->rx_filter) {
case HWTSTAMP_FILTER_NONE:
tsync_rx_ctl = 0;
tsync_rx_mtrl = 0;
clear_bit(WX_FLAG_RX_HWTSTAMP_ENABLED, flags);
clear_bit(WX_FLAG_RX_HWTSTAMP_IN_REGISTER, flags);
break;
case HWTSTAMP_FILTER_PTP_V1_L4_SYNC:
tsync_rx_ctl |= WX_PSR_1588_CTL_TYPE_L4_V1;
tsync_rx_mtrl |= WX_PSR_1588_MSG_V1_SYNC;
set_bit(WX_FLAG_RX_HWTSTAMP_ENABLED, flags);
set_bit(WX_FLAG_RX_HWTSTAMP_IN_REGISTER, flags);
break;
case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ:
tsync_rx_ctl |= WX_PSR_1588_CTL_TYPE_L4_V1;
tsync_rx_mtrl |= WX_PSR_1588_MSG_V1_DELAY_REQ;
set_bit(WX_FLAG_RX_HWTSTAMP_ENABLED, flags);
set_bit(WX_FLAG_RX_HWTSTAMP_IN_REGISTER, flags);
break;
case HWTSTAMP_FILTER_PTP_V2_EVENT:
case HWTSTAMP_FILTER_PTP_V2_L2_EVENT:
case HWTSTAMP_FILTER_PTP_V2_L4_EVENT:
case HWTSTAMP_FILTER_PTP_V2_SYNC:
case HWTSTAMP_FILTER_PTP_V2_L2_SYNC:
case HWTSTAMP_FILTER_PTP_V2_L4_SYNC:
case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ:
case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ:
case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ:
tsync_rx_ctl |= WX_PSR_1588_CTL_TYPE_EVENT_V2;
is_l2 = true;
config->rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT;
set_bit(WX_FLAG_RX_HWTSTAMP_ENABLED, flags);
set_bit(WX_FLAG_RX_HWTSTAMP_IN_REGISTER, flags);
break;
default:
config->rx_filter = HWTSTAMP_FILTER_NONE;
return -ERANGE;
}
if (is_l2)
wr32(wx, WX_PSR_ETYPE_SWC(WX_PSR_ETYPE_SWC_FILTER_1588),
(WX_PSR_ETYPE_SWC_FILTER_EN |
WX_PSR_ETYPE_SWC_1588 |
ETH_P_1588));
else
wr32(wx, WX_PSR_ETYPE_SWC(WX_PSR_ETYPE_SWC_FILTER_1588), 0);
regval = rd32ptp(wx, WX_TSC_1588_CTL);
regval &= ~WX_TSC_1588_CTL_ENABLED;
regval |= tsync_tx_ctl;
wr32ptp(wx, WX_TSC_1588_CTL, regval);
regval = rd32(wx, WX_PSR_1588_CTL);
regval &= ~(WX_PSR_1588_CTL_ENABLED | WX_PSR_1588_CTL_TYPE_MASK);
regval |= tsync_rx_ctl;
wr32(wx, WX_PSR_1588_CTL, regval);
wr32(wx, WX_PSR_1588_MSG, tsync_rx_mtrl);
WX_WRITE_FLUSH(wx);
memcpy(wx->flags, flags, sizeof(wx->flags));
wx_ptp_clear_tx_timestamp(wx);
rd32(wx, WX_PSR_1588_STMPH);
return 0;
}
static u64 wx_ptp_read(struct cyclecounter *hw_cc)
{
struct wx *wx = container_of(hw_cc, struct wx, hw_cc);
return wx_ptp_readtime(wx, NULL);
}
static void wx_ptp_link_speed_adjust(struct wx *wx, u32 *shift, u32 *incval)
{
switch (wx->mac.type) {
case wx_mac_aml:
case wx_mac_aml40:
*shift = WX_INCVAL_SHIFT_AML;
*incval = WX_INCVAL_AML;
return;
case wx_mac_em:
*shift = WX_INCVAL_SHIFT_EM;
*incval = WX_INCVAL_EM;
return;
default:
break;
}
switch (wx->speed) {
case SPEED_10:
*shift = WX_INCVAL_SHIFT_10;
*incval = WX_INCVAL_10;
break;
case SPEED_100:
*shift = WX_INCVAL_SHIFT_100;
*incval = WX_INCVAL_100;
break;
case SPEED_1000:
*shift = WX_INCVAL_SHIFT_1GB;
*incval = WX_INCVAL_1GB;
break;
case SPEED_10000:
default:
*shift = WX_INCVAL_SHIFT_10GB;
*incval = WX_INCVAL_10GB;
break;
}
}
void wx_ptp_reset_cyclecounter(struct wx *wx)
{
u32 incval = 0, mask = 0;
struct cyclecounter cc;
unsigned long flags;
cc.mask = CLOCKSOURCE_MASK(64);
cc.mult = 1;
cc.shift = 0;
cc.read = wx_ptp_read;
wx_ptp_link_speed_adjust(wx, &cc.shift, &incval);
WRITE_ONCE(wx->base_incval, incval);
mask = (wx->mac.type == wx_mac_em) ? 0x7FFFFFF : 0xFFFFFF;
incval &= mask;
if (wx->mac.type != wx_mac_em)
incval |= 2 << 24;
wr32ptp(wx, WX_TSC_1588_INC, incval);
smp_mb();
write_seqlock_irqsave(&wx->hw_tc_lock, flags);
memcpy(&wx->hw_cc, &cc, sizeof(wx->hw_cc));
write_sequnlock_irqrestore(&wx->hw_tc_lock, flags);
}
EXPORT_SYMBOL(wx_ptp_reset_cyclecounter);
void wx_ptp_reset(struct wx *wx)
{
unsigned long flags;
wx_ptp_set_timestamp_mode(wx, &wx->tstamp_config);
wx_ptp_reset_cyclecounter(wx);
wr32ptp(wx, WX_TSC_1588_SYSTIML, 0);
wr32ptp(wx, WX_TSC_1588_SYSTIMH, 0);
WX_WRITE_FLUSH(wx);
write_seqlock_irqsave(&wx->hw_tc_lock, flags);
timecounter_init(&wx->hw_tc, &wx->hw_cc,
ktime_to_ns(ktime_get_real()));
write_sequnlock_irqrestore(&wx->hw_tc_lock, flags);
wx->last_overflow_check = jiffies;
ptp_schedule_worker(wx->ptp_clock, HZ);
if (wx->ptp_setup_sdp)
wx->ptp_setup_sdp(wx);
}
EXPORT_SYMBOL(wx_ptp_reset);
void wx_ptp_init(struct wx *wx)
{
seqlock_init(&wx->hw_tc_lock);
if (wx_ptp_create_clock(wx))
return;
wx->tx_hwtstamp_pkts = 0;
wx->tx_hwtstamp_timeouts = 0;
wx->tx_hwtstamp_skipped = 0;
wx->tx_hwtstamp_errors = 0;
wx->rx_hwtstamp_cleared = 0;
wx_ptp_reset(wx);
set_bit(WX_STATE_PTP_RUNNING, wx->state);
}
EXPORT_SYMBOL(wx_ptp_init);
void wx_ptp_suspend(struct wx *wx)
{
if (!test_and_clear_bit(WX_STATE_PTP_RUNNING, wx->state))
return;
clear_bit(WX_FLAG_PTP_PPS_ENABLED, wx->flags);
if (wx->ptp_setup_sdp)
wx->ptp_setup_sdp(wx);
wx_ptp_clear_tx_timestamp(wx);
}
EXPORT_SYMBOL(wx_ptp_suspend);
void wx_ptp_stop(struct wx *wx)
{
wx_ptp_suspend(wx);
if (wx->ptp_clock) {
ptp_clock_unregister(wx->ptp_clock);
wx->ptp_clock = NULL;
dev_info(&wx->pdev->dev, "removed PHC on %s\n", wx->netdev->name);
}
}
EXPORT_SYMBOL(wx_ptp_stop);
void wx_ptp_rx_hwtstamp(struct wx *wx, struct sk_buff *skb)
{
u64 regval = 0;
u32 tsyncrxctl;
tsyncrxctl = rd32(wx, WX_PSR_1588_CTL);
if (!(tsyncrxctl & WX_PSR_1588_CTL_VALID))
return;
regval |= (u64)rd32(wx, WX_PSR_1588_STMPL);
regval |= (u64)rd32(wx, WX_PSR_1588_STMPH) << 32;
wx_ptp_convert_to_hwtstamp(wx, skb_hwtstamps(skb), regval);
}
int wx_hwtstamp_get(struct net_device *dev,
struct kernel_hwtstamp_config *cfg)
{
struct wx *wx = netdev_priv(dev);
if (!netif_running(dev))
return -EINVAL;
*cfg = wx->tstamp_config;
return 0;
}
EXPORT_SYMBOL(wx_hwtstamp_get);
int wx_hwtstamp_set(struct net_device *dev,
struct kernel_hwtstamp_config *cfg,
struct netlink_ext_ack *extack)
{
struct wx *wx = netdev_priv(dev);
int err;
if (!netif_running(dev))
return -EINVAL;
err = wx_ptp_set_timestamp_mode(wx, cfg);
if (err)
return err;
memcpy(&wx->tstamp_config, cfg, sizeof(wx->tstamp_config));
return 0;
}
EXPORT_SYMBOL(wx_hwtstamp_set);