igb: Prevent dropped Tx timestamps via work items and interrupts.
[deliverable/linux.git] / drivers / net / ethernet / intel / igb / igb_ptp.c
index e69555f1f73eff084a372f3146547bc220d74d2c..d57060c0813d64c5348c9c66e4183925b33cb59c 100644 (file)
@@ -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()));
+}
This page took 0.02534 seconds and 5 git commands to generate.