implement PTP hardware clock timers (alarms) in the igb ethernet driver

Signed-off-by: Kieran Tyrrell <kie...@sienda.com>
---
 drivers/net/ethernet/intel/igb/igb.h      |  4 ++
 drivers/net/ethernet/intel/igb/igb_main.c | 34 ++++++++----
 drivers/net/ethernet/intel/igb/igb_ptp.c  | 92 +++++++++++++++++++++++++++++++
 3 files changed, 119 insertions(+), 11 deletions(-)

diff --git a/drivers/net/ethernet/intel/igb/igb.h 
b/drivers/net/ethernet/intel/igb/igb.h
index 5387b3a..ea4023d 100644
--- a/drivers/net/ethernet/intel/igb/igb.h
+++ b/drivers/net/ethernet/intel/igb/igb.h
@@ -352,6 +352,7 @@ struct hwmon_buff {
 
 #define IGB_N_EXTTS    2
 #define IGB_N_PEROUT   2
+#define IGB_N_ALARM    1
 #define IGB_N_SDP      4
 #define IGB_RETA_SIZE  128
 
@@ -473,6 +474,8 @@ struct igb_adapter {
        int copper_tries;
        struct e1000_info ei;
        u16 eee_advert;
+
+       bool timer_enabled;
 };
 
 /* flags controlling PTP/1588 function */
@@ -558,6 +561,7 @@ void igb_ptp_rx_pktstamp(struct igb_q_vector *q_vector, 
unsigned char *va,
 int igb_ptp_set_ts_config(struct net_device *netdev, struct ifreq *ifr);
 int igb_ptp_get_ts_config(struct net_device *netdev, struct ifreq *ifr);
 void igb_set_flag_queue_pairs(struct igb_adapter *, const u32);
+void igb_tt0_timer_enable(struct igb_adapter *adapter, bool enable);
 #ifdef CONFIG_IGB_HWMON
 void igb_sysfs_exit(struct igb_adapter *adapter);
 int igb_sysfs_init(struct igb_adapter *adapter);
diff --git a/drivers/net/ethernet/intel/igb/igb_main.c 
b/drivers/net/ethernet/intel/igb/igb_main.c
index 942a89f..c55577e 100644
--- a/drivers/net/ethernet/intel/igb/igb_main.c
+++ b/drivers/net/ethernet/intel/igb/igb_main.c
@@ -5638,17 +5638,29 @@ static void igb_tsync_interrupt(struct igb_adapter 
*adapter)
        }
 
        if (tsicr & TSINTR_TT0) {
-               spin_lock(&adapter->tmreg_lock);
-               ts = timespec64_add(adapter->perout[0].start,
-                                   adapter->perout[0].period);
-               /* u32 conversion of tv_sec is safe until y2106 */
-               wr32(E1000_TRGTTIML0, ts.tv_nsec);
-               wr32(E1000_TRGTTIMH0, (u32)ts.tv_sec);
-               tsauxc = rd32(E1000_TSAUXC);
-               tsauxc |= TSAUXC_EN_TT0;
-               wr32(E1000_TSAUXC, tsauxc);
-               adapter->perout[0].start = ts;
-               spin_unlock(&adapter->tmreg_lock);
+               if (adapter->timer_enabled) {
+                       /* disable timer */
+                       spin_lock(&adapter->tmreg_lock);
+                       igb_tt0_timer_enable(adapter, false);
+                       spin_unlock(&adapter->tmreg_lock);
+                       event.type = PTP_CLOCK_ALARM;
+                       event.index = 0;
+                       ptp_clock_event(adapter->ptp_clock, &event);
+               }
+               else {
+                       /* this is a periodic output interrupt */
+                       spin_lock(&adapter->tmreg_lock);
+                       ts = timespec64_add(adapter->perout[0].start,
+                                       adapter->perout[0].period);
+                       /* u32 conversion of tv_sec is safe until y2106 */
+                       wr32(E1000_TRGTTIML0, ts.tv_nsec);
+                       wr32(E1000_TRGTTIMH0, (u32)ts.tv_sec);
+                       tsauxc = rd32(E1000_TSAUXC);
+                       tsauxc |= TSAUXC_EN_TT0;
+                       wr32(E1000_TSAUXC, tsauxc);
+                       adapter->perout[0].start = ts;
+                       spin_unlock(&adapter->tmreg_lock);
+               }
                ack |= TSINTR_TT0;
        }
 
diff --git a/drivers/net/ethernet/intel/igb/igb_ptp.c 
b/drivers/net/ethernet/intel/igb/igb_ptp.c
index 336c103..2285ad8 100644
--- a/drivers/net/ethernet/intel/igb/igb_ptp.c
+++ b/drivers/net/ethernet/intel/igb/igb_ptp.c
@@ -19,6 +19,7 @@
 #include <linux/device.h>
 #include <linux/pci.h>
 #include <linux/ptp_classify.h>
+#include <linux/posix-timers.h>
 
 #include "igb.h"
 
@@ -1063,6 +1064,93 @@ int igb_ptp_set_ts_config(struct net_device *netdev, 
struct ifreq *ifr)
                -EFAULT : 0;
 }
 
