mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-03-02 17:52:36 -05:00
Merge branch 'support-ptp-clock-for-wangxun-nics'
Jiawen Wu says: ==================== Support PTP clock for Wangxun NICs Implement support for PTP clock on Wangxun NICs. v7: https://lore.kernel.org/20250213083041.78917-1-jiawenwu@trustnetic.com v6: https://lore.kernel.org/20250208031348.4368-1-jiawenwu@trustnetic.com v5: https://lore.kernel.org/20250117062051.2257073-1-jiawenwu@trustnetic.com v4: https://lore.kernel.org/20250114084425.2203428-1-jiawenwu@trustnetic.com v3: https://lore.kernel.org/20250110031716.2120642-1-jiawenwu@trustnetic.com v2: https://lore.kernel.org/20250106084506.2042912-1-jiawenwu@trustnetic.com v1: https://lore.kernel.org/20250102103026.1982137-1-jiawenwu@trustnetic.com ==================== Link: https://patch.msgid.link/20250218023432.146536-1-jiawenwu@trustnetic.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
@@ -18,6 +18,7 @@ if NET_VENDOR_WANGXUN
|
||||
|
||||
config LIBWX
|
||||
tristate
|
||||
depends on PTP_1588_CLOCK_OPTIONAL
|
||||
select PAGE_POOL
|
||||
help
|
||||
Common library for Wangxun(R) Ethernet drivers.
|
||||
|
||||
@@ -4,4 +4,4 @@
|
||||
|
||||
obj-$(CONFIG_LIBWX) += libwx.o
|
||||
|
||||
libwx-objs := wx_hw.o wx_lib.o wx_ethtool.o
|
||||
libwx-objs := wx_hw.o wx_lib.o wx_ethtool.o wx_ptp.o
|
||||
|
||||
@@ -41,6 +41,9 @@ static const struct wx_stats wx_gstrings_stats[] = {
|
||||
WX_STAT("rx_csum_offload_good_count", hw_csum_rx_good),
|
||||
WX_STAT("rx_csum_offload_errors", hw_csum_rx_error),
|
||||
WX_STAT("alloc_rx_buff_failed", alloc_rx_buff_failed),
|
||||
WX_STAT("tx_hwtstamp_timeouts", tx_hwtstamp_timeouts),
|
||||
WX_STAT("tx_hwtstamp_skipped", tx_hwtstamp_skipped),
|
||||
WX_STAT("rx_hwtstamp_cleared", rx_hwtstamp_cleared),
|
||||
};
|
||||
|
||||
static const struct wx_stats wx_gstrings_fdir_stats[] = {
|
||||
@@ -452,3 +455,53 @@ void wx_set_msglevel(struct net_device *netdev, u32 data)
|
||||
wx->msg_enable = data;
|
||||
}
|
||||
EXPORT_SYMBOL(wx_set_msglevel);
|
||||
|
||||
int wx_get_ts_info(struct net_device *dev,
|
||||
struct kernel_ethtool_ts_info *info)
|
||||
{
|
||||
struct wx *wx = netdev_priv(dev);
|
||||
|
||||
info->rx_filters = BIT(HWTSTAMP_FILTER_NONE) |
|
||||
BIT(HWTSTAMP_FILTER_PTP_V1_L4_SYNC) |
|
||||
BIT(HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ) |
|
||||
BIT(HWTSTAMP_FILTER_PTP_V2_L2_EVENT) |
|
||||
BIT(HWTSTAMP_FILTER_PTP_V2_L4_EVENT) |
|
||||
BIT(HWTSTAMP_FILTER_PTP_V2_SYNC) |
|
||||
BIT(HWTSTAMP_FILTER_PTP_V2_L2_SYNC) |
|
||||
BIT(HWTSTAMP_FILTER_PTP_V2_L4_SYNC) |
|
||||
BIT(HWTSTAMP_FILTER_PTP_V2_DELAY_REQ) |
|
||||
BIT(HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ) |
|
||||
BIT(HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ) |
|
||||
BIT(HWTSTAMP_FILTER_PTP_V2_EVENT);
|
||||
|
||||
info->so_timestamping = SOF_TIMESTAMPING_TX_SOFTWARE |
|
||||
SOF_TIMESTAMPING_TX_HARDWARE |
|
||||
SOF_TIMESTAMPING_RX_HARDWARE |
|
||||
SOF_TIMESTAMPING_RAW_HARDWARE;
|
||||
|
||||
if (wx->ptp_clock)
|
||||
info->phc_index = ptp_clock_index(wx->ptp_clock);
|
||||
else
|
||||
info->phc_index = -1;
|
||||
|
||||
info->tx_types = BIT(HWTSTAMP_TX_OFF) |
|
||||
BIT(HWTSTAMP_TX_ON);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(wx_get_ts_info);
|
||||
|
||||
void wx_get_ptp_stats(struct net_device *dev,
|
||||
struct ethtool_ts_stats *ts_stats)
|
||||
{
|
||||
struct wx *wx = netdev_priv(dev);
|
||||
|
||||
if (wx->ptp_clock) {
|
||||
ts_stats->pkts = wx->tx_hwtstamp_pkts;
|
||||
ts_stats->lost = wx->tx_hwtstamp_timeouts +
|
||||
wx->tx_hwtstamp_skipped +
|
||||
wx->rx_hwtstamp_cleared;
|
||||
ts_stats->err = wx->tx_hwtstamp_errors;
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(wx_get_ptp_stats);
|
||||
|
||||
@@ -40,4 +40,8 @@ int wx_set_channels(struct net_device *dev,
|
||||
struct ethtool_channels *ch);
|
||||
u32 wx_get_msglevel(struct net_device *netdev);
|
||||
void wx_set_msglevel(struct net_device *netdev, u32 data);
|
||||
int wx_get_ts_info(struct net_device *dev,
|
||||
struct kernel_ethtool_ts_info *info);
|
||||
void wx_get_ptp_stats(struct net_device *dev,
|
||||
struct ethtool_ts_stats *ts_stats);
|
||||
#endif /* _WX_ETHTOOL_H_ */
|
||||
|
||||
@@ -393,6 +393,25 @@ int wx_host_interface_command(struct wx *wx, u32 *buffer,
|
||||
}
|
||||
EXPORT_SYMBOL(wx_host_interface_command);
|
||||
|
||||
int wx_set_pps(struct wx *wx, bool enable, u64 nsec, u64 cycles)
|
||||
{
|
||||
struct wx_hic_set_pps pps_cmd;
|
||||
|
||||
pps_cmd.hdr.cmd = FW_PPS_SET_CMD;
|
||||
pps_cmd.hdr.buf_len = FW_PPS_SET_LEN;
|
||||
pps_cmd.hdr.cmd_or_resp.cmd_resv = FW_CEM_CMD_RESERVED;
|
||||
pps_cmd.lan_id = wx->bus.func;
|
||||
pps_cmd.enable = (u8)enable;
|
||||
pps_cmd.nsec = nsec;
|
||||
pps_cmd.cycles = cycles;
|
||||
pps_cmd.hdr.checksum = FW_DEFAULT_CHECKSUM;
|
||||
|
||||
return wx_host_interface_command(wx, (u32 *)&pps_cmd,
|
||||
sizeof(pps_cmd),
|
||||
WX_HI_COMMAND_TIMEOUT,
|
||||
false);
|
||||
}
|
||||
|
||||
/**
|
||||
* wx_read_ee_hostif_data - Read EEPROM word using a host interface cmd
|
||||
* assuming that the semaphore is already obtained.
|
||||
|
||||
@@ -18,6 +18,7 @@ void wx_control_hw(struct wx *wx, bool drv);
|
||||
int wx_mng_present(struct wx *wx);
|
||||
int wx_host_interface_command(struct wx *wx, u32 *buffer,
|
||||
u32 length, u32 timeout, bool return_data);
|
||||
int wx_set_pps(struct wx *wx, bool enable, u64 nsec, u64 cycles);
|
||||
int wx_read_ee_hostif(struct wx *wx, u16 offset, u16 *data);
|
||||
int wx_read_ee_hostif_buffer(struct wx *wx,
|
||||
u16 offset, u16 words, u16 *data);
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
|
||||
#include "wx_type.h"
|
||||
#include "wx_lib.h"
|
||||
#include "wx_ptp.h"
|
||||
#include "wx_hw.h"
|
||||
|
||||
/* Lookup table mapping the HW PTYPE to the bit field for decoding */
|
||||
@@ -597,8 +598,17 @@ static void wx_process_skb_fields(struct wx_ring *rx_ring,
|
||||
union wx_rx_desc *rx_desc,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct wx *wx = netdev_priv(rx_ring->netdev);
|
||||
|
||||
wx_rx_hash(rx_ring, rx_desc, skb);
|
||||
wx_rx_checksum(rx_ring, rx_desc, skb);
|
||||
|
||||
if (unlikely(test_bit(WX_FLAG_RX_HWTSTAMP_ENABLED, wx->flags)) &&
|
||||
unlikely(wx_test_staterr(rx_desc, WX_RXD_STAT_TS))) {
|
||||
wx_ptp_rx_hwtstamp(rx_ring->q_vector->wx, skb);
|
||||
rx_ring->last_rx_timestamp = jiffies;
|
||||
}
|
||||
|
||||
wx_rx_vlan(rx_ring, rx_desc, skb);
|
||||
skb_record_rx_queue(skb, rx_ring->queue_index);
|
||||
skb->protocol = eth_type_trans(skb, rx_ring->netdev);
|
||||
@@ -705,6 +715,7 @@ static bool wx_clean_tx_irq(struct wx_q_vector *q_vector,
|
||||
{
|
||||
unsigned int budget = q_vector->wx->tx_work_limit;
|
||||
unsigned int total_bytes = 0, total_packets = 0;
|
||||
struct wx *wx = netdev_priv(tx_ring->netdev);
|
||||
unsigned int i = tx_ring->next_to_clean;
|
||||
struct wx_tx_buffer *tx_buffer;
|
||||
union wx_tx_desc *tx_desc;
|
||||
@@ -737,6 +748,11 @@ static bool wx_clean_tx_irq(struct wx_q_vector *q_vector,
|
||||
total_bytes += tx_buffer->bytecount;
|
||||
total_packets += tx_buffer->gso_segs;
|
||||
|
||||
/* schedule check for Tx timestamp */
|
||||
if (unlikely(test_bit(WX_STATE_PTP_TX_IN_PROGRESS, wx->state)) &&
|
||||
skb_shinfo(tx_buffer->skb)->tx_flags & SKBTX_IN_PROGRESS)
|
||||
ptp_schedule_worker(wx->ptp_clock, 0);
|
||||
|
||||
/* free the skb */
|
||||
napi_consume_skb(tx_buffer->skb, napi_budget);
|
||||
|
||||
@@ -932,9 +948,9 @@ static void wx_tx_olinfo_status(union wx_tx_desc *tx_desc,
|
||||
tx_desc->read.olinfo_status = cpu_to_le32(olinfo_status);
|
||||
}
|
||||
|
||||
static void wx_tx_map(struct wx_ring *tx_ring,
|
||||
struct wx_tx_buffer *first,
|
||||
const u8 hdr_len)
|
||||
static int wx_tx_map(struct wx_ring *tx_ring,
|
||||
struct wx_tx_buffer *first,
|
||||
const u8 hdr_len)
|
||||
{
|
||||
struct sk_buff *skb = first->skb;
|
||||
struct wx_tx_buffer *tx_buffer;
|
||||
@@ -1013,6 +1029,8 @@ static void wx_tx_map(struct wx_ring *tx_ring,
|
||||
|
||||
netdev_tx_sent_queue(wx_txring_txq(tx_ring), first->bytecount);
|
||||
|
||||
/* set the timestamp */
|
||||
first->time_stamp = jiffies;
|
||||
skb_tx_timestamp(skb);
|
||||
|
||||
/* Force memory writes to complete before letting h/w know there
|
||||
@@ -1038,7 +1056,7 @@ static void wx_tx_map(struct wx_ring *tx_ring,
|
||||
if (netif_xmit_stopped(wx_txring_txq(tx_ring)) || !netdev_xmit_more())
|
||||
writel(i, tx_ring->tail);
|
||||
|
||||
return;
|
||||
return 0;
|
||||
dma_error:
|
||||
dev_err(tx_ring->dev, "TX DMA map failed\n");
|
||||
|
||||
@@ -1062,6 +1080,8 @@ static void wx_tx_map(struct wx_ring *tx_ring,
|
||||
first->skb = NULL;
|
||||
|
||||
tx_ring->next_to_use = i;
|
||||
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
static void wx_tx_ctxtdesc(struct wx_ring *tx_ring, u32 vlan_macip_lens,
|
||||
@@ -1486,6 +1506,20 @@ static netdev_tx_t wx_xmit_frame_ring(struct sk_buff *skb,
|
||||
tx_flags |= WX_TX_FLAGS_HW_VLAN;
|
||||
}
|
||||
|
||||
if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) &&
|
||||
wx->ptp_clock) {
|
||||
if (wx->tstamp_config.tx_type == HWTSTAMP_TX_ON &&
|
||||
!test_and_set_bit_lock(WX_STATE_PTP_TX_IN_PROGRESS,
|
||||
wx->state)) {
|
||||
skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS;
|
||||
tx_flags |= WX_TX_FLAGS_TSTAMP;
|
||||
wx->ptp_tx_skb = skb_get(skb);
|
||||
wx->ptp_tx_start = jiffies;
|
||||
} else {
|
||||
wx->tx_hwtstamp_skipped++;
|
||||
}
|
||||
}
|
||||
|
||||
/* record initial flags and protocol */
|
||||
first->tx_flags = tx_flags;
|
||||
first->protocol = vlan_get_protocol(skb);
|
||||
@@ -1501,12 +1535,20 @@ static netdev_tx_t wx_xmit_frame_ring(struct sk_buff *skb,
|
||||
if (test_bit(WX_FLAG_FDIR_CAPABLE, wx->flags) && tx_ring->atr_sample_rate)
|
||||
wx->atr(tx_ring, first, ptype);
|
||||
|
||||
wx_tx_map(tx_ring, first, hdr_len);
|
||||
if (wx_tx_map(tx_ring, first, hdr_len))
|
||||
goto cleanup_tx_tstamp;
|
||||
|
||||
return NETDEV_TX_OK;
|
||||
out_drop:
|
||||
dev_kfree_skb_any(first->skb);
|
||||
first->skb = NULL;
|
||||
cleanup_tx_tstamp:
|
||||
if (unlikely(tx_flags & WX_TX_FLAGS_TSTAMP)) {
|
||||
dev_kfree_skb_any(wx->ptp_tx_skb);
|
||||
wx->ptp_tx_skb = NULL;
|
||||
wx->tx_hwtstamp_errors++;
|
||||
clear_bit_unlock(WX_STATE_PTP_TX_IN_PROGRESS, wx->state);
|
||||
}
|
||||
|
||||
return NETDEV_TX_OK;
|
||||
}
|
||||
|
||||
883
drivers/net/ethernet/wangxun/libwx/wx_ptp.c
Normal file
883
drivers/net/ethernet/wangxun/libwx/wx_ptp.c
Normal file
@@ -0,0 +1,883 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* Copyright (c) 2015 - 2025 Beijing WangXun Technology Co., Ltd. */
|
||||
/* Copyright (c) 1999 - 2025 Intel Corporation. */
|
||||
|
||||
#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_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_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(); /* Force any pending update before accessing. */
|
||||
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);
|
||||
/* reset the timecounter */
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* wx_ptp_clear_tx_timestamp - utility function to clear Tx timestamp state
|
||||
* @wx: the private board structure
|
||||
*
|
||||
* This function should be called whenever the state related to a Tx timestamp
|
||||
* needs to be cleared. This helps ensure that all related bits are reset for
|
||||
* the next Tx timestamp event.
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* wx_ptp_convert_to_hwtstamp - convert register value to hw timestamp
|
||||
* @wx: private board structure
|
||||
* @hwtstamp: stack timestamp structure
|
||||
* @timestamp: unsigned 64bit system time value
|
||||
*
|
||||
* We need to convert the adapter's RX/TXSTMP registers into a hwtstamp value
|
||||
* which can be used by the stack's ptp functions.
|
||||
*
|
||||
* The lock is used to protect consistency of the cyclecounter and the SYSTIME
|
||||
* registers. However, it does not need to protect against the Rx or Tx
|
||||
* timestamp registers, as there can't be a new timestamp until the old one is
|
||||
* unlatched by reading.
|
||||
*
|
||||
* In addition to the timestamp in hardware, some controllers need a software
|
||||
* overflow cyclecounter, and this function takes this into account as well.
|
||||
**/
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* wx_ptp_tx_hwtstamp - utility function which checks for TX time stamp
|
||||
* @wx: the private board struct
|
||||
*
|
||||
* if the timestamp is valid, we convert it into the timecounter ns
|
||||
* value, then store that result into the shhwtstamps structure which
|
||||
* is passed up the network stack
|
||||
*/
|
||||
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;
|
||||
|
||||
/* we have to have a valid skb to poll for a timestamp */
|
||||
if (!wx->ptp_tx_skb) {
|
||||
wx_ptp_clear_tx_timestamp(wx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* stop polling once we have a valid timestamp */
|
||||
tsynctxctl = rd32ptp(wx, WX_TSC_1588_CTL);
|
||||
if (tsynctxctl & WX_TSC_1588_CTL_VALID) {
|
||||
wx_ptp_tx_hwtstamp(wx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* wx_ptp_overflow_check - watchdog task to detect SYSTIME overflow
|
||||
* @wx: pointer to wx struct
|
||||
*
|
||||
* this watchdog task periodically reads the timecounter
|
||||
* in order to prevent missing when the system time registers wrap
|
||||
* around. This needs to be run approximately twice a minute for the fastest
|
||||
* overflowing hardware. We run it for all hardware since it shouldn't have a
|
||||
* large impact.
|
||||
*/
|
||||
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) {
|
||||
/* Update the timecounter */
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* wx_ptp_rx_hang - detect error case when Rx timestamp registers latched
|
||||
* @wx: pointer to wx struct
|
||||
*
|
||||
* this watchdog task is scheduled to detect error case where hardware has
|
||||
* dropped an Rx packet that was timestamped when the ring is full. The
|
||||
* particular error is rare but leaves the device in a state unable to
|
||||
* timestamp any future packets.
|
||||
*/
|
||||
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 we don't have a valid timestamp in the registers, just update the
|
||||
* timeout counter and exit
|
||||
*/
|
||||
if (!(tsyncrxctl & WX_PSR_1588_CTL_VALID)) {
|
||||
wx->last_rx_ptp_check = jiffies;
|
||||
return;
|
||||
}
|
||||
|
||||
/* determine the most recent watchdog or rx_timestamp event */
|
||||
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;
|
||||
}
|
||||
|
||||
/* only need to read the high RXSTMP register to clear the lock */
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* wx_ptp_tx_hang - detect error case where Tx timestamp never finishes
|
||||
* @wx: private network wx structure
|
||||
*/
|
||||
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 we haven't received a timestamp within the timeout, it is
|
||||
* reasonable to assume that it will never occur, so we can unlock the
|
||||
* timestamp bit when this occurs.
|
||||
*/
|
||||
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;
|
||||
|
||||
/* Read the current clock time, and save the cycle counter value */
|
||||
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;
|
||||
|
||||
/* Figure out how far past the next second we are */
|
||||
div_u64_rem(ns, WX_NS_PER_SEC, &rem);
|
||||
|
||||
/* Figure out how many nanoseconds to add to round the clock edge up
|
||||
* to the next full second
|
||||
*/
|
||||
rem = (WX_NS_PER_SEC - rem);
|
||||
|
||||
/* Adjust the clock edge to align with the next full second. */
|
||||
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;
|
||||
}
|
||||
|
||||
/* disable the pin first */
|
||||
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);
|
||||
|
||||
/* Adjust the clock edge to align with the next full second. */
|
||||
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);
|
||||
|
||||
/**
|
||||
* When PPS is enabled, unmask the interrupt for the ClockOut
|
||||
* feature, so that the interrupt handler can send the PPS
|
||||
* event when the clock SDP triggers. Clear mask when PPS is
|
||||
* disabled
|
||||
*/
|
||||
if (rq->type != PTP_CLK_REQ_PEROUT || !wx->ptp_setup_sdp)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
/* Reject requests with unsupported flags */
|
||||
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;
|
||||
|
||||
/* this check is necessary in case the interrupt was enabled via some
|
||||
* alternative means (ex. debug_fs). Better to check here than
|
||||
* everywhere that calls this function.
|
||||
*/
|
||||
if (!wx->ptp_clock)
|
||||
return;
|
||||
|
||||
int_status = rd32ptp(wx, WX_TSC_1588_INT_ST);
|
||||
if (int_status & WX_TSC_1588_INT_ST_TT1) {
|
||||
/* disable the pin first */
|
||||
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;
|
||||
|
||||
/* do nothing if we already have a clock device */
|
||||
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;
|
||||
if (wx->mac.type == 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;
|
||||
} else {
|
||||
wx->ptp_caps.max_adj = 250000000;
|
||||
wx->ptp_caps.n_per_out = 0;
|
||||
wx->ptp_setup_sdp = NULL;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
/* Set the default timestamp mode to disabled here. We do this in
|
||||
* create_clock instead of initialization, because we don't want to
|
||||
* override the previous settings during a suspend/resume cycle.
|
||||
*/
|
||||
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:
|
||||
/* register PSR_1588_MSG must be set in order to do V1 packets,
|
||||
* therefore it is not possible to time stamp both V1 Sync and
|
||||
* Delay_Req messages unless hardware supports timestamping all
|
||||
* packets => return error
|
||||
*/
|
||||
config->rx_filter = HWTSTAMP_FILTER_NONE;
|
||||
return -ERANGE;
|
||||
}
|
||||
|
||||
/* define ethertype filter for timestamping L2 packets */
|
||||
if (is_l2)
|
||||
wr32(wx, WX_PSR_ETYPE_SWC(WX_PSR_ETYPE_SWC_FILTER_1588),
|
||||
(WX_PSR_ETYPE_SWC_FILTER_EN | /* enable filter */
|
||||
WX_PSR_ETYPE_SWC_1588 | /* enable timestamping */
|
||||
ETH_P_1588)); /* 1588 eth protocol type */
|
||||
else
|
||||
wr32(wx, WX_PSR_ETYPE_SWC(WX_PSR_ETYPE_SWC_FILTER_1588), 0);
|
||||
|
||||
/* enable/disable TX */
|
||||
regval = rd32ptp(wx, WX_TSC_1588_CTL);
|
||||
regval &= ~WX_TSC_1588_CTL_ENABLED;
|
||||
regval |= tsync_tx_ctl;
|
||||
wr32ptp(wx, WX_TSC_1588_CTL, regval);
|
||||
|
||||
/* enable/disable RX */
|
||||
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);
|
||||
|
||||
/* define which PTP packets are time stamped */
|
||||
wr32(wx, WX_PSR_1588_MSG, tsync_rx_mtrl);
|
||||
|
||||
WX_WRITE_FLUSH(wx);
|
||||
|
||||
/* configure adapter flags only when HW is actually configured */
|
||||
memcpy(wx->flags, flags, sizeof(wx->flags));
|
||||
|
||||
/* clear TX/RX timestamp state, just to be sure */
|
||||
wx_ptp_clear_tx_timestamp(wx);
|
||||
rd32(wx, WX_PSR_1588_STMPH);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u64 wx_ptp_read(const 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)
|
||||
{
|
||||
if (wx->mac.type == wx_mac_em) {
|
||||
*shift = WX_INCVAL_SHIFT_EM;
|
||||
*incval = WX_INCVAL_EM;
|
||||
return;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* wx_ptp_reset_cyclecounter - create the cycle counter from hw
|
||||
* @wx: pointer to the wx structure
|
||||
*
|
||||
* This function should be called to set the proper values for the TSC_1588_INC
|
||||
* register and tell the cyclecounter structure what the tick rate of SYSTIME
|
||||
* is. It does not directly modify SYSTIME registers or the timecounter
|
||||
* structure. It should be called whenever a new TSC_1588_INC value is
|
||||
* necessary, such as during initialization or when the link speed changes.
|
||||
*/
|
||||
void wx_ptp_reset_cyclecounter(struct wx *wx)
|
||||
{
|
||||
u32 incval = 0, mask = 0;
|
||||
struct cyclecounter cc;
|
||||
unsigned long flags;
|
||||
|
||||
/* For some of the boards below this mask is technically incorrect.
|
||||
* The timestamp mask overflows at approximately 61bits. However the
|
||||
* particular hardware does not overflow on an even bitmask value.
|
||||
* Instead, it overflows due to conversion of upper 32bits billions of
|
||||
* cycles. Timecounters are not really intended for this purpose so
|
||||
* they do not properly function if the overflow point isn't 2^N-1.
|
||||
* However, the actual SYSTIME values in question take ~138 years to
|
||||
* overflow. In practice this means they won't actually overflow. A
|
||||
* proper fix to this problem would require modification of the
|
||||
* timecounter delta calculations.
|
||||
*/
|
||||
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);
|
||||
|
||||
/* update the base incval used to calculate frequency adjustment */
|
||||
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(); /* Force the above update. */
|
||||
|
||||
/* need lock to prevent incorrect read while modifying cyclecounter */
|
||||
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;
|
||||
|
||||
/* reset the hardware timestamping mode */
|
||||
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);
|
||||
|
||||
/* Now that the shift has been calculated and the systime
|
||||
* registers reset, (re-)enable the Clock out feature
|
||||
*/
|
||||
if (wx->ptp_setup_sdp)
|
||||
wx->ptp_setup_sdp(wx);
|
||||
}
|
||||
EXPORT_SYMBOL(wx_ptp_reset);
|
||||
|
||||
void wx_ptp_init(struct wx *wx)
|
||||
{
|
||||
/* Initialize the seqlock_t first, since the user might call the clock
|
||||
* functions any time after we've initialized the ptp clock device.
|
||||
*/
|
||||
seqlock_init(&wx->hw_tc_lock);
|
||||
|
||||
/* obtain a ptp clock device, or re-use an existing device */
|
||||
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;
|
||||
/* reset the ptp related hardware bits */
|
||||
wx_ptp_reset(wx);
|
||||
|
||||
/* enter the WX_STATE_PTP_RUNNING state */
|
||||
set_bit(WX_STATE_PTP_RUNNING, wx->state);
|
||||
}
|
||||
EXPORT_SYMBOL(wx_ptp_init);
|
||||
|
||||
/**
|
||||
* wx_ptp_suspend - stop ptp work items
|
||||
* @wx: pointer to wx struct
|
||||
*
|
||||
* This function suspends ptp activity, and prevents more work from being
|
||||
* generated, but does not destroy the clock device.
|
||||
*/
|
||||
void wx_ptp_suspend(struct wx *wx)
|
||||
{
|
||||
/* leave the WX_STATE_PTP_RUNNING STATE */
|
||||
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);
|
||||
|
||||
/**
|
||||
* wx_ptp_stop - destroy the ptp_clock device
|
||||
* @wx: pointer to wx struct
|
||||
*
|
||||
* Completely destroy the ptp_clock device, and disable all PTP related
|
||||
* features. Intended to be run when the device is being closed.
|
||||
*/
|
||||
void wx_ptp_stop(struct wx *wx)
|
||||
{
|
||||
/* first, suspend ptp activity */
|
||||
wx_ptp_suspend(wx);
|
||||
|
||||
/* now destroy the ptp clock device */
|
||||
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);
|
||||
|
||||
/**
|
||||
* wx_ptp_rx_hwtstamp - utility function which checks for RX time stamp
|
||||
* @wx: pointer to wx struct
|
||||
* @skb: particular skb to send timestamp with
|
||||
*
|
||||
* if the timestamp is valid, we convert it into the timecounter ns
|
||||
* value, then store that result into the shhwtstamps structure which
|
||||
* is passed up the network stack
|
||||
*/
|
||||
void wx_ptp_rx_hwtstamp(struct wx *wx, struct sk_buff *skb)
|
||||
{
|
||||
u64 regval = 0;
|
||||
u32 tsyncrxctl;
|
||||
|
||||
/* Read the tsyncrxctl register afterwards in order to prevent taking an
|
||||
* I/O hit on every packet.
|
||||
*/
|
||||
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;
|
||||
|
||||
/* save these settings for future reference */
|
||||
memcpy(&wx->tstamp_config, cfg, sizeof(wx->tstamp_config));
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(wx_hwtstamp_set);
|
||||
20
drivers/net/ethernet/wangxun/libwx/wx_ptp.h
Normal file
20
drivers/net/ethernet/wangxun/libwx/wx_ptp.h
Normal file
@@ -0,0 +1,20 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/* Copyright (c) 2019 - 2025 Beijing WangXun Technology Co., Ltd. */
|
||||
|
||||
#ifndef _WX_PTP_H_
|
||||
#define _WX_PTP_H_
|
||||
|
||||
void wx_ptp_check_pps_event(struct wx *wx);
|
||||
void wx_ptp_reset_cyclecounter(struct wx *wx);
|
||||
void wx_ptp_reset(struct wx *wx);
|
||||
void wx_ptp_init(struct wx *wx);
|
||||
void wx_ptp_suspend(struct wx *wx);
|
||||
void wx_ptp_stop(struct wx *wx);
|
||||
void wx_ptp_rx_hwtstamp(struct wx *wx, struct sk_buff *skb);
|
||||
int wx_hwtstamp_get(struct net_device *dev,
|
||||
struct kernel_hwtstamp_config *cfg);
|
||||
int wx_hwtstamp_set(struct net_device *dev,
|
||||
struct kernel_hwtstamp_config *cfg,
|
||||
struct netlink_ext_ack *extack);
|
||||
|
||||
#endif /* _WX_PTP_H_ */
|
||||
@@ -4,6 +4,8 @@
|
||||
#ifndef _WX_TYPE_H_
|
||||
#define _WX_TYPE_H_
|
||||
|
||||
#include <linux/ptp_clock_kernel.h>
|
||||
#include <linux/timecounter.h>
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/if_vlan.h>
|
||||
@@ -180,6 +182,23 @@
|
||||
#define WX_PSR_VLAN_CTL 0x15088
|
||||
#define WX_PSR_VLAN_CTL_CFIEN BIT(29) /* bit 29 */
|
||||
#define WX_PSR_VLAN_CTL_VFE BIT(30) /* bit 30 */
|
||||
/* EType Queue Filter */
|
||||
#define WX_PSR_ETYPE_SWC(_i) (0x15128 + ((_i) * 4))
|
||||
#define WX_PSR_ETYPE_SWC_FILTER_1588 3
|
||||
#define WX_PSR_ETYPE_SWC_FILTER_EN BIT(31)
|
||||
#define WX_PSR_ETYPE_SWC_1588 BIT(30)
|
||||
/* 1588 */
|
||||
#define WX_PSR_1588_MSG 0x15120
|
||||
#define WX_PSR_1588_MSG_V1_SYNC FIELD_PREP(GENMASK(7, 0), 0)
|
||||
#define WX_PSR_1588_MSG_V1_DELAY_REQ FIELD_PREP(GENMASK(7, 0), 1)
|
||||
#define WX_PSR_1588_STMPL 0x151E8
|
||||
#define WX_PSR_1588_STMPH 0x151A4
|
||||
#define WX_PSR_1588_CTL 0x15188
|
||||
#define WX_PSR_1588_CTL_ENABLED BIT(4)
|
||||
#define WX_PSR_1588_CTL_TYPE_MASK GENMASK(3, 1)
|
||||
#define WX_PSR_1588_CTL_TYPE_L4_V1 FIELD_PREP(GENMASK(3, 1), 1)
|
||||
#define WX_PSR_1588_CTL_TYPE_EVENT_V2 FIELD_PREP(GENMASK(3, 1), 5)
|
||||
#define WX_PSR_1588_CTL_VALID BIT(0)
|
||||
/* mcasst/ucast overflow tbl */
|
||||
#define WX_PSR_MC_TBL(_i) (0x15200 + ((_i) * 4))
|
||||
#define WX_PSR_UC_TBL(_i) (0x15400 + ((_i) * 4))
|
||||
@@ -253,6 +272,32 @@
|
||||
#define WX_TSC_ST_SECTX_RDY BIT(0)
|
||||
#define WX_TSC_BUF_AE 0x1D00C
|
||||
#define WX_TSC_BUF_AE_THR GENMASK(9, 0)
|
||||
/* 1588 */
|
||||
#define WX_TSC_1588_CTL 0x11F00
|
||||
#define WX_TSC_1588_CTL_ENABLED BIT(4)
|
||||
#define WX_TSC_1588_CTL_VALID BIT(0)
|
||||
#define WX_TSC_1588_STMPL 0x11F04
|
||||
#define WX_TSC_1588_STMPH 0x11F08
|
||||
#define WX_TSC_1588_SYSTIML 0x11F0C
|
||||
#define WX_TSC_1588_SYSTIMH 0x11F10
|
||||
#define WX_TSC_1588_INC 0x11F14
|
||||
#define WX_TSC_1588_INT_ST 0x11F20
|
||||
#define WX_TSC_1588_INT_ST_TT1 BIT(5)
|
||||
#define WX_TSC_1588_INT_EN 0x11F24
|
||||
#define WX_TSC_1588_INT_EN_TT1 BIT(5)
|
||||
#define WX_TSC_1588_AUX_CTL 0x11F28
|
||||
#define WX_TSC_1588_AUX_CTL_EN_TS0 BIT(8)
|
||||
#define WX_TSC_1588_AUX_CTL_EN_TT1 BIT(2)
|
||||
#define WX_TSC_1588_AUX_CTL_PLSG BIT(1)
|
||||
#define WX_TSC_1588_AUX_CTL_EN_TT0 BIT(0)
|
||||
#define WX_TSC_1588_TRGT_L(i) (0x11F2C + ((i) * 8)) /* [0,1] */
|
||||
#define WX_TSC_1588_TRGT_H(i) (0x11F30 + ((i) * 8)) /* [0,1] */
|
||||
#define WX_TSC_1588_SDP(i) (0x11F5C + ((i) * 4)) /* [0,3] */
|
||||
#define WX_TSC_1588_SDP_OUT_LEVEL_H FIELD_PREP(BIT(4), 0)
|
||||
#define WX_TSC_1588_SDP_OUT_LEVEL_L FIELD_PREP(BIT(4), 1)
|
||||
#define WX_TSC_1588_SDP_FUN_SEL_MASK GENMASK(2, 0)
|
||||
#define WX_TSC_1588_SDP_FUN_SEL_TT0 FIELD_PREP(WX_TSC_1588_SDP_FUN_SEL_MASK, 1)
|
||||
#define WX_TSC_1588_SDP_FUN_SEL_TS0 FIELD_PREP(WX_TSC_1588_SDP_FUN_SEL_MASK, 5)
|
||||
|
||||
/************************************** MNG ********************************/
|
||||
#define WX_MNG_SWFW_SYNC 0x1E008
|
||||
@@ -382,6 +427,8 @@ enum WX_MSCA_CMD_value {
|
||||
#define FW_CEM_CMD_RESERVED 0X0
|
||||
#define FW_CEM_MAX_RETRIES 3
|
||||
#define FW_CEM_RESP_STATUS_SUCCESS 0x1
|
||||
#define FW_PPS_SET_CMD 0xF6
|
||||
#define FW_PPS_SET_LEN 0x14
|
||||
|
||||
#define WX_SW_REGION_PTR 0x1C
|
||||
|
||||
@@ -460,6 +507,7 @@ enum WX_MSCA_CMD_value {
|
||||
#define WX_RXD_STAT_L4CS BIT(7) /* L4 xsum calculated */
|
||||
#define WX_RXD_STAT_IPCS BIT(8) /* IP xsum calculated */
|
||||
#define WX_RXD_STAT_OUTERIPCS BIT(10) /* Cloud IP xsum calculated*/
|
||||
#define WX_RXD_STAT_TS BIT(14) /* IEEE1588 Time Stamp */
|
||||
|
||||
#define WX_RXD_ERR_OUTERIPER BIT(26) /* CRC IP Header error */
|
||||
#define WX_RXD_ERR_RXE BIT(29) /* Any MAC Error */
|
||||
@@ -701,6 +749,15 @@ struct wx_hic_reset {
|
||||
u16 reset_type;
|
||||
};
|
||||
|
||||
struct wx_hic_set_pps {
|
||||
struct wx_hic_hdr hdr;
|
||||
u8 lan_id;
|
||||
u8 enable;
|
||||
u16 pad2;
|
||||
u64 nsec;
|
||||
u64 cycles;
|
||||
};
|
||||
|
||||
/* Bus parameters */
|
||||
struct wx_bus_info {
|
||||
u8 func;
|
||||
@@ -863,6 +920,7 @@ struct wx_tx_context_desc {
|
||||
*/
|
||||
struct wx_tx_buffer {
|
||||
union wx_tx_desc *next_to_watch;
|
||||
unsigned long time_stamp;
|
||||
struct sk_buff *skb;
|
||||
unsigned int bytecount;
|
||||
unsigned short gso_segs;
|
||||
@@ -924,6 +982,7 @@ struct wx_ring {
|
||||
unsigned int size; /* length in bytes */
|
||||
|
||||
u16 count; /* amount of descriptors */
|
||||
unsigned long last_rx_timestamp;
|
||||
|
||||
u8 queue_index; /* needed for multiqueue queue management */
|
||||
u8 reg_idx; /* holds the special value that gets
|
||||
@@ -1026,6 +1085,8 @@ struct wx_hw_stats {
|
||||
|
||||
enum wx_state {
|
||||
WX_STATE_RESETTING,
|
||||
WX_STATE_PTP_RUNNING,
|
||||
WX_STATE_PTP_TX_IN_PROGRESS,
|
||||
WX_STATE_NBITS, /* must be last */
|
||||
};
|
||||
|
||||
@@ -1033,6 +1094,9 @@ enum wx_pf_flags {
|
||||
WX_FLAG_FDIR_CAPABLE,
|
||||
WX_FLAG_FDIR_HASH,
|
||||
WX_FLAG_FDIR_PERFECT,
|
||||
WX_FLAG_RX_HWTSTAMP_ENABLED,
|
||||
WX_FLAG_RX_HWTSTAMP_IN_REGISTER,
|
||||
WX_FLAG_PTP_PPS_ENABLED,
|
||||
WX_PF_FLAGS_NBITS /* must be last */
|
||||
};
|
||||
|
||||
@@ -1133,6 +1197,29 @@ struct wx {
|
||||
void (*atr)(struct wx_ring *ring, struct wx_tx_buffer *first, u8 ptype);
|
||||
void (*configure_fdir)(struct wx *wx);
|
||||
void (*do_reset)(struct net_device *netdev);
|
||||
int (*ptp_setup_sdp)(struct wx *wx);
|
||||
|
||||
bool pps_enabled;
|
||||
u64 pps_width;
|
||||
u64 pps_edge_start;
|
||||
u64 pps_edge_end;
|
||||
u64 sec_to_cc;
|
||||
u32 base_incval;
|
||||
u32 tx_hwtstamp_pkts;
|
||||
u32 tx_hwtstamp_timeouts;
|
||||
u32 tx_hwtstamp_skipped;
|
||||
u32 tx_hwtstamp_errors;
|
||||
u32 rx_hwtstamp_cleared;
|
||||
unsigned long last_overflow_check;
|
||||
unsigned long last_rx_ptp_check;
|
||||
unsigned long ptp_tx_start;
|
||||
seqlock_t hw_tc_lock; /* seqlock for ptp */
|
||||
struct cyclecounter hw_cc;
|
||||
struct timecounter hw_tc;
|
||||
struct ptp_clock *ptp_clock;
|
||||
struct ptp_clock_info ptp_caps;
|
||||
struct kernel_hwtstamp_config tstamp_config;
|
||||
struct sk_buff *ptp_tx_skb;
|
||||
};
|
||||
|
||||
#define WX_INTR_ALL (~0ULL)
|
||||
@@ -1177,6 +1264,24 @@ rd64(struct wx *wx, u32 reg)
|
||||
return (lsb | msb << 32);
|
||||
}
|
||||
|
||||
static inline u32
|
||||
rd32ptp(struct wx *wx, u32 reg)
|
||||
{
|
||||
if (wx->mac.type == wx_mac_em)
|
||||
return rd32(wx, reg);
|
||||
|
||||
return rd32(wx, reg + 0xB500);
|
||||
}
|
||||
|
||||
static inline void
|
||||
wr32ptp(struct wx *wx, u32 reg, u32 value)
|
||||
{
|
||||
if (wx->mac.type == wx_mac_em)
|
||||
return wr32(wx, reg, value);
|
||||
|
||||
return wr32(wx, reg + 0xB500, value);
|
||||
}
|
||||
|
||||
/* On some domestic CPU platforms, sometimes IO is not synchronized with
|
||||
* flushing memory, here use readl() to flush PCI read and write.
|
||||
*/
|
||||
|
||||
@@ -138,6 +138,8 @@ static const struct ethtool_ops ngbe_ethtool_ops = {
|
||||
.set_channels = ngbe_set_channels,
|
||||
.get_msglevel = wx_get_msglevel,
|
||||
.set_msglevel = wx_set_msglevel,
|
||||
.get_ts_info = wx_get_ts_info,
|
||||
.get_ts_stats = wx_get_ptp_stats,
|
||||
};
|
||||
|
||||
void ngbe_set_ethtool_ops(struct net_device *netdev)
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include "../libwx/wx_type.h"
|
||||
#include "../libwx/wx_hw.h"
|
||||
#include "../libwx/wx_lib.h"
|
||||
#include "../libwx/wx_ptp.h"
|
||||
#include "ngbe_type.h"
|
||||
#include "ngbe_mdio.h"
|
||||
#include "ngbe_hw.h"
|
||||
@@ -167,7 +168,7 @@ static irqreturn_t ngbe_intr(int __always_unused irq, void *data)
|
||||
struct wx_q_vector *q_vector;
|
||||
struct wx *wx = data;
|
||||
struct pci_dev *pdev;
|
||||
u32 eicr;
|
||||
u32 eicr, eicr_misc;
|
||||
|
||||
q_vector = wx->q_vector[0];
|
||||
pdev = wx->pdev;
|
||||
@@ -185,6 +186,10 @@ static irqreturn_t ngbe_intr(int __always_unused irq, void *data)
|
||||
if (!(pdev->msi_enabled))
|
||||
wr32(wx, WX_PX_INTA, 1);
|
||||
|
||||
eicr_misc = wx_misc_isb(wx, WX_ISB_MISC);
|
||||
if (unlikely(eicr_misc & NGBE_PX_MISC_IC_TIMESYNC))
|
||||
wx_ptp_check_pps_event(wx);
|
||||
|
||||
wx->isb_mem[WX_ISB_MISC] = 0;
|
||||
/* would disable interrupts here but it is auto disabled */
|
||||
napi_schedule_irqoff(&q_vector->napi);
|
||||
@@ -198,6 +203,12 @@ static irqreturn_t ngbe_intr(int __always_unused irq, void *data)
|
||||
static irqreturn_t ngbe_msix_other(int __always_unused irq, void *data)
|
||||
{
|
||||
struct wx *wx = data;
|
||||
u32 eicr;
|
||||
|
||||
eicr = wx_misc_isb(wx, WX_ISB_MISC);
|
||||
|
||||
if (unlikely(eicr & NGBE_PX_MISC_IC_TIMESYNC))
|
||||
wx_ptp_check_pps_event(wx);
|
||||
|
||||
/* re-enable the original interrupt state, no lsc, no queues */
|
||||
if (netif_running(wx->netdev))
|
||||
@@ -317,6 +328,8 @@ void ngbe_down(struct wx *wx)
|
||||
{
|
||||
phylink_stop(wx->phylink);
|
||||
ngbe_disable_device(wx);
|
||||
if (test_bit(WX_STATE_PTP_RUNNING, wx->state))
|
||||
wx_ptp_reset(wx);
|
||||
wx_clean_all_tx_rings(wx);
|
||||
wx_clean_all_rx_rings(wx);
|
||||
}
|
||||
@@ -379,6 +392,8 @@ static int ngbe_open(struct net_device *netdev)
|
||||
if (err)
|
||||
goto err_dis_phy;
|
||||
|
||||
wx_ptp_init(wx);
|
||||
|
||||
ngbe_up(wx);
|
||||
|
||||
return 0;
|
||||
@@ -407,6 +422,7 @@ static int ngbe_close(struct net_device *netdev)
|
||||
{
|
||||
struct wx *wx = netdev_priv(netdev);
|
||||
|
||||
wx_ptp_stop(wx);
|
||||
ngbe_down(wx);
|
||||
wx_free_irq(wx);
|
||||
wx_free_isb_resources(wx);
|
||||
@@ -507,6 +523,8 @@ static const struct net_device_ops ngbe_netdev_ops = {
|
||||
.ndo_get_stats64 = wx_get_stats64,
|
||||
.ndo_vlan_rx_add_vid = wx_vlan_rx_add_vid,
|
||||
.ndo_vlan_rx_kill_vid = wx_vlan_rx_kill_vid,
|
||||
.ndo_hwtstamp_set = wx_hwtstamp_set,
|
||||
.ndo_hwtstamp_get = wx_hwtstamp_get,
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <linux/phy.h>
|
||||
|
||||
#include "../libwx/wx_type.h"
|
||||
#include "../libwx/wx_ptp.h"
|
||||
#include "../libwx/wx_hw.h"
|
||||
#include "ngbe_type.h"
|
||||
#include "ngbe_mdio.h"
|
||||
@@ -64,6 +65,11 @@ static void ngbe_mac_config(struct phylink_config *config, unsigned int mode,
|
||||
static void ngbe_mac_link_down(struct phylink_config *config,
|
||||
unsigned int mode, phy_interface_t interface)
|
||||
{
|
||||
struct wx *wx = phylink_to_wx(config);
|
||||
|
||||
wx->speed = SPEED_UNKNOWN;
|
||||
if (test_bit(WX_STATE_PTP_RUNNING, wx->state))
|
||||
wx_ptp_reset_cyclecounter(wx);
|
||||
}
|
||||
|
||||
static void ngbe_mac_link_up(struct phylink_config *config,
|
||||
@@ -103,6 +109,11 @@ static void ngbe_mac_link_up(struct phylink_config *config,
|
||||
wr32(wx, WX_MAC_PKT_FLT, WX_MAC_PKT_FLT_PR);
|
||||
reg = rd32(wx, WX_MAC_WDG_TIMEOUT);
|
||||
wr32(wx, WX_MAC_WDG_TIMEOUT, reg);
|
||||
|
||||
wx->speed = speed;
|
||||
wx->last_rx_ptp_check = jiffies;
|
||||
if (test_bit(WX_STATE_PTP_RUNNING, wx->state))
|
||||
wx_ptp_reset_cyclecounter(wx);
|
||||
}
|
||||
|
||||
static const struct phylink_mac_ops ngbe_mac_ops = {
|
||||
|
||||
@@ -70,15 +70,20 @@
|
||||
|
||||
/* Extended Interrupt Enable Set */
|
||||
#define NGBE_PX_MISC_IEN_DEV_RST BIT(10)
|
||||
#define NGBE_PX_MISC_IEN_TIMESYNC BIT(11)
|
||||
#define NGBE_PX_MISC_IEN_ETH_LK BIT(18)
|
||||
#define NGBE_PX_MISC_IEN_INT_ERR BIT(20)
|
||||
#define NGBE_PX_MISC_IEN_GPIO BIT(26)
|
||||
#define NGBE_PX_MISC_IEN_MASK ( \
|
||||
NGBE_PX_MISC_IEN_DEV_RST | \
|
||||
NGBE_PX_MISC_IEN_TIMESYNC | \
|
||||
NGBE_PX_MISC_IEN_ETH_LK | \
|
||||
NGBE_PX_MISC_IEN_INT_ERR | \
|
||||
NGBE_PX_MISC_IEN_GPIO)
|
||||
|
||||
/* Extended Interrupt Cause Read */
|
||||
#define NGBE_PX_MISC_IC_TIMESYNC BIT(11) /* time sync */
|
||||
|
||||
#define NGBE_INTR_ALL 0x1FF
|
||||
#define NGBE_INTR_MISC BIT(0)
|
||||
|
||||
|
||||
@@ -529,6 +529,8 @@ static const struct ethtool_ops txgbe_ethtool_ops = {
|
||||
.set_rxnfc = txgbe_set_rxnfc,
|
||||
.get_msglevel = wx_get_msglevel,
|
||||
.set_msglevel = wx_set_msglevel,
|
||||
.get_ts_info = wx_get_ts_info,
|
||||
.get_ts_stats = wx_get_ptp_stats,
|
||||
};
|
||||
|
||||
void txgbe_set_ethtool_ops(struct net_device *netdev)
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
|
||||
#include "../libwx/wx_type.h"
|
||||
#include "../libwx/wx_lib.h"
|
||||
#include "../libwx/wx_ptp.h"
|
||||
#include "../libwx/wx_hw.h"
|
||||
#include "txgbe_type.h"
|
||||
#include "txgbe_hw.h"
|
||||
@@ -116,6 +117,9 @@ static void txgbe_reset(struct wx *wx)
|
||||
memcpy(old_addr, &wx->mac_table[0].addr, netdev->addr_len);
|
||||
wx_flush_sw_mac_table(wx);
|
||||
wx_mac_set_default_filter(wx, old_addr);
|
||||
|
||||
if (test_bit(WX_STATE_PTP_RUNNING, wx->state))
|
||||
wx_ptp_reset(wx);
|
||||
}
|
||||
|
||||
static void txgbe_disable_device(struct wx *wx)
|
||||
@@ -176,6 +180,7 @@ void txgbe_down(struct wx *wx)
|
||||
void txgbe_up(struct wx *wx)
|
||||
{
|
||||
wx_configure(wx);
|
||||
wx_ptp_init(wx);
|
||||
txgbe_up_complete(wx);
|
||||
}
|
||||
|
||||
@@ -321,6 +326,8 @@ static int txgbe_open(struct net_device *netdev)
|
||||
if (err)
|
||||
goto err_free_irq;
|
||||
|
||||
wx_ptp_init(wx);
|
||||
|
||||
txgbe_up_complete(wx);
|
||||
|
||||
return 0;
|
||||
@@ -344,6 +351,7 @@ static int txgbe_open(struct net_device *netdev)
|
||||
*/
|
||||
static void txgbe_close_suspend(struct wx *wx)
|
||||
{
|
||||
wx_ptp_suspend(wx);
|
||||
txgbe_disable_device(wx);
|
||||
wx_free_resources(wx);
|
||||
}
|
||||
@@ -363,6 +371,7 @@ static int txgbe_close(struct net_device *netdev)
|
||||
{
|
||||
struct wx *wx = netdev_priv(netdev);
|
||||
|
||||
wx_ptp_stop(wx);
|
||||
txgbe_down(wx);
|
||||
wx_free_irq(wx);
|
||||
wx_free_resources(wx);
|
||||
@@ -479,6 +488,8 @@ static const struct net_device_ops txgbe_netdev_ops = {
|
||||
.ndo_get_stats64 = wx_get_stats64,
|
||||
.ndo_vlan_rx_add_vid = wx_vlan_rx_add_vid,
|
||||
.ndo_vlan_rx_kill_vid = wx_vlan_rx_kill_vid,
|
||||
.ndo_hwtstamp_set = wx_hwtstamp_set,
|
||||
.ndo_hwtstamp_get = wx_hwtstamp_get,
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
|
||||
#include "../libwx/wx_type.h"
|
||||
#include "../libwx/wx_lib.h"
|
||||
#include "../libwx/wx_ptp.h"
|
||||
#include "../libwx/wx_hw.h"
|
||||
#include "txgbe_type.h"
|
||||
#include "txgbe_phy.h"
|
||||
@@ -179,6 +180,10 @@ static void txgbe_mac_link_down(struct phylink_config *config,
|
||||
struct wx *wx = phylink_to_wx(config);
|
||||
|
||||
wr32m(wx, WX_MAC_TX_CFG, WX_MAC_TX_CFG_TE, 0);
|
||||
|
||||
wx->speed = SPEED_UNKNOWN;
|
||||
if (test_bit(WX_STATE_PTP_RUNNING, wx->state))
|
||||
wx_ptp_reset_cyclecounter(wx);
|
||||
}
|
||||
|
||||
static void txgbe_mac_link_up(struct phylink_config *config,
|
||||
@@ -215,6 +220,11 @@ static void txgbe_mac_link_up(struct phylink_config *config,
|
||||
wr32(wx, WX_MAC_PKT_FLT, WX_MAC_PKT_FLT_PR);
|
||||
wdg = rd32(wx, WX_MAC_WDG_TIMEOUT);
|
||||
wr32(wx, WX_MAC_WDG_TIMEOUT, wdg);
|
||||
|
||||
wx->speed = speed;
|
||||
wx->last_rx_ptp_check = jiffies;
|
||||
if (test_bit(WX_STATE_PTP_RUNNING, wx->state))
|
||||
wx_ptp_reset_cyclecounter(wx);
|
||||
}
|
||||
|
||||
static int txgbe_mac_prepare(struct phylink_config *config, unsigned int mode,
|
||||
|
||||
Reference in New Issue
Block a user