Message ID | 20221018010733.4765-5-muhammad.husaini.zulkifli@intel.com |
---|---|
State | Changes Requested |
Headers | show |
Series | Add support for DMA timestamp for non-PTP packets | expand |
On 10/17/2022 6:07 PM, Muhammad Husaini Zulkifli wrote: > From: Vinicius Costa Gomes <vinicius.gomes@intel.com> > > For PTP traffic, timestamp is retrieved from TXSTMP register. > For all other packets, DMA time stamp field of the Transmit > Descriptor Write-back is used. > > If the TXSTAMPO register is used for both PTP and non-PTP packets, > there is a significant possibility that the time stamp for a PTP packet > will be lost when there is a lot of traffic. > > This patch introduce to use the DMA Time Stamp for non PTP packet to > solve the current issue. User application can add new SOF_TIMESTAMPING flag > SOF_TIMESTAMPING_TX_HARDWARE_DMA_FETCH when configure the > hwtstamp_config for the socket option if require DMA Time Stamp. > > Before: > > ptp4l: rms 2 max 5 freq -3404 +/- 3 delay 1 +/- 0 > ptp4l: rms 3 max 6 freq -3400 +/- 3 delay 1 +/- 0 > ptp4l: rms 2 max 4 freq -3400 +/- 3 delay 1 +/- 0 > ptp4l: timed out while polling for tx timestamp > ptp4l: increasing tx_timestamp_timeout may correct this issue, > but it is likely caused by a driver bug > ptp4l: port 1 (enp170s0.vlan): send peer delay response failed > ptp4l: port 1 (enp170s0.vlan): SLAVE to FAULTY on FAULT_DETECTED > ptp4l: port 1 (enp170s0.vlan): FAULTY to LISTENING on INIT_COMPLETE > ptp4l: port 1 (enp170s0.vlan): LISTENING to MASTER on > ANNOUNCE_RECEIPT_TIMEOUT_EXPIRES > ptp4l: selected local clock aa00aa.fffe.00aa00 as best master > ptp4l: port 1 (enp170s0.vlan): assuming the grand master role > ptp4l: port 1 (enp170s0.vlan): new foreign master 22bb22.fffe.bb22bb-1 > ptp4l: selected best master clock 22bb22.fffe.bb22bb > ptp4l: port 1 (enp170s0.vlan): MASTER to UNCALIBRATED on RS_SLAVE > ptp4l: port 1 (enp170s0.vlan): UNCALIBRATED to SLAVE on > MASTER_CLOCK_SELECTED > ptp4l: rms 39 max 66 freq -3355 +/- 45 delay 1 +/- 0 > ptp4l: rms 20 max 36 freq -3339 +/- 12 delay 1 +/- 0 > ptp4l: rms 11 max 18 freq -3371 +/- 11 delay 1 +/- 0 > ptp4l: rms 10 max 16 freq -3384 +/- 2 delay 1 +/- 0 > ptp4l: rms 1 max 2 freq -3375 +/- 2 delay 1 +/- 0 > ptp4l: rms 3 max 6 freq -3373 +/- 4 delay 0 +/- 0 > > After: > > ptp4l: rms 3 max 4 freq -3386 +/- 4 delay 0 +/- 0 > ptp4l: rms 3 max 7 freq -3380 +/- 3 delay 0 +/- 0 > ptp4l: rms 3 max 6 freq -3380 +/- 3 delay 0 +/- 0 > ptp4l: rms 1 max 3 freq -3381 +/- 2 delay 0 +/- 0 > ptp4l: rms 3 max 5 freq -3377 +/- 2 delay 0 +/- 0 > ptp4l: rms 2 max 3 freq -3377 +/- 2 delay 0 +/- 0 > ptp4l: rms 3 max 6 freq -3375 +/- 4 delay 0 +/- 0 > ptp4l: rms 2 max 4 freq -3380 +/- 2 delay 1 +/- 0 > ptp4l: rms 4 max 7 freq -3385 +/- 3 delay 0 +/- 0 > ptp4l: rms 2 max 3 freq -3384 +/- 2 delay 0 +/- 0 > ptp4l: rms 4 max 7 freq -3376 +/- 2 delay 0 +/- 0 > ptp4l: rms 3 max 5 freq -3376 +/- 4 delay 0 +/- 0 > ptp4l: rms 3 max 5 freq -3382 +/- 2 delay 0 +/- 0 > ptp4l: rms 5 max 7 freq -3389 +/- 2 delay 0 +/- 0 > ptp4l: rms 3 max 4 freq -3388 +/- 3 delay 1 +/- 0 > ptp4l: rms 3 max 5 freq -3387 +/- 4 delay 1 +/- 0 > ptp4l: rms 5 max 8 freq -3395 +/- 3 delay 1 +/- 0 > ptp4l: rms 5 max 8 freq -3399 +/- 4 delay 0 +/- 0 > ptp4l: rms 2 max 5 freq -3397 +/- 3 delay 1 +/- 0 > ptp4l: rms 2 max 4 freq -3397 +/- 3 delay 1 +/- 0 > ptp4l: rms 2 max 3 freq -3397 +/- 2 delay 1 +/- 0 > ptp4l: rms 3 max 5 freq -3391 +/- 2 delay 2 +/- 0 > > Test Setup: > back-to-back communication between Host and DUT. Host will act as > transmitter and DUT will become receiver. Host will generate the > packet using sample application with timestamping_flag of > SOF_TIMESTAMPING_TX_HARDWARE_DMA_FETCH and hwtstamp_config flag of > HWTSTAMP_FLAG_DMA_TIMESTAMP. > > Signed-off-by: Vinicius Costa Gomes <vinicius.gomes@intel.com> > Co-developed-by: Muhammad Husaini Zulkifli <muhammad.husaini.zulkifli@intel.com> > Signed-off-by: Muhammad Husaini Zulkifli <muhammad.husaini.zulkifli@intel.com> > Co-developed-by: Aravindhan Gunasekaran <aravindhan.gunasekaran@intel.com> > Signed-off-by: Aravindhan Gunasekaran <aravindhan.gunasekaran@intel.com> > Signed-off-by: Muhammad Husaini Zulkifli <muhammad.husaini.zulkifli@intel.com> > --- > drivers/net/ethernet/intel/igc/igc.h | 10 +++ > drivers/net/ethernet/intel/igc/igc_base.h | 2 +- > drivers/net/ethernet/intel/igc/igc_defines.h | 2 + > drivers/net/ethernet/intel/igc/igc_ethtool.c | 5 +- > drivers/net/ethernet/intel/igc/igc_main.c | 24 ++++-- > drivers/net/ethernet/intel/igc/igc_ptp.c | 83 ++++++++++++++++++++ > 6 files changed, 119 insertions(+), 7 deletions(-) > > diff --git a/drivers/net/ethernet/intel/igc/igc.h b/drivers/net/ethernet/intel/igc/igc.h > index 1e7e7071f64d..38a24b5260d1 100644 > --- a/drivers/net/ethernet/intel/igc/igc.h > +++ b/drivers/net/ethernet/intel/igc/igc.h > @@ -348,6 +348,12 @@ extern char igc_driver_name[]; > #define IGC_I225_RX_LATENCY_1000 300 > #define IGC_I225_RX_LATENCY_2500 1485 > > +/* Transmit latency (for DMA timestamps) in nanosecond */ > +#define IGC_I225_TX_DMA_LATENCY_10 13100 > +#define IGC_I225_TX_DMA_LATENCY_100 1410 > +#define IGC_I225_TX_DMA_LATENCY_1000 285 > +#define IGC_I225_TX_DMA_LATENCY_2500 1485 > + > /* RX and TX descriptor control thresholds. > * PTHRESH - MAC will consider prefetch if it has fewer than this number of > * descriptors available in its onboard memory. > @@ -410,6 +416,8 @@ enum igc_tx_flags { > /* olinfo flags */ > IGC_TX_FLAGS_IPV4 = 0x10, > IGC_TX_FLAGS_CSUM = 0x20, > + > + IGC_TX_FLAGS_DMA_TSTAMP = 0x200, > }; > > enum igc_boards { > @@ -627,6 +635,8 @@ void igc_ptp_reset(struct igc_adapter *adapter); > void igc_ptp_suspend(struct igc_adapter *adapter); > void igc_ptp_stop(struct igc_adapter *adapter); > ktime_t igc_ptp_rx_pktstamp(struct igc_adapter *adapter, __le32 *buf); > +void igc_ptp_tx_dma_tstamp(struct igc_adapter *adapter, > + struct sk_buff *skb, u64 tstamp); > int igc_ptp_set_ts_config(struct net_device *netdev, struct ifreq *ifr); > int igc_ptp_get_ts_config(struct net_device *netdev, struct ifreq *ifr); > void igc_ptp_tx_hang(struct igc_adapter *adapter); > diff --git a/drivers/net/ethernet/intel/igc/igc_base.h b/drivers/net/ethernet/intel/igc/igc_base.h > index ce530f5fd7bd..672cf2d92165 100644 > --- a/drivers/net/ethernet/intel/igc/igc_base.h > +++ b/drivers/net/ethernet/intel/igc/igc_base.h > @@ -16,7 +16,7 @@ union igc_adv_tx_desc { > __le32 olinfo_status; > } read; > struct { > - __le64 rsvd; /* Reserved */ > + __le64 dma_tstamp; > __le32 nxtseq_seed; > __le32 status; > } wb; > diff --git a/drivers/net/ethernet/intel/igc/igc_defines.h b/drivers/net/ethernet/intel/igc/igc_defines.h > index f7311aeb293b..baedf48b4e2e 100644 > --- a/drivers/net/ethernet/intel/igc/igc_defines.h > +++ b/drivers/net/ethernet/intel/igc/igc_defines.h > @@ -312,6 +312,7 @@ > #define IGC_TXD_CMD_DEXT 0x20000000 /* Desc extension (0 = legacy) */ > #define IGC_TXD_CMD_VLE 0x40000000 /* Add VLAN tag */ > #define IGC_TXD_STAT_DD 0x00000001 /* Descriptor Done */ > +#define IGC_TXD_STAT_TS_STAT 0x00000002 /* DMA Timestamp in packet */ > #define IGC_TXD_CMD_TCP 0x01000000 /* TCP packet */ > #define IGC_TXD_CMD_IP 0x02000000 /* IP packet */ > #define IGC_TXD_CMD_TSE 0x04000000 /* TCP Seg enable */ > @@ -520,6 +521,7 @@ > /* Transmit Scheduling */ > #define IGC_TQAVCTRL_TRANSMIT_MODE_TSN 0x00000001 > #define IGC_TQAVCTRL_ENHANCED_QAV 0x00000008 > +#define IGC_TQAVCTRL_1588_STAT_EN 0x00000004 > > #define IGC_TXQCTL_QUEUE_MODE_LAUNCHT 0x00000001 > #define IGC_TXQCTL_STRICT_CYCLE 0x00000002 > diff --git a/drivers/net/ethernet/intel/igc/igc_ethtool.c b/drivers/net/ethernet/intel/igc/igc_ethtool.c > index 8cc077b712ad..7d198fb6d619 100644 > --- a/drivers/net/ethernet/intel/igc/igc_ethtool.c > +++ b/drivers/net/ethernet/intel/igc/igc_ethtool.c > @@ -1532,7 +1532,8 @@ static int igc_ethtool_get_ts_info(struct net_device *dev, > SOF_TIMESTAMPING_SOFTWARE | > SOF_TIMESTAMPING_TX_HARDWARE | > SOF_TIMESTAMPING_RX_HARDWARE | > - SOF_TIMESTAMPING_RAW_HARDWARE; > + SOF_TIMESTAMPING_RAW_HARDWARE | > + SOF_TIMESTAMPING_TX_HARDWARE_DMA_FETCH; > > info->tx_types = > BIT(HWTSTAMP_TX_OFF) | > @@ -1541,6 +1542,8 @@ static int igc_ethtool_get_ts_info(struct net_device *dev, > info->rx_filters = BIT(HWTSTAMP_FILTER_NONE); > info->rx_filters |= BIT(HWTSTAMP_FILTER_ALL); > > + info->flag = HWTSTAMP_FLAG_DMA_TIMESTAMP; > + > return 0; > default: > return -EOPNOTSUPP; > diff --git a/drivers/net/ethernet/intel/igc/igc_main.c b/drivers/net/ethernet/intel/igc/igc_main.c > index 671255edf3c2..daa6ca5acab3 100644 > --- a/drivers/net/ethernet/intel/igc/igc_main.c > +++ b/drivers/net/ethernet/intel/igc/igc_main.c > @@ -1415,6 +1415,7 @@ static int igc_tso(struct igc_ring *tx_ring, > static netdev_tx_t igc_xmit_frame_ring(struct sk_buff *skb, > struct igc_ring *tx_ring) > { > + struct igc_adapter *adapter = netdev_priv(tx_ring->netdev); > u16 count = TXD_USE_COUNT(skb_headlen(skb)); > __be16 protocol = vlan_get_protocol(skb); > struct igc_tx_buffer *first; > @@ -1445,16 +1446,14 @@ static netdev_tx_t igc_xmit_frame_ring(struct sk_buff *skb, > first->bytecount = skb->len; > first->gso_segs = 1; > > - if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP)) { > - struct igc_adapter *adapter = netdev_priv(tx_ring->netdev); > - > + if (unlikely((skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) && > + !(skb_shinfo(skb)->tx_flags & SKBTX_HW_DMA_TSTAMP))) { > /* FIXME: add support for retrieving timestamps from > * the other timer registers before skipping the > * timestamping request. > */ What are the semantics of asking for both SKBTX_HW_TSTAMP vs SKBTX_HW_DMA_TSTAMP? I guess we can only support one? and if both are set we're going t prever SKBTX_HW_DMA_TSTAMP? > if (adapter->tstamp_config.tx_type == HWTSTAMP_TX_ON && > - !test_and_set_bit_lock(__IGC_PTP_TX_IN_PROGRESS, > - &adapter->state)) { > + !test_and_set_bit_lock(__IGC_PTP_TX_IN_PROGRESS, &adapter->state)) { > skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS; > tx_flags |= IGC_TX_FLAGS_TSTAMP; > > @@ -1463,6 +1462,14 @@ static netdev_tx_t igc_xmit_frame_ring(struct sk_buff *skb, > } else { > adapter->tx_hwtstamp_skipped++; > } > + } else if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_HW_DMA_TSTAMP)) { > + if (adapter->tstamp_config.tx_type == HWTSTAMP_TX_ON && > + adapter->tstamp_config.flags == HWTSTAMP_FLAG_DMA_TIMESTAMP) { > + skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS; > + tx_flags |= IGC_TX_FLAGS_DMA_TSTAMP; > + } else { > + adapter->tx_hwtstamp_skipped++; > + } > } > > if (skb_vlan_tag_present(skb)) { > @@ -2741,6 +2748,13 @@ static bool igc_clean_tx_irq(struct igc_q_vector *q_vector, int napi_budget) > if (!(eop_desc->wb.status & cpu_to_le32(IGC_TXD_STAT_DD))) > break; > > + if (eop_desc->wb.status & cpu_to_le32(IGC_TXD_STAT_TS_STAT) && > + tx_buffer->tx_flags & IGC_TX_FLAGS_DMA_TSTAMP) { > + u64 tstamp = le64_to_cpu(eop_desc->wb.dma_tstamp); > + > + igc_ptp_tx_dma_tstamp(adapter, tx_buffer->skb, tstamp); > + } > + > /* clear next_to_watch to prevent false hangs */ > tx_buffer->next_to_watch = NULL; > > diff --git a/drivers/net/ethernet/intel/igc/igc_ptp.c b/drivers/net/ethernet/intel/igc/igc_ptp.c > index 8dbb9f903ca7..631972d7e97b 100644 > --- a/drivers/net/ethernet/intel/igc/igc_ptp.c > +++ b/drivers/net/ethernet/intel/igc/igc_ptp.c > @@ -432,6 +432,29 @@ static void igc_ptp_systim_to_hwtstamp(struct igc_adapter *adapter, > } > } > > +static void igc_ptp_dma_time_to_hwtstamp(struct igc_adapter *adapter, > + struct skb_shared_hwtstamps *hwtstamps, > + u64 systim) > +{ > + struct igc_hw *hw = &adapter->hw; > + u32 sec, nsec; > + > + nsec = rd32(IGC_SYSTIML); > + sec = rd32(IGC_SYSTIMH); > + Why are we reading systime register in the hotpath? > + if (unlikely(nsec < (systim & 0xFFFFFFFF))) > + --sec; > + > + switch (adapter->hw.mac.type) { > + case igc_i225: > + memset(hwtstamps, 0, sizeof(*hwtstamps)); > + hwtstamps->hwtstamp = ktime_set(sec, systim & 0xFFFFFFFF); > + break; This seems to take the seconds from IGC_SYSTIML/H but the lower 32 bits from the systim value passed in which is the DMA timestamp I guess? If that value is a u64 why can't we simply directly convert it? Wouldn't we rather be using something like timecounter_cyc2time here instead of directly reading the clock in the hotpath? How does this handle things like rollover where the DMA timestamp was just captured before a rollover and now we put the wrong seconds value.. If we already get a 64bit value from the DMA timestamp why do we even need to read seconds here? is it the wrong time format? What does the datasheet say for these fields? Thanks, Jake > + default: > + break; > + } > +} > + > /** > * igc_ptp_rx_pktstamp - Retrieve timestamp from Rx packet buffer > * @adapter: Pointer to adapter the packet buffer belongs to > @@ -549,6 +572,28 @@ static void igc_ptp_enable_tx_timestamp(struct igc_adapter *adapter) > rd32(IGC_TXSTMPH); > } > > +static void igc_ptp_disable_dma_timestamp(struct igc_adapter *adapter) > +{ > + struct igc_hw *hw = &adapter->hw; > + u32 tqavctrl; > + > + tqavctrl = rd32(IGC_TQAVCTRL); > + tqavctrl &= ~IGC_TQAVCTRL_1588_STAT_EN; > + > + wr32(IGC_TQAVCTRL, tqavctrl); > +} > + > +static void igc_ptp_enable_dma_timestamp(struct igc_adapter *adapter) > +{ > + struct igc_hw *hw = &adapter->hw; > + u32 tqavctrl; > + > + tqavctrl = rd32(IGC_TQAVCTRL); > + tqavctrl |= IGC_TQAVCTRL_1588_STAT_EN; > + > + wr32(IGC_TQAVCTRL, tqavctrl); > +} > + > /** > * igc_ptp_set_timestamp_mode - setup hardware for timestamping > * @adapter: networking device structure > @@ -562,9 +607,14 @@ static int igc_ptp_set_timestamp_mode(struct igc_adapter *adapter, > switch (config->tx_type) { > case HWTSTAMP_TX_OFF: > igc_ptp_disable_tx_timestamp(adapter); > + igc_ptp_disable_dma_timestamp(adapter); > break; > case HWTSTAMP_TX_ON: > igc_ptp_enable_tx_timestamp(adapter); > + > + /* Ensure that flag only can be used during HWTSTAMP_TX_ON */ > + if (config->flags == HWTSTAMP_FLAG_DMA_TIMESTAMP) > + igc_ptp_enable_dma_timestamp(adapter); > break; > default: > return -ERANGE; > @@ -683,6 +733,39 @@ static void igc_ptp_tx_hwtstamp(struct igc_adapter *adapter) > dev_kfree_skb_any(skb); > } > > +void igc_ptp_tx_dma_tstamp(struct igc_adapter *adapter, > + struct sk_buff *skb, u64 tstamp) > +{ > + struct skb_shared_hwtstamps shhwtstamps; > + int adjust = 0; > + > + if (!(skb_shinfo(skb)->tx_flags & SKBTX_IN_PROGRESS)) > + return; > + > + igc_ptp_dma_time_to_hwtstamp(adapter, &shhwtstamps, tstamp); > + > + switch (adapter->link_speed) { > + case SPEED_10: > + adjust = IGC_I225_TX_DMA_LATENCY_10; > + break; > + case SPEED_100: > + adjust = IGC_I225_TX_DMA_LATENCY_100; > + break; > + case SPEED_1000: > + adjust = IGC_I225_TX_DMA_LATENCY_1000; > + break; > + case SPEED_2500: > + adjust = IGC_I225_TX_DMA_LATENCY_2500; > + break; > + } > + > + shhwtstamps.hwtstamp = > + ktime_add_ns(shhwtstamps.hwtstamp, adjust); > + > + /* Notify the stack and free the skb after we've unlocked */ > + skb_tstamp_tx(skb, &shhwtstamps); > +} > + > /** > * igc_ptp_tx_work > * @work: pointer to work struct
diff --git a/drivers/net/ethernet/intel/igc/igc.h b/drivers/net/ethernet/intel/igc/igc.h index 1e7e7071f64d..38a24b5260d1 100644 --- a/drivers/net/ethernet/intel/igc/igc.h +++ b/drivers/net/ethernet/intel/igc/igc.h @@ -348,6 +348,12 @@ extern char igc_driver_name[]; #define IGC_I225_RX_LATENCY_1000 300 #define IGC_I225_RX_LATENCY_2500 1485 +/* Transmit latency (for DMA timestamps) in nanosecond */ +#define IGC_I225_TX_DMA_LATENCY_10 13100 +#define IGC_I225_TX_DMA_LATENCY_100 1410 +#define IGC_I225_TX_DMA_LATENCY_1000 285 +#define IGC_I225_TX_DMA_LATENCY_2500 1485 + /* RX and TX descriptor control thresholds. * PTHRESH - MAC will consider prefetch if it has fewer than this number of * descriptors available in its onboard memory. @@ -410,6 +416,8 @@ enum igc_tx_flags { /* olinfo flags */ IGC_TX_FLAGS_IPV4 = 0x10, IGC_TX_FLAGS_CSUM = 0x20, + + IGC_TX_FLAGS_DMA_TSTAMP = 0x200, }; enum igc_boards { @@ -627,6 +635,8 @@ void igc_ptp_reset(struct igc_adapter *adapter); void igc_ptp_suspend(struct igc_adapter *adapter); void igc_ptp_stop(struct igc_adapter *adapter); ktime_t igc_ptp_rx_pktstamp(struct igc_adapter *adapter, __le32 *buf); +void igc_ptp_tx_dma_tstamp(struct igc_adapter *adapter, + struct sk_buff *skb, u64 tstamp); int igc_ptp_set_ts_config(struct net_device *netdev, struct ifreq *ifr); int igc_ptp_get_ts_config(struct net_device *netdev, struct ifreq *ifr); void igc_ptp_tx_hang(struct igc_adapter *adapter); diff --git a/drivers/net/ethernet/intel/igc/igc_base.h b/drivers/net/ethernet/intel/igc/igc_base.h index ce530f5fd7bd..672cf2d92165 100644 --- a/drivers/net/ethernet/intel/igc/igc_base.h +++ b/drivers/net/ethernet/intel/igc/igc_base.h @@ -16,7 +16,7 @@ union igc_adv_tx_desc { __le32 olinfo_status; } read; struct { - __le64 rsvd; /* Reserved */ + __le64 dma_tstamp; __le32 nxtseq_seed; __le32 status; } wb; diff --git a/drivers/net/ethernet/intel/igc/igc_defines.h b/drivers/net/ethernet/intel/igc/igc_defines.h index f7311aeb293b..baedf48b4e2e 100644 --- a/drivers/net/ethernet/intel/igc/igc_defines.h +++ b/drivers/net/ethernet/intel/igc/igc_defines.h @@ -312,6 +312,7 @@ #define IGC_TXD_CMD_DEXT 0x20000000 /* Desc extension (0 = legacy) */ #define IGC_TXD_CMD_VLE 0x40000000 /* Add VLAN tag */ #define IGC_TXD_STAT_DD 0x00000001 /* Descriptor Done */ +#define IGC_TXD_STAT_TS_STAT 0x00000002 /* DMA Timestamp in packet */ #define IGC_TXD_CMD_TCP 0x01000000 /* TCP packet */ #define IGC_TXD_CMD_IP 0x02000000 /* IP packet */ #define IGC_TXD_CMD_TSE 0x04000000 /* TCP Seg enable */ @@ -520,6 +521,7 @@ /* Transmit Scheduling */ #define IGC_TQAVCTRL_TRANSMIT_MODE_TSN 0x00000001 #define IGC_TQAVCTRL_ENHANCED_QAV 0x00000008 +#define IGC_TQAVCTRL_1588_STAT_EN 0x00000004 #define IGC_TXQCTL_QUEUE_MODE_LAUNCHT 0x00000001 #define IGC_TXQCTL_STRICT_CYCLE 0x00000002 diff --git a/drivers/net/ethernet/intel/igc/igc_ethtool.c b/drivers/net/ethernet/intel/igc/igc_ethtool.c index 8cc077b712ad..7d198fb6d619 100644 --- a/drivers/net/ethernet/intel/igc/igc_ethtool.c +++ b/drivers/net/ethernet/intel/igc/igc_ethtool.c @@ -1532,7 +1532,8 @@ static int igc_ethtool_get_ts_info(struct net_device *dev, SOF_TIMESTAMPING_SOFTWARE | SOF_TIMESTAMPING_TX_HARDWARE | SOF_TIMESTAMPING_RX_HARDWARE | - SOF_TIMESTAMPING_RAW_HARDWARE; + SOF_TIMESTAMPING_RAW_HARDWARE | + SOF_TIMESTAMPING_TX_HARDWARE_DMA_FETCH; info->tx_types = BIT(HWTSTAMP_TX_OFF) | @@ -1541,6 +1542,8 @@ static int igc_ethtool_get_ts_info(struct net_device *dev, info->rx_filters = BIT(HWTSTAMP_FILTER_NONE); info->rx_filters |= BIT(HWTSTAMP_FILTER_ALL); + info->flag = HWTSTAMP_FLAG_DMA_TIMESTAMP; + return 0; default: return -EOPNOTSUPP; diff --git a/drivers/net/ethernet/intel/igc/igc_main.c b/drivers/net/ethernet/intel/igc/igc_main.c index 671255edf3c2..daa6ca5acab3 100644 --- a/drivers/net/ethernet/intel/igc/igc_main.c +++ b/drivers/net/ethernet/intel/igc/igc_main.c @@ -1415,6 +1415,7 @@ static int igc_tso(struct igc_ring *tx_ring, static netdev_tx_t igc_xmit_frame_ring(struct sk_buff *skb, struct igc_ring *tx_ring) { + struct igc_adapter *adapter = netdev_priv(tx_ring->netdev); u16 count = TXD_USE_COUNT(skb_headlen(skb)); __be16 protocol = vlan_get_protocol(skb); struct igc_tx_buffer *first; @@ -1445,16 +1446,14 @@ static netdev_tx_t igc_xmit_frame_ring(struct sk_buff *skb, first->bytecount = skb->len; first->gso_segs = 1; - if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP)) { - struct igc_adapter *adapter = netdev_priv(tx_ring->netdev); - + if (unlikely((skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) && + !(skb_shinfo(skb)->tx_flags & SKBTX_HW_DMA_TSTAMP))) { /* FIXME: add support for retrieving timestamps from * the other timer registers before skipping the * timestamping request. */ if (adapter->tstamp_config.tx_type == HWTSTAMP_TX_ON && - !test_and_set_bit_lock(__IGC_PTP_TX_IN_PROGRESS, - &adapter->state)) { + !test_and_set_bit_lock(__IGC_PTP_TX_IN_PROGRESS, &adapter->state)) { skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS; tx_flags |= IGC_TX_FLAGS_TSTAMP; @@ -1463,6 +1462,14 @@ static netdev_tx_t igc_xmit_frame_ring(struct sk_buff *skb, } else { adapter->tx_hwtstamp_skipped++; } + } else if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_HW_DMA_TSTAMP)) { + if (adapter->tstamp_config.tx_type == HWTSTAMP_TX_ON && + adapter->tstamp_config.flags == HWTSTAMP_FLAG_DMA_TIMESTAMP) { + skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS; + tx_flags |= IGC_TX_FLAGS_DMA_TSTAMP; + } else { + adapter->tx_hwtstamp_skipped++; + } } if (skb_vlan_tag_present(skb)) { @@ -2741,6 +2748,13 @@ static bool igc_clean_tx_irq(struct igc_q_vector *q_vector, int napi_budget) if (!(eop_desc->wb.status & cpu_to_le32(IGC_TXD_STAT_DD))) break; + if (eop_desc->wb.status & cpu_to_le32(IGC_TXD_STAT_TS_STAT) && + tx_buffer->tx_flags & IGC_TX_FLAGS_DMA_TSTAMP) { + u64 tstamp = le64_to_cpu(eop_desc->wb.dma_tstamp); + + igc_ptp_tx_dma_tstamp(adapter, tx_buffer->skb, tstamp); + } + /* clear next_to_watch to prevent false hangs */ tx_buffer->next_to_watch = NULL; diff --git a/drivers/net/ethernet/intel/igc/igc_ptp.c b/drivers/net/ethernet/intel/igc/igc_ptp.c index 8dbb9f903ca7..631972d7e97b 100644 --- a/drivers/net/ethernet/intel/igc/igc_ptp.c +++ b/drivers/net/ethernet/intel/igc/igc_ptp.c @@ -432,6 +432,29 @@ static void igc_ptp_systim_to_hwtstamp(struct igc_adapter *adapter, } } +static void igc_ptp_dma_time_to_hwtstamp(struct igc_adapter *adapter, + struct skb_shared_hwtstamps *hwtstamps, + u64 systim) +{ + struct igc_hw *hw = &adapter->hw; + u32 sec, nsec; + + nsec = rd32(IGC_SYSTIML); + sec = rd32(IGC_SYSTIMH); + + if (unlikely(nsec < (systim & 0xFFFFFFFF))) + --sec; + + switch (adapter->hw.mac.type) { + case igc_i225: + memset(hwtstamps, 0, sizeof(*hwtstamps)); + hwtstamps->hwtstamp = ktime_set(sec, systim & 0xFFFFFFFF); + break; + default: + break; + } +} + /** * igc_ptp_rx_pktstamp - Retrieve timestamp from Rx packet buffer * @adapter: Pointer to adapter the packet buffer belongs to @@ -549,6 +572,28 @@ static void igc_ptp_enable_tx_timestamp(struct igc_adapter *adapter) rd32(IGC_TXSTMPH); } +static void igc_ptp_disable_dma_timestamp(struct igc_adapter *adapter) +{ + struct igc_hw *hw = &adapter->hw; + u32 tqavctrl; + + tqavctrl = rd32(IGC_TQAVCTRL); + tqavctrl &= ~IGC_TQAVCTRL_1588_STAT_EN; + + wr32(IGC_TQAVCTRL, tqavctrl); +} + +static void igc_ptp_enable_dma_timestamp(struct igc_adapter *adapter) +{ + struct igc_hw *hw = &adapter->hw; + u32 tqavctrl; + + tqavctrl = rd32(IGC_TQAVCTRL); + tqavctrl |= IGC_TQAVCTRL_1588_STAT_EN; + + wr32(IGC_TQAVCTRL, tqavctrl); +} + /** * igc_ptp_set_timestamp_mode - setup hardware for timestamping * @adapter: networking device structure @@ -562,9 +607,14 @@ static int igc_ptp_set_timestamp_mode(struct igc_adapter *adapter, switch (config->tx_type) { case HWTSTAMP_TX_OFF: igc_ptp_disable_tx_timestamp(adapter); + igc_ptp_disable_dma_timestamp(adapter); break; case HWTSTAMP_TX_ON: igc_ptp_enable_tx_timestamp(adapter); + + /* Ensure that flag only can be used during HWTSTAMP_TX_ON */ + if (config->flags == HWTSTAMP_FLAG_DMA_TIMESTAMP) + igc_ptp_enable_dma_timestamp(adapter); break; default: return -ERANGE; @@ -683,6 +733,39 @@ static void igc_ptp_tx_hwtstamp(struct igc_adapter *adapter) dev_kfree_skb_any(skb); } +void igc_ptp_tx_dma_tstamp(struct igc_adapter *adapter, + struct sk_buff *skb, u64 tstamp) +{ + struct skb_shared_hwtstamps shhwtstamps; + int adjust = 0; + + if (!(skb_shinfo(skb)->tx_flags & SKBTX_IN_PROGRESS)) + return; + + igc_ptp_dma_time_to_hwtstamp(adapter, &shhwtstamps, tstamp); + + switch (adapter->link_speed) { + case SPEED_10: + adjust = IGC_I225_TX_DMA_LATENCY_10; + break; + case SPEED_100: + adjust = IGC_I225_TX_DMA_LATENCY_100; + break; + case SPEED_1000: + adjust = IGC_I225_TX_DMA_LATENCY_1000; + break; + case SPEED_2500: + adjust = IGC_I225_TX_DMA_LATENCY_2500; + break; + } + + shhwtstamps.hwtstamp = + ktime_add_ns(shhwtstamps.hwtstamp, adjust); + + /* Notify the stack and free the skb after we've unlocked */ + skb_tstamp_tx(skb, &shhwtstamps); +} + /** * igc_ptp_tx_work * @work: pointer to work struct