+/* tmreg_lock should be held for this call */
+void igb_tt0_timer_enable(struct igb_adapter *adapter, bool enable)
+{
+       u32 tsauxc, tsim;
+       struct e1000_hw *hw = &adapter->hw;
+       tsauxc = rd32(E1000_TSAUXC);
+       tsim = rd32(E1000_TSIM);
+
+       if (enable)     {
+               tsauxc |= TSAUXC_EN_TT0;
+               tsim |= TSINTR_TT0;
+               wr32(E1000_TSAUXC, tsauxc);
+               wr32(E1000_TSIM, tsim);
+       }
+       else {
+               tsauxc &= ~TSAUXC_EN_TT0;
+               tsim &= ~TSINTR_TT0;
+               wr32(E1000_TSAUXC, tsauxc);
+               wr32(E1000_TSIM, tsim);
+       }
+}
+
+static int igb_ptp_timer_settime_i210(struct ptp_clock_info *ptp, struct 
timespec64 *ts)
+{
+       struct igb_adapter *igb =
+                               container_of(ptp, struct igb_adapter, ptp_caps);
+       struct e1000_hw *hw = &igb->hw;
+       unsigned long irqsaveflags;
+
+       spin_lock_irqsave(&igb->tmreg_lock, irqsaveflags);
+
+       if (timespec64_to_ns(ts) == 0) {
+               /* disable timer */
+               igb_tt0_timer_enable(igb, false);
+       }
+       else {
+               /* set trigger time and then enable timer */
+               wr32(E1000_TRGTTIML0, ts->tv_nsec);
+               wr32(E1000_TRGTTIMH0, (u32)ts->tv_sec);
+
+               igb_tt0_timer_enable(igb, true);
+       }
+
+       spin_unlock_irqrestore(&igb->tmreg_lock, irqsaveflags);
+
+       return 0;
+}
+
+int igb_ptp_timer_enable_i210(struct ptp_clock_info *ptp, bool enable)
+{
+       struct igb_adapter *igb =
+                       container_of(ptp, struct igb_adapter, ptp_caps);
+       struct e1000_hw *hw = &igb->hw;
+       u32 tsauxc;
+       unsigned long flags;
+       bool periodic_output_enabled;
+
+       if (enable)     {
+               if(igb->timer_enabled == false) {
+                       spin_lock_irqsave(&igb->tmreg_lock, flags);
+                       tsauxc = rd32(E1000_TSAUXC);
+                       periodic_output_enabled = tsauxc & (TSAUXC_EN_TT0 | 
TSAUXC_EN_CLK0 | TSAUXC_ST0);
+                       spin_unlock_irqrestore(&igb->tmreg_lock, flags);
+
+                       if (periodic_output_enabled) {
+                               /* we share the TT0 with the periodic output 
generator, and that is already enabled */
+                               return -EBUSY;
+                       }
+
+                       igb->timer_enabled = true;
+                       return 0;
+               }
+               /* timer is already enabled */
+               return -EINVAL;
+       }
+       else {
+               if(!igb->timer_enabled)
+                       return -EINVAL;
+
+               /* disable the timer */
+               igb_tt0_timer_enable(igb, false);
+               igb->timer_enabled = false;
+       }
+
+       return 0;
+}
+
 /**
  * igb_ptp_init - Initialize PTP functionality
  * @adapter: Board private structure
@@ -1125,6 +1213,7 @@ void igb_ptp_init(struct igb_adapter *adapter)
                snprintf(adapter->ptp_caps.name, 16, "%pm", netdev->dev_addr);
                adapter->ptp_caps.owner = THIS_MODULE;
                adapter->ptp_caps.max_adj = 62499999;
+               adapter->ptp_caps.n_alarm = IGB_N_ALARM;
                adapter->ptp_caps.n_ext_ts = IGB_N_EXTTS;
                adapter->ptp_caps.n_per_out = IGB_N_PEROUT;
                adapter->ptp_caps.n_pins = IGB_N_SDP;
@@ -1136,6 +1225,9 @@ void igb_ptp_init(struct igb_adapter *adapter)
                adapter->ptp_caps.settime64 = igb_ptp_settime_i210;
                adapter->ptp_caps.enable = igb_ptp_feature_enable_i210;
                adapter->ptp_caps.verify = igb_ptp_verify_pin;
+
+               adapter->ptp_caps.timerenable = igb_ptp_timer_enable_i210;
+               adapter->ptp_caps.timersettime = igb_ptp_timer_settime_i210;
                break;
        default:
                adapter->ptp_clock = NULL;
-- 
2.1.4



------------------------------------------------------------------------------
_______________________________________________
Linuxptp-devel mailing list
Linuxptp-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/linuxptp-devel

Reply via email to