diff options
author | Matthew Vick <matthew.vick@intel.com> | 2012-08-18 07:26:33 +0000 |
---|---|---|
committer | Jeff Kirsher <jeffrey.t.kirsher@intel.com> | 2012-09-17 02:04:24 -0700 |
commit | 1f6e8178d6851951876ad8524f4de7a0e6b111be (patch) | |
tree | e7a363f6216bc4456ae57d4cd1895bcf5f958ce8 /drivers/net/ethernet/intel/igb/igb_ptp.c | |
parent | 201987e3d03fadf0d87980981b7421198e3e5922 (diff) |
igb: Prevent dropped Tx timestamps via work items and interrupts.
In rare circumstances, it's possible a descriptor writeback will occur
before a timestamped Tx packet will go out on the wire, leading to the
driver believing the hardware failed to timestamp the packet. Schedule a
work item for 82576 and use the available time sync interrupt registers
on 82580 and above to account for this.
Cc: Richard Cochran <richardcochran@gmail.com>
Signed-off-by: Matthew Vick <matthew.vick@intel.com>
Tested-by: Jeff Pieper <jeffrey.e.pieper@intel.com>
Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
Diffstat (limited to 'drivers/net/ethernet/intel/igb/igb_ptp.c')
-rw-r--r-- | drivers/net/ethernet/intel/igb/igb_ptp.c | 102 |
1 files changed, 86 insertions, 16 deletions
diff --git a/drivers/net/ethernet/intel/igb/igb_ptp.c b/drivers/net/ethernet/intel/igb/igb_ptp.c index e69555f1f73e..d57060c0813d 100644 --- a/drivers/net/ethernet/intel/igb/igb_ptp.c +++ b/drivers/net/ethernet/intel/igb/igb_ptp.c @@ -289,6 +289,31 @@ static int igb_ptp_enable(struct ptp_clock_info *ptp, return -EOPNOTSUPP; } +/** + * igb_ptp_tx_work + * @work: pointer to work struct + * + * This work function polls the TSYNCTXCTL valid bit to determine when a + * timestamp has been taken for the current stored skb. + */ +void igb_ptp_tx_work(struct work_struct *work) +{ + struct igb_adapter *adapter = container_of(work, struct igb_adapter, + ptp_tx_work); + struct e1000_hw *hw = &adapter->hw; + u32 tsynctxctl; + + if (!adapter->ptp_tx_skb) + return; + + tsynctxctl = rd32(E1000_TSYNCTXCTL); + if (tsynctxctl & E1000_TSYNCTXCTL_VALID) + igb_ptp_tx_hwtstamp(adapter); + else + /* reschedule to check later */ + schedule_work(&adapter->ptp_tx_work); +} + static void igb_ptp_overflow_check(struct work_struct *work) { struct igb_adapter *igb = @@ -305,31 +330,25 @@ static void igb_ptp_overflow_check(struct work_struct *work) /** * igb_ptp_tx_hwtstamp - utility function which checks for TX time stamp - * @q_vector: pointer to q_vector containing needed info - * @buffer: pointer to igb_tx_buffer structure + * @adapter: Board private structure. * * If we were asked to do hardware stamping and such a time stamp is * available, then it must have been for this skb here because we only * allow only one such packet into the queue. */ -void igb_ptp_tx_hwtstamp(struct igb_q_vector *q_vector, - struct igb_tx_buffer *buffer_info) +void igb_ptp_tx_hwtstamp(struct igb_adapter *adapter) { - struct igb_adapter *adapter = q_vector->adapter; struct e1000_hw *hw = &adapter->hw; struct skb_shared_hwtstamps shhwtstamps; u64 regval; - /* if skb does not support hw timestamp or TX stamp not valid exit */ - if (likely(!(buffer_info->tx_flags & IGB_TX_FLAGS_TSTAMP)) || - !(rd32(E1000_TSYNCTXCTL) & E1000_TSYNCTXCTL_VALID)) - return; - regval = rd32(E1000_TXSTMPL); regval |= (u64)rd32(E1000_TXSTMPH) << 32; igb_ptp_systim_to_hwtstamp(adapter, &shhwtstamps, regval); - skb_tstamp_tx(buffer_info->skb, &shhwtstamps); + skb_tstamp_tx(adapter->ptp_tx_skb, &shhwtstamps); + dev_kfree_skb_any(adapter->ptp_tx_skb); + adapter->ptp_tx_skb = NULL; } void igb_ptp_rx_hwtstamp(struct igb_q_vector *q_vector, @@ -603,16 +622,26 @@ void igb_ptp_init(struct igb_adapter *adapter) spin_lock_init(&adapter->tmreg_lock); + INIT_WORK(&adapter->ptp_tx_work, igb_ptp_tx_work); + schedule_delayed_work(&adapter->ptp_overflow_work, IGB_SYSTIM_OVERFLOW_PERIOD); + /* Initialize the time sync interrupts for devices that support it. */ + if (hw->mac.type >= e1000_82580) { + wr32(E1000_TSIM, E1000_TSIM_TXTS); + wr32(E1000_IMS, E1000_IMS_TS); + } + adapter->ptp_clock = ptp_clock_register(&adapter->ptp_caps); if (IS_ERR(adapter->ptp_clock)) { adapter->ptp_clock = NULL; dev_err(&adapter->pdev->dev, "ptp_clock_register failed\n"); - } else + } else { dev_info(&adapter->pdev->dev, "added PHC on %s\n", adapter->netdev->name); + adapter->flags |= IGB_FLAG_PTP; + } } /** @@ -624,20 +653,61 @@ void igb_ptp_init(struct igb_adapter *adapter) void igb_ptp_stop(struct igb_adapter *adapter) { switch (adapter->hw.mac.type) { - case e1000_i211: - case e1000_i210: - case e1000_i350: - case e1000_82580: case e1000_82576: + case e1000_82580: + case e1000_i350: cancel_delayed_work_sync(&adapter->ptp_overflow_work); break; + case e1000_i210: + case e1000_i211: + /* No delayed work to cancel. */ + break; default: return; } + cancel_work_sync(&adapter->ptp_tx_work); + if (adapter->ptp_clock) { ptp_clock_unregister(adapter->ptp_clock); dev_info(&adapter->pdev->dev, "removed PHC on %s\n", adapter->netdev->name); + adapter->flags &= ~IGB_FLAG_PTP; } } + +/** + * igb_ptp_reset - Re-enable the adapter for PTP following a reset. + * @adapter: Board private structure. + * + * This function handles the reset work required to re-enable the PTP device. + **/ +void igb_ptp_reset(struct igb_adapter *adapter) +{ + struct e1000_hw *hw = &adapter->hw; + + if (!(adapter->flags & IGB_FLAG_PTP)) + return; + + switch (adapter->hw.mac.type) { + case e1000_82576: + /* Dial the nominal frequency. */ + wr32(E1000_TIMINCA, INCPERIOD_82576 | INCVALUE_82576); + break; + case e1000_82580: + case e1000_i350: + case e1000_i210: + case e1000_i211: + /* Enable the timer functions and interrupts. */ + wr32(E1000_TSAUXC, 0x0); + wr32(E1000_TSIM, E1000_TSIM_TXTS); + wr32(E1000_IMS, E1000_IMS_TS); + break; + default: + /* No work to do. */ + return; + } + + timecounter_init(&adapter->tc, &adapter->cc, + ktime_to_ns(ktime_get_real())); +} |