Hi,

This patch adds timestamping for PTP on the 82574L. It must be applied 
on the e1000e 1.6.3 driver.

It has been tested on Ubuntu 11.04 (kernel 2.6.38) with 82574L and 
82566DC-2 (which are not HW timestamp compatible).

This implementation is based on the timestamping functionality of the 
igb driver 3.1.16; it will probably differ from the official version 
that Intel is currently developing for the e1000e.

Comments are welcome!

Regards,

Maxime Larocque
Orthogone Technologies


Signed-off-by: Maxime Larocque <[email protected]>

diff -rup e1000e-1.6.3//src/defines.h main_274//src/defines.h
--- e1000e-1.6.3//src/defines.h    2011-09-20 19:04:40.000000000 -0400
+++ main_274//src/defines.h    2011-10-31 16:21:55.683753000 -0400
@@ -227,6 +227,26 @@
  #define E1000_RXD_SPC_CFI_MASK  0x1000  /* CFI is bit 12 */
  #define E1000_RXD_SPC_CFI_SHIFT 12

+#define E1000_RXDEXT_STAT_TST      0x00000100    /* Time stamp taken */
+#define E1000_RXDEXT_PKTTYPE_MASK  0x000F0000
+#define E1000_RXDEXT_PKTTYPE_SHIFT 16
+
+/* Packet Types as indicated in the receive descriptor */
+#define E1000_RXDEXT_PKTTYPE_NONE                0x0
+#define E1000_RXDEXT_PKTTYPE_IPV4                0x1 /* IPV4 */
+#define E1000_RXDEXT_PKTTYPE_IPV4_TCP_UDP       0x2 /* IPV4, TCP/UDP */
+#define E1000_RXDEXT_PKTTYPE_IPV4_V6               0x3 /* IPV4, IPV6 */
+#define E1000_RXDEXT_PKTTYPE_IPV4_V6_TCP_UDP    0x4 /* IPV4, IPV6, 
TCP/UDP */
+#define E1000_RXDEXT_PKTTYPE_IPV6                 0x5 /* IPV6 */
+#define E1000_RXDEXT_PKTTYPE_IPV6_TCP_UDP          0x6 /* IPV6, TCP/UDP */
+#define E1000_RXDEXT_PKTTYPE_IPV4_ISCSI            0x7 /* IPV4, TCP, 
ISCSI */
+#define E1000_RXDEXT_PKTTYPE_IPV4_NFS           0x8 /* IPV4, TCP/UDP, 
NFS */
+#define E1000_RXDEXT_PKTTYPE_IPV4_V6_ISCSI        0x9 /* IPV4, IPV6, 
TCP, ISCSI */
+#define E1000_RXDEXT_PKTTYPE_IPV4_V6_NFS        0xA /* IPV4, IPV6, 
TCP/UDP, NFS */
+#define E1000_RXDEXT_PKTTYPE_IPV6_ISCSI            0xB /* IPV6, TCP, 
ISCSI */
+#define E1000_RXDEXT_PKTTYPE_IPV6_NFS              0xC /* IPV6, 
TCP/UDP, NFS */
+#define E1000_RXDEXT_PKTTYPE_PTP                 0xE /* PTP packet */
+
  #define E1000_RXDEXT_STATERR_LB    0x00040000
  #define E1000_RXDEXT_STATERR_CE    0x01000000
  #define E1000_RXDEXT_STATERR_SE    0x02000000
@@ -1035,7 +1055,8 @@
  #define E1000_TSYNCRXCFG_PTP_V2_SIGNALLING_MESSAGE           0x0C00
  #define E1000_TSYNCRXCFG_PTP_V2_MANAGEMENT_MESSAGE           0x0D00

-#define E1000_TIMINCA_16NS_SHIFT 24
+#define E1000_TIMINCA_INCVALUE_SHIFT  0
+#define E1000_TIMINCA_INCPERIOD_SHIFT 24

  /* PCI Express Control */
  #define E1000_GCR_RXD_NO_SNOOP          0x00000001
diff -rup e1000e-1.6.3//src/e1000.h main_274//src/e1000.h
--- e1000e-1.6.3//src/e1000.h    2011-09-20 19:04:40.000000000 -0400
+++ main_274//src/e1000.h    2011-10-31 16:21:55.683753000 -0400
@@ -38,6 +38,12 @@
  #include <linux/pci.h>
  #include <linux/if_vlan.h>

+#ifdef HAVE_HW_TIME_STAMP
+#include <linux/clocksource.h>
+#include <linux/timecompare.h>
+#include <linux/net_tstamp.h>
+#endif
+
  #include "kcompat.h"
  #include "hw.h"

@@ -161,6 +167,9 @@ struct e1000_buffer {
              u16 next_to_watch;
              unsigned int segs;
              unsigned int bytecount;
+#ifdef HAVE_HW_TIME_STAMP
+            u8 shtx;
+#endif
              u16 mapped_as_page;
          };
          /* Rx */
@@ -320,6 +329,14 @@ struct e1000_adapter {
  #ifndef HAVE_NETDEV_STATS_IN_NETDEV
      struct net_device_stats net_stats;
  #endif
+#ifdef HAVE_HW_TIME_STAMP
+    struct cyclecounter cycles;
+    struct timecounter clock;
+    struct timecompare compare;
+    struct hwtstamp_config hwtstamp_config;
+    struct timer_list reset_rx_hwtstamp_timer;
+    struct work_struct reset_rx_hwtstamp_task;
+#endif

      /* structs defined in e1000_hw.h */
      struct e1000_hw hw;
@@ -430,6 +447,8 @@ struct e1000_info {
  #define FLAG2_NO_DISABLE_RX               (1 << 10)
  #define FLAG2_PCIM2PCI_ARBITER_WA         (1 << 11)

+#define E1000_82574_TSYNC_SHIFT           0
+
  #define E1000_RX_DESC_PS(R, i)        \
      (&(((union e1000_rx_desc_packet_split *)((R).desc))[i]))
  #define E1000_RX_DESC_EXT(R, i)        \
diff -rup e1000e-1.6.3//src/netdev.c main_274//src/netdev.c
--- e1000e-1.6.3//src/netdev.c    2011-09-20 19:04:40.000000000 -0400
+++ main_274//src/netdev.c    2011-10-31 16:21:55.683753000 -0400
@@ -766,6 +766,128 @@ static int e1000_desc_unused(struct e100
      return ring->count + ring->next_to_clean - ring->next_to_use - 1;
  }

+#ifdef HAVE_HW_TIME_STAMP
+/**
+ * e1000e_clear_rx_hwtstamp - clear RX timestamp if needed
+ *
+ * this function exists because we must check
+ * if RXSTMP register is stuck after packet lost
+ *
+ * @adapter: board private structure
+ **/
+static void e1000e_clear_rx_hwtstamp(struct e1000_adapter *adapter)
+{
+    struct e1000_hw *hw = &adapter->hw;
+    u64 regval;
+
+    if (test_bit(__E1000_DOWN, &adapter->state))
+        return;
+
+    /*
+     * If this bit is set, then the RX registers contain the time stamp. No
+     * other packet will be time stamped until we read these registers, so
+     * read the registers to make them available again.
+     */
+    if(!(E1000_READ_REG(hw, E1000_TSYNCRXCTL) & E1000_TSYNCRXCTL_VALID))
+        return;
+
+    regval = E1000_READ_REG(hw, E1000_RXSTMPL);
+    regval |= (u64)E1000_READ_REG(hw, E1000_RXSTMPH) << 32;
+
+    printk(KERN_INFO "e1000e: RXSTMP has been cleared on %s\n", 
adapter->netdev->name);
+
+    /* Reset the timer */
+    if (!test_bit(__E1000_DOWN, &adapter->state))
+        if ((adapter->hwtstamp_config.rx_filter != HWTSTAMP_FILTER_NONE)
+ && (adapter->hwtstamp_config.rx_filter != HWTSTAMP_FILTER_ALL))
+            mod_timer(&adapter->reset_rx_hwtstamp_timer,
+                  round_jiffies(jiffies + 2 * HZ));
+}
+
+/**
+ * e1000e_reset_rx_hwtstamp_task - work thread to clear RX timestamp
+ * @work: pointer to our work struct
+ *
+ * this worker thread exists because we must check
+ * if RXSTMP register is stuck after packet lost
+ **/
+static void e1000e_reset_rx_hwtstamp_task(struct work_struct *work)
+{
+    struct e1000_adapter *adapter = container_of(work,
+                    struct e1000_adapter, reset_rx_hwtstamp_task);
+
+    e1000e_clear_rx_hwtstamp(adapter);
+}
+
+/**
+ * e1000_reset_rx_hwtstamp - Timer Call-back
+ * @data: pointer to adapter cast into an unsigned long
+ *
+ * Check if RXSTMP register is stuck because of a lost packet
+ */
+static void e1000_reset_rx_hwtstamp(unsigned long data)
+{
+    struct e1000_adapter *adapter = (struct e1000_adapter *) data;
+
+    if (test_bit(__E1000_DOWN, &adapter->state))
+        return;
+
+    /* Do the rest outside of interrupt context */
+    schedule_work(&adapter->reset_rx_hwtstamp_task);
+}
+
+
+/**
+ * e1000_systim_to_hwtstamp - convert system time value to hw timestamp
+ * @adapter: board private structure
+ * @shhwtstamps: timestamp structure to update
+ * @regval: unsigned 64bit system time value.
+ *
+ * We need to convert the system time value stored in the RX/TXSTMP 
registers
+ * into a hwtstamp which can be used by the upper level timestamping 
functions
+ */
+static void e1000_systim_to_hwtstamp(struct e1000_adapter *adapter,
+                                   struct skb_shared_hwtstamps 
*shhwtstamps,
+                                   u64 regval)
+{
+    u64 ns;
+
+    ns = timecounter_cyc2time(&adapter->clock, regval);
+    timecompare_update(&adapter->compare, ns);
+    memset(shhwtstamps, 0, sizeof(struct skb_shared_hwtstamps));
+    shhwtstamps->hwtstamp = ns_to_ktime(ns);
+    shhwtstamps->syststamp = timecompare_transform(&adapter->compare, ns);
+}
+
+static void e1000_rx_hwtstamp(struct e1000_adapter *adapter, struct 
sk_buff *skb)
+{
+    struct e1000_hw *hw = &adapter->hw;
+    u64 regval;
+
+    /*
+     * If this bit is set, then the RX registers contain the time stamp. No
+     * other packet will be time stamped until we read these registers, so
+     * read the registers to make them available again. Because only one
+     * packet can be time stamped at a time, we know that the register
+     * values must belong to this one here and therefore we don't need to
+     * compare any of the additional attributes stored for it.
+     *
+     * If nothing went wrong, then it should have a skb_shared_tx that we
+     * can turn into a skb_shared_hwtstamps.
+     */
+    if(!(E1000_READ_REG(hw, E1000_TSYNCRXCTL) & E1000_TSYNCRXCTL_VALID))
+        return;
+
+    regval = E1000_READ_REG(hw, E1000_RXSTMPL);
+    regval |= (u64)E1000_READ_REG(hw, E1000_RXSTMPH) << 32;
+
+    e1000_systim_to_hwtstamp(adapter, skb_hwtstamps(skb), regval);
+
+    /* Reset the timer */
+    mod_timer(&adapter->reset_rx_hwtstamp_timer, round_jiffies(jiffies 
+ 2 * HZ));
+}
+#endif
+
  /**
   * e1000_receive_skb - helper function to handle Rx indications
   * @adapter: board private structure
@@ -775,7 +897,7 @@ static int e1000_desc_unused(struct e100
   **/
  static void e1000_receive_skb(struct e1000_adapter *adapter,
                    struct net_device *netdev, struct sk_buff *skb,
-                  u8 status, __le16 vlan)
+                  u32 staterr, __le16 vlan)
  {
  #ifndef CONFIG_E1000E_NAPI
      int ret;
@@ -783,26 +905,31 @@ static void e1000_receive_skb(struct e10
  #ifndef HAVE_VLAN_RX_REGISTER
      u16 tag = le16_to_cpu(vlan);
  #endif
+#ifdef HAVE_HW_TIME_STAMP
+    if ((adapter->hw.mac.type == e1000_82574) && (staterr & 
E1000_RXDEXT_STAT_TST))
+        e1000_rx_hwtstamp(adapter, skb);
+#endif
+
      skb->protocol = eth_type_trans(skb, netdev);

  #ifdef CONFIG_E1000E_NAPI
  #ifdef HAVE_VLAN_RX_REGISTER
  #ifdef NETIF_F_HW_VLAN_TX
-    if (adapter->vlgrp && (status & E1000_RXD_STAT_VP))
+    if (adapter->vlgrp && (staterr & E1000_RXD_STAT_VP))
          vlan_gro_receive(&adapter->napi, adapter->vlgrp,
                   le16_to_cpu(vlan), skb);
      else
  #endif /* NETIF_F_HW_VLAN_TX */
          napi_gro_receive(&adapter->napi, skb);
  #else /* HAVE_VLAN_RX_REGISTER */
-    if (status & E1000_RXD_STAT_VP)
+    if (staterr & E1000_RXD_STAT_VP)
          __vlan_hwaccel_put_tag(skb, tag);

      napi_gro_receive(&adapter->napi, skb);
  #endif /* HAVE_VLAN_RX_REGISTER */
  #else /* CONFIG_E1000E_NAPI */
  #ifdef NETIF_F_HW_VLAN_TX
-    if (adapter->vlgrp && (status & E1000_RXD_STAT_VP))
+    if (adapter->vlgrp && (staterr & E1000_RXD_STAT_VP))
          ret = vlan_hwaccel_rx(skb, adapter->vlgrp, le16_to_cpu(vlan));
      else
  #endif
@@ -1434,6 +1561,35 @@ static void e1000_print_hw_hang(struct w
          e_err("Try turning off Tx pause (flow control) via ethtool\n");
  }

+#ifdef HAVE_HW_TIME_STAMP
+/**
+ * e1000_tx_hwtstamp - utility function which checks for TX time stamp
+ * @q_vector: pointer to q_vector containing needed info
+ * @buffer: pointer to igb_buffer 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.
+ */
+static void e1000_tx_hwtstamp(struct e1000_adapter *adapter, struct 
e1000_buffer *buffer_info)
+{
+    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 ( !(buffer_info->shtx & SKBTX_HW_TSTAMP) ||
+        !(E1000_READ_REG(hw, E1000_TSYNCTXCTL) & E1000_TSYNCTXCTL_VALID))
+        return;
+
+    regval = E1000_READ_REG(hw, E1000_TXSTMPL);
+    regval |= (u64)E1000_READ_REG(hw, E1000_TXSTMPH) << 32;
+
+    e1000_systim_to_hwtstamp(adapter, &shhwtstamps, regval);
+    skb_tstamp_tx(buffer_info->skb, &shhwtstamps);
+}
+
+#endif
  /**
   * e1000_clean_tx_irq - Reclaim resources after transmit completes
   * @adapter: board private structure
@@ -1462,6 +1618,17 @@ static bool e1000_clean_tx_irq(struct e1
              buffer_info = &tx_ring->buffer_info[i];
              cleaned = (i == eop);

+#ifdef HAVE_HW_TIME_STAMP
+            if (buffer_info->skb) {
+#if ( LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36) )
+                skb_tx_timestamp(buffer_info->skb);
+#endif
+
+                if (hw->mac.type == e1000_82574)
+                    e1000_tx_hwtstamp(adapter, buffer_info);
+            }
+#endif
+
              if (cleaned) {
                  total_tx_packets += buffer_info->segs;
                  total_tx_bytes += buffer_info->bytecount;
@@ -3088,6 +3255,9 @@ static int e1000_poll(struct napi_struct
  #endif
      /* If Tx completed and all Rx work done, exit the polling mode */
      if (work_done < budget) {
+#ifdef HAVE_HW_TIME_STAMP
+        e1000e_clear_rx_hwtstamp(adapter);
+#endif
          napi_complete(napi);
          if (adapter->itr_setting & 3)
              e1000_set_itr(adapter);
@@ -3837,6 +4007,84 @@ static void e1000_configure_rx(struct e1
      ew32(RCTL, rctl);
  }

+#ifdef HAVE_HW_TIME_STAMP
+/**
+ * e1000_read_clock - read raw cycle counter (to be used by time counter)
+ */
+static cycle_t e1000_read_clock(const struct cyclecounter *tc)
+{
+    struct e1000_adapter *adapter =
+        container_of(tc, struct e1000_adapter, cycles);
+    struct e1000_hw *hw = &adapter->hw;
+
+    u32 systim_read1_low;
+    u32 systim_read1_high;
+    u32 systim_read2_low;
+    u32 systim_read2_high;
+
+    systim_read1_low = E1000_READ_REG(hw, E1000_SYSTIML);
+    systim_read1_high = E1000_READ_REG(hw, E1000_SYSTIMH);
+    systim_read2_low = E1000_READ_REG(hw, E1000_SYSTIML);
+    systim_read2_high = E1000_READ_REG(hw, E1000_SYSTIMH);
+
+    /*
+     * The timestamp latches on lowest register read?
+     * It does not happen below condition is sometimes true...
+     */
+    if (systim_read1_low > systim_read2_low) {
+        if (systim_read1_high == systim_read2_high) {
+            /* Wrap around occurs while doing the first read, timestamp 
latch failed! */
+            printk(KERN_INFO "e1000e: SYSTIMH did not latch properly on 
%s\n", adapter->netdev->name);
+            return (cycle_t)systim_read2_low + 
((cycle_t)systim_read2_high << 32);
+        }
+    }
+
+    return (cycle_t)systim_read1_low + ((cycle_t)systim_read1_high << 32);
+}
+
+/**
+ * e1000_configure_systim - Configure System Time after Reset
+ * @adapter: board private structure
+ *
+ * Configure the System Time of the MAC after a reset.
+ **/
+static void e1000_configure_systim(struct e1000_adapter *adapter)
+{
+    struct e1000_hw *hw = &adapter->hw;
+
+    switch (hw->mac.type) {
+    case e1000_82574:
+        /*
+         * Initialize hardware timer: we keep it running just in case
+         * that some program needs it later on.
+         */
+        E1000_WRITE_REG(hw, E1000_TIMINCA,
+                        (1 << E1000_TIMINCA_INCPERIOD_SHIFT) |
+                        (40 << E1000_TIMINCA_INCVALUE_SHIFT));
+
+        /* Set registers so that rollover occurs soon to test this. */
+        E1000_WRITE_REG(hw, E1000_SYSTIML, 0x00000000);
+        E1000_WRITE_REG(hw, E1000_SYSTIMH, 0xFF800000);
+        e1e_flush();
+
+        timecounter_init(&adapter->clock,
+ &adapter->cycles,
+                 ktime_to_ns(ktime_get_real()));
+        /*
+         * Synchronize our NIC clock against system wall clock. NIC
+         * time stamp reading requires ~3us per sample, each sample
+         * was pretty stable even under load => only require 10
+         * samples for each offset comparison.
+         */
+        timecompare_update(&adapter->compare, 0);
+        break;
+    default:
+        /* only 82574 supports timesync */
+        break;
+    }
+}
+#endif /* HAVE_HW_TIME_STAMP */
+
  /**
   *  e1000_update_mc_addr_list - Update Multicast addresses
   *  @hw: pointer to the HW structure
@@ -4003,6 +4251,9 @@ static void e1000_configure(struct e1000
      e1000_configure_rx(adapter);
      adapter->alloc_rx_buf(adapter, e1000_desc_unused(adapter->rx_ring),
                    GFP_ATOMIC);
+#ifdef HAVE_HW_TIME_STAMP
+    e1000_configure_systim(adapter);
+#endif
  }

  /**
@@ -4315,6 +4566,9 @@ void e1000e_down(struct e1000_adapter *a

      del_timer_sync(&adapter->watchdog_timer);
      del_timer_sync(&adapter->phy_info_timer);
+#ifdef HAVE_HW_TIME_STAMP
+    del_timer_sync(&adapter->reset_rx_hwtstamp_timer);
+#endif

      netif_carrier_off(netdev);

@@ -5408,6 +5662,7 @@ link_up:
  #define E1000_TX_FLAGS_VLAN        0x00000002
  #define E1000_TX_FLAGS_TSO        0x00000004
  #define E1000_TX_FLAGS_IPV4        0x00000008
+#define E1000_TX_FLAGS_TSTAMP    0x00000010
  #define E1000_TX_FLAGS_VLAN_MASK    0xffff0000
  #define E1000_TX_FLAGS_VLAN_SHIFT    16

@@ -5631,6 +5886,13 @@ static int e1000_tx_map(struct e1000_ada
      else
          i--;

+#ifdef HAVE_HW_TIME_STAMP
+#ifdef SKB_SHARED_TX_IS_UNION
+    tx_ring->buffer_info[i].shtx = skb_shinfo(skb)->tx_flags.flags;
+#else
+    tx_ring->buffer_info[i].shtx = skb_shinfo(skb)->tx_flags;
+#endif
+#endif
  #ifdef NETIF_F_TSO
      segs = skb_shinfo(skb)->gso_segs ? : 1;
  #else
@@ -5691,6 +5953,12 @@ static void e1000_tx_queue(struct e1000_
          txd_upper |= (tx_flags & E1000_TX_FLAGS_VLAN_MASK);
      }

+    if ((tx_flags & E1000_TX_FLAGS_TSTAMP) && (adapter->hw.mac.type == 
e1000_82574))
+    {
+        txd_lower |= E1000_TXD_CMD_DEXT | E1000_TXD_DTYP_D;
+        txd_upper |= E1000_TXD_EXTCMD_TSTAMP;
+    }
+
      i = tx_ring->next_to_use;

      do {
@@ -5898,6 +6166,20 @@ static netdev_tx_t e1000_xmit_frame(stru
      if (e1000_maybe_stop_tx(netdev, count + 2))
          return NETDEV_TX_BUSY;

+#ifdef HAVE_HW_TIME_STAMP
+#ifdef SKB_SHARED_TX_IS_UNION
+    if (unlikely(skb_shinfo(skb)->tx_flags.flags & SKBTX_HW_TSTAMP)) {
+        skb_shinfo(skb)->tx_flags.flags |= SKBTX_IN_PROGRESS;
+        tx_flags |= E1000_TX_FLAGS_TSTAMP;
+    }
+#else
+    if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP)) {
+        skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS;
+        tx_flags |= E1000_TX_FLAGS_TSTAMP;
+    }
+#endif
+
+#endif
  #ifdef NETIF_F_HW_VLAN_TX
      if (vlan_tx_tag_present(skb)) {
          tx_flags |= E1000_TX_FLAGS_VLAN;
@@ -6151,6 +6433,164 @@ static int e1000_mii_ioctl(struct net_de
  }
  #endif /* defined(SIOCGMIIPHY||SIOCGMIIREG||SIOCSMIIREG) */

+#ifdef HAVE_HW_TIME_STAMP
+/**
+ * e1000_hwtstamp_ioctl - control hardware time stamping
+ * @netdev:
+ * @ifreq:
+ * @cmd:
+ *
+ * Outgoing time stamping can be enabled and disabled. Play nice and
+ * disable it when requested, although it shouldn't case any overhead
+ * when no packet needs it. At most one packet in the queue may be
+ * marked for time stamping, otherwise it would be impossible to tell
+ * for sure to which packet the hardware time stamp belongs.
+ *
+ * Incoming time stamping has to be configured via the hardware
+ * filters. Not all combinations are supported, in particular event
+ * type has to be specified. Matching the kind of event packet is
+ * not supported, with the exception of "all V2 events regardless of
+ * level 2 or 4".
+ *
+ **/
+static int e1000_hwtstamp_ioctl(struct net_device *netdev,
+                  struct ifreq *ifr, int cmd)
+{
+    struct e1000_adapter *adapter = netdev_priv(netdev);
+    struct e1000_hw *hw = &adapter->hw;
+    struct hwtstamp_config config;
+    u32 tsync_tx_ctl = E1000_TSYNCTXCTL_ENABLED;
+    u32 tsync_rx_ctl = E1000_TSYNCRXCTL_ENABLED;
+    u32 tsync_rx_cfg = 0;
+    bool is_l4 = false;
+    bool is_l2 = false;
+    u32 regval;
+
+    if (copy_from_user(&config, ifr->ifr_data, sizeof(config)))
+        return -EFAULT;
+
+    /* reserved for future extensions */
+    if (config.flags)
+        return -EINVAL;
+
+    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;
+        break;
+    case HWTSTAMP_FILTER_PTP_V1_L4_EVENT:
+    case HWTSTAMP_FILTER_PTP_V2_L4_EVENT:
+    case HWTSTAMP_FILTER_PTP_V2_L2_EVENT:
+    case HWTSTAMP_FILTER_ALL:
+        /*
+         * register TSYNCRXCFG must be set, therefore it is not
+         * possible to time stamp both Sync and Delay_Req messages
+         * => fall back to time stamping all packets
+         */
+        tsync_rx_ctl |= E1000_TSYNCRXCTL_TYPE_ALL;
+        config.rx_filter = HWTSTAMP_FILTER_ALL;
+        break;
+    case HWTSTAMP_FILTER_PTP_V1_L4_SYNC:
+        tsync_rx_ctl |= E1000_TSYNCRXCTL_TYPE_L4_V1;
+        tsync_rx_cfg = E1000_TSYNCRXCFG_PTP_V1_SYNC_MESSAGE;
+        is_l4 = true;
+        break;
+    case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ:
+        tsync_rx_ctl |= E1000_TSYNCRXCTL_TYPE_L4_V1;
+        tsync_rx_cfg = E1000_TSYNCRXCFG_PTP_V1_DELAY_REQ_MESSAGE;
+        is_l4 = true;
+        break;
+    case HWTSTAMP_FILTER_PTP_V2_L2_SYNC:
+    case HWTSTAMP_FILTER_PTP_V2_L4_SYNC:
+    case HWTSTAMP_FILTER_PTP_V2_SYNC:
+        tsync_rx_ctl |= E1000_TSYNCRXCTL_TYPE_L2_L4_V2;
+        tsync_rx_cfg = E1000_TSYNCRXCFG_PTP_V2_SYNC_MESSAGE;
+        is_l2 = true;
+        is_l4 = true;
+        if (config.rx_filter != HWTSTAMP_FILTER_PTP_V2_SYNC) {
+            config.rx_filter = HWTSTAMP_FILTER_SOME;
+        }
+        break;
+    case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ:
+    case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ:
+    case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ:
+        tsync_rx_ctl |= E1000_TSYNCRXCTL_TYPE_L2_L4_V2;
+        tsync_rx_cfg = E1000_TSYNCRXCFG_PTP_V2_DELAY_REQ_MESSAGE;
+        is_l2 = true;
+        is_l4 = true;
+        if (config.rx_filter != HWTSTAMP_FILTER_PTP_V2_DELAY_REQ) {
+            config.rx_filter = HWTSTAMP_FILTER_SOME;
+        }
+        break;
+    case HWTSTAMP_FILTER_PTP_V2_EVENT:
+        tsync_rx_ctl |= E1000_TSYNCRXCTL_TYPE_EVENT_V2;
+        config.rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT;
+        is_l2 = true;
+        is_l4 = true;
+        break;
+    default:
+        return -ERANGE;
+    }
+
+    if (hw->mac.type != e1000_82574) {
+        if (tsync_rx_ctl | tsync_tx_ctl)
+            return -EINVAL;
+        return 0;
+    }
+
+    /* Configure system time */
+    if (tsync_rx_ctl | tsync_tx_ctl)
+        e1000_configure_systim(adapter);
+
+    /* enable/disable TX */
+    regval = E1000_READ_REG(hw, E1000_TSYNCTXCTL);
+    regval &= ~E1000_TSYNCTXCTL_ENABLED;
+    regval |= tsync_tx_ctl;
+    E1000_WRITE_REG(hw, E1000_TSYNCTXCTL, regval);
+
+    /* enable/disable RX */
+    regval = E1000_READ_REG(hw, E1000_TSYNCRXCTL);
+    regval &= ~(E1000_TSYNCRXCTL_ENABLED | E1000_TSYNCRXCTL_TYPE_MASK);
+    regval |= tsync_rx_ctl;
+    E1000_WRITE_REG(hw, E1000_TSYNCRXCTL, regval);
+
+    /* define which PTP packets are time stamped */
+    regval = (tsync_rx_cfg << 16);
+    regval |= ETH_P_1588;        // No htons is required here.
+    E1000_WRITE_REG(hw, E1000_RXMTRL, regval);
+
+#define PTP_PORT 319
+    /* Filter by destination port */
+    if (is_l4)
+        E1000_WRITE_REG(hw, E1000_RXUDP, htons(PTP_PORT));
+
+    e1e_flush();
+
+    adapter->hwtstamp_config = config;
+
+    /* clear TX/RX time stamp registers, just to be sure */
+    regval = E1000_READ_REG(hw, E1000_TXSTMPH);
+    regval = E1000_READ_REG(hw, E1000_RXSTMPH);
+
+    /* Reset the timer */
+    if (tsync_rx_ctl)
+        mod_timer(&adapter->reset_rx_hwtstamp_timer, 
round_jiffies(jiffies + 2 * HZ));
+
+    return copy_to_user(ifr->ifr_data, &config, sizeof(config)) ?
+        -EFAULT : 0;
+}
+
+#endif
+
  static int e1000_ioctl(struct net_device *netdev, struct ifreq *ifr, 
int cmd)
  {
      switch (cmd) {
@@ -6166,6 +6606,10 @@ static int e1000_ioctl(struct net_device
  #if defined(SIOCGMIIPHY) || defined(SIOCGMIIREG) || defined(SIOCSMIIREG)
          return e1000_mii_ioctl(netdev, ifr, cmd);
  #endif
+#ifdef HAVE_HW_TIME_STAMP
+    case SIOCSHWTSTAMP:
+        return e1000_hwtstamp_ioctl(netdev, ifr, cmd);
+#endif
  #ifdef ETHTOOL_OPS_COMPAT
      case SIOCETHTOOL:
          return ethtool_ioctl(ifr);
@@ -7188,6 +7632,12 @@ static int __devinit e1000_probe(struct
      adapter->phy_info_timer.function = e1000_update_phy_info;
      adapter->phy_info_timer.data = (unsigned long)adapter;

+#ifdef HAVE_HW_TIME_STAMP
+    init_timer(&adapter->reset_rx_hwtstamp_timer);
+    adapter->reset_rx_hwtstamp_timer.function = e1000_reset_rx_hwtstamp;
+    adapter->reset_rx_hwtstamp_timer.data = (unsigned long)adapter;
+#endif
+
      INIT_WORK(&adapter->reset_task, e1000_reset_task);
      INIT_WORK(&adapter->watchdog_task, e1000_watchdog_task);
      INIT_WORK(&adapter->downshift_task, e1000e_downshift_workaround);
@@ -7196,6 +7646,9 @@ static int __devinit e1000_probe(struct
  #ifndef HAVE_ETHTOOL_SET_PHYS_ID
      INIT_WORK(&adapter->led_blink_task, e1000e_led_blink_task);
  #endif
+#ifdef HAVE_HW_TIME_STAMP
+    INIT_WORK(&adapter->reset_rx_hwtstamp_task, 
e1000e_reset_rx_hwtstamp_task);
+#endif

      /* Initialize link parameters. User can change them with ethtool */
      adapter->hw.mac.autoneg = 1;
@@ -7278,6 +7731,47 @@ static int __devinit e1000_probe(struct
      if (pci_dev_run_wake(pdev))
          pm_runtime_put_noidle(&pdev->dev);

+#ifdef HAVE_HW_TIME_STAMP
+    switch (hw->mac.type) {
+    case e1000_82574:
+        /*
+         * Initialize hardware timer: we keep it running just in case
+         * that some program needs it later on.
+         */
+        memset(&adapter->cycles, 0, sizeof(adapter->cycles));
+        adapter->cycles.read = e1000_read_clock;
+        adapter->cycles.mask = CLOCKSOURCE_MASK(64);
+        adapter->cycles.mult = 1;
+        /**
+         * REMOVED:
+         * Scale the NIC clock cycle by a large factor so that
+         * relatively small clock corrections can be added or
+         * substracted at each clock tick. The drawbacks of a large
+         * factor are a) that the clock register overflows more quickly
+         * (not such a big deal) and b) that the increment per tick has
+         * to fit into 24 bits.  As a result we need to use a shift of
+         * 18 so we can fit a value of 40 into the TIMINCA register.
+         *
+         * NOW:
+         * A shift of 0 is now used since there is no need to do small
+         * clock adjustment directly on NIC clock. A large shift was
+         * causing problem on 82574 because SYSTIM latching mechanism
+         * does not seem to work properly...
+         */
+        adapter->cycles.shift = E1000_82574_TSYNC_SHIFT;
+
+        memset(&adapter->compare, 0, sizeof(adapter->compare));
+        adapter->compare.source = &adapter->clock;
+        adapter->compare.target = ktime_get_real;
+        adapter->compare.num_samples = 20;
+
+        e1000_configure_systim(adapter);
+        break;
+    default:
+        /* only 82574 supports timesync */
+        break;
+    }
+#endif
      return 0;

  err_register:
@@ -7331,6 +7825,9 @@ static void __devexit e1000_remove(struc
          set_bit(__E1000_DOWN, &adapter->state);
      del_timer_sync(&adapter->watchdog_timer);
      del_timer_sync(&adapter->phy_info_timer);
+#ifdef HAVE_HW_TIME_STAMP
+    del_timer_sync(&adapter->reset_rx_hwtstamp_timer);
+#endif

      cancel_work_sync(&adapter->reset_task);
      cancel_work_sync(&adapter->watchdog_task);
@@ -7340,6 +7837,9 @@ static void __devexit e1000_remove(struc
      cancel_work_sync(&adapter->led_blink_task);
  #endif
      cancel_work_sync(&adapter->print_hang_task);
+#ifdef HAVE_HW_TIME_STAMP
+    cancel_work_sync(&adapter->reset_rx_hwtstamp_task);
+#endif

      if (!(netdev->flags & IFF_UP))
          e1000_power_down_phy(adapter);
diff -rup e1000e-1.6.3//src/regs.h main_274//src/regs.h
--- e1000e-1.6.3//src/regs.h    2011-09-20 19:04:40.000000000 -0400
+++ main_274//src/regs.h    2011-10-31 16:21:55.683753000 -0400
@@ -349,6 +349,8 @@
  #define E1000_SYSTIML    0x0B600 /* System time register Low - RO */
  #define E1000_SYSTIMH    0x0B604 /* System time register High - RO */
  #define E1000_TIMINCA    0x0B608 /* Increment attributes register - RW */
+#define E1000_TIMADJL    0x0B60C /* Time Adjustment Offset Register Low 
- RW */
+#define E1000_TIMADJH    0x0B610 /* Time Adjustment Offset Register 
High - RW */
  #define E1000_RXMTRL     0x0B634 /* Time sync Rx EtherType and Msg 
Type - RW */
  #define E1000_RXUDP      0x0B638 /* Time Sync Rx UDP Port - RW */



------------------------------------------------------------------------------
RSA&#174; Conference 2012
Save $700 by Nov 18
Register now&#33;
http://p.sf.net/sfu/rsa-sfdev2dev1
_______________________________________________
E1000-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/e1000-devel
To learn more about Intel&#174; Ethernet, visit 
http://communities.intel.com/community/wired

Reply via email to