diff mbox

[net-next,04/14] e1000e: add support for IEEE-1588 PTP

Message ID 1358422519-20981-5-git-send-email-jeffrey.t.kirsher@intel.com
State Changes Requested, archived
Delegated to: David Miller
Headers show

Commit Message

Kirsher, Jeffrey T Jan. 17, 2013, 11:35 a.m. UTC
From: Bruce Allan <bruce.w.allan@intel.com>

Add PTP IEEE-1588 support and make accessible via the PHC subsystem.

Cc: Richard Cochran <richardcochran@gmail.com>
Signed-off-by: Bruce Allan <bruce.w.allan@intel.com>
Reviewed-by: Jacob Keller <Jacob.e.keller@intel.com>
Tested-by: Jeff Pieper <jeffrey.e.pieper@intel.com>
Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
---
 drivers/net/ethernet/intel/Kconfig          |   1 +
 drivers/net/ethernet/intel/e1000e/Makefile  |   2 +-
 drivers/net/ethernet/intel/e1000e/defines.h |  11 ++
 drivers/net/ethernet/intel/e1000e/e1000.h   |   9 +-
 drivers/net/ethernet/intel/e1000e/ethtool.c |  12 ++
 drivers/net/ethernet/intel/e1000e/hw.h      |   2 +
 drivers/net/ethernet/intel/e1000e/netdev.c  | 107 ++++++++++-
 drivers/net/ethernet/intel/e1000e/ptp.c     | 276 ++++++++++++++++++++++++++++
 8 files changed, 417 insertions(+), 3 deletions(-)
 create mode 100644 drivers/net/ethernet/intel/e1000e/ptp.c

Comments

Richard Cochran Jan. 17, 2013, 3:35 p.m. UTC | #1
On Thu, Jan 17, 2013 at 03:35:09AM -0800, Jeff Kirsher wrote:

> +static struct ptp_clock_info e1000e_ptp_clock_info = {
> +	.owner		= THIS_MODULE,

small nit: better to use a static string for .name here than ...

> +	.n_alarm	= 0,
> +	.n_ext_ts	= 0,
> +	.n_per_out	= 0,
> +	.pps		= 0,
> +	.adjfreq	= e1000e_phc_adjfreq,
> +	.adjtime	= e1000e_phc_adjtime,
> +	.gettime	= e1000e_phc_gettime,
> +	.settime	= e1000e_phc_settime,
> +	.enable		= e1000e_phc_enable,
> +};
> +
> +/**
> + * e1000e_ptp_init - initialize PTP for devices which support it
> + * @adapter: board private structure
> + *
> + * This function performs the required steps for enabling PTP support.
> + * If PTP support has already been loaded it simply calls the cyclecounter
> + * init routine and exits.
> + **/
> +void e1000e_ptp_init(struct e1000_adapter *adapter)
> +{
> +	struct e1000_hw *hw = &adapter->hw;
> +
> +	adapter->ptp_clock = NULL;
> +
> +	if (!(adapter->flags & FLAG_HAS_HW_TIMESTAMP))
> +		return;
> +
> +	adapter->ptp_clock_info = e1000e_ptp_clock_info;
> +
> +	snprintf(adapter->ptp_clock_info.name,
> +		 sizeof(adapter->ptp_clock_info.name), "%pm",
> +		 adapter->netdev->perm_addr);

... putting any kind of address here. After some back and forth,
discussing what the 'name' field should be, we decided on

 * @name:      A short "friendly name" to identify the clock and to
 *             help distinguish PHY based devices from MAC based ones.
 *             The string is not meant to be a unique id.

and most other drivers put the name of the driver here.

Other than that, this new driver looks good to me. I'll try it out
soon.

Acked-by: Richard Cochran <richardcochran@gmail.com>

--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Stephen Hemminger Jan. 17, 2013, 3:56 p.m. UTC | #2
On Thu, 17 Jan 2013 03:35:09 -0800
Jeff Kirsher <jeffrey.t.kirsher@intel.com> wrote:

> +static struct ptp_clock_info e1000e_ptp_clock_info = {

Should be static const since it is immutable and contains function pointers.
--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Allan, Bruce W Jan. 18, 2013, 1:13 a.m. UTC | #3
> -----Original Message-----
> From: Richard Cochran [mailto:richardcochran@gmail.com]
> Sent: Thursday, January 17, 2013 7:36 AM
> To: Kirsher, Jeffrey T
> Cc: davem@davemloft.net; Allan, Bruce W; netdev@vger.kernel.org;
> gospo@redhat.com; sassmann@redhat.com
> Subject: Re: [net-next 04/14] e1000e: add support for IEEE-1588 PTP
> 
> On Thu, Jan 17, 2013 at 03:35:09AM -0800, Jeff Kirsher wrote:
> 
> > +static struct ptp_clock_info e1000e_ptp_clock_info = {
> > +	.owner		= THIS_MODULE,
> 
> small nit: better to use a static string for .name here than ...
> 
> > +	.n_alarm	= 0,
> > +	.n_ext_ts	= 0,
> > +	.n_per_out	= 0,
> > +	.pps		= 0,
> > +	.adjfreq	= e1000e_phc_adjfreq,
> > +	.adjtime	= e1000e_phc_adjtime,
> > +	.gettime	= e1000e_phc_gettime,
> > +	.settime	= e1000e_phc_settime,
> > +	.enable		= e1000e_phc_enable,
> > +};
> > +
> > +/**
> > + * e1000e_ptp_init - initialize PTP for devices which support it
> > + * @adapter: board private structure
> > + *
> > + * This function performs the required steps for enabling PTP support.
> > + * If PTP support has already been loaded it simply calls the cyclecounter
> > + * init routine and exits.
> > + **/
> > +void e1000e_ptp_init(struct e1000_adapter *adapter)
> > +{
> > +	struct e1000_hw *hw = &adapter->hw;
> > +
> > +	adapter->ptp_clock = NULL;
> > +
> > +	if (!(adapter->flags & FLAG_HAS_HW_TIMESTAMP))
> > +		return;
> > +
> > +	adapter->ptp_clock_info = e1000e_ptp_clock_info;
> > +
> > +	snprintf(adapter->ptp_clock_info.name,
> > +		 sizeof(adapter->ptp_clock_info.name), "%pm",
> > +		 adapter->netdev->perm_addr);
> 
> ... putting any kind of address here. After some back and forth,
> discussing what the 'name' field should be, we decided on
> 
>  * @name:      A short "friendly name" to identify the clock and to
>  *             help distinguish PHY based devices from MAC based ones.
>  *             The string is not meant to be a unique id.
> 
> and most other drivers put the name of the driver here.
> 
> Other than that, this new driver looks good to me. I'll try it out
> soon.
> 
> Acked-by: Richard Cochran <richardcochran@gmail.com>

Thanks for the review Richard.

--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Allan, Bruce W Jan. 18, 2013, 1:13 a.m. UTC | #4
> -----Original Message-----
> From: Stephen Hemminger [mailto:stephen@networkplumber.org]
> Sent: Thursday, January 17, 2013 7:56 AM
> To: Kirsher, Jeffrey T
> Cc: davem@davemloft.net; Allan, Bruce W; netdev@vger.kernel.org;
> gospo@redhat.com; sassmann@redhat.com; Richard Cochran
> Subject: Re: [net-next 04/14] e1000e: add support for IEEE-1588 PTP
> 
> On Thu, 17 Jan 2013 03:35:09 -0800
> Jeff Kirsher <jeffrey.t.kirsher@intel.com> wrote:
> 
> > +static struct ptp_clock_info e1000e_ptp_clock_info = {
> 
> Should be static const since it is immutable and contains function pointers.

Nice catch!  Thanks for the review Stephen.  I'll submit a follow-on patch to change this
if that is alright with you.  Btw, did you catch that through inspection or did you use a
static analysis tool?

Bruce.
--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Kirsher, Jeffrey T Jan. 18, 2013, 6:27 a.m. UTC | #5
On Thu, 2013-01-17 at 17:13 -0800, Allan, Bruce W wrote:
> > -----Original Message-----
> > From: Stephen Hemminger [mailto:stephen@networkplumber.org]
> > Sent: Thursday, January 17, 2013 7:56 AM
> > To: Kirsher, Jeffrey T
> > Cc: davem@davemloft.net; Allan, Bruce W; netdev@vger.kernel.org;
> > gospo@redhat.com; sassmann@redhat.com; Richard Cochran
> > Subject: Re: [net-next 04/14] e1000e: add support for IEEE-1588 PTP
> > 
> > On Thu, 17 Jan 2013 03:35:09 -0800
> > Jeff Kirsher <jeffrey.t.kirsher@intel.com> wrote:
> > 
> > > +static struct ptp_clock_info e1000e_ptp_clock_info = {
> > 
> > Should be static const since it is immutable and contains function pointers.
> 
> Nice catch!  Thanks for the review Stephen.  I'll submit a follow-on patch to change this
> if that is alright with you.  Btw, did you catch that through inspection or did you use a
> static analysis tool?
> 
> Bruce.

Bruce-

I have to make some other changes in the series of patches, so we can
just fix up this patch for when I re-submit the series of patches.

Cheers,
Jeff
diff mbox

Patch

diff --git a/drivers/net/ethernet/intel/Kconfig b/drivers/net/ethernet/intel/Kconfig
index bde4f3d..1f92417 100644
--- a/drivers/net/ethernet/intel/Kconfig
+++ b/drivers/net/ethernet/intel/Kconfig
@@ -74,6 +74,7 @@  config E1000E
 	tristate "Intel(R) PRO/1000 PCI-Express Gigabit Ethernet support"
 	depends on PCI && (!SPARC32 || BROKEN)
 	select CRC32
+	select PTP_1588_CLOCK
 	---help---
 	  This driver supports the PCI-Express Intel(R) PRO/1000 gigabit
 	  ethernet family of adapters. For PCI or PCI-X e1000 adapters,
diff --git a/drivers/net/ethernet/intel/e1000e/Makefile b/drivers/net/ethernet/intel/e1000e/Makefile
index 591b713..1da0faac 100644
--- a/drivers/net/ethernet/intel/e1000e/Makefile
+++ b/drivers/net/ethernet/intel/e1000e/Makefile
@@ -34,5 +34,5 @@  obj-$(CONFIG_E1000E) += e1000e.o
 
 e1000e-objs := 82571.o ich8lan.o 80003es2lan.o \
 	       mac.o manage.o nvm.o phy.o \
-	       param.o ethtool.o netdev.o
+	       param.o ethtool.o netdev.o ptp.o
 
diff --git a/drivers/net/ethernet/intel/e1000e/defines.h b/drivers/net/ethernet/intel/e1000e/defines.h
index 36f9fad..5cb7441 100644
--- a/drivers/net/ethernet/intel/e1000e/defines.h
+++ b/drivers/net/ethernet/intel/e1000e/defines.h
@@ -544,9 +544,20 @@ 
 
 #define E1000_TSYNCRXCTL_VALID		0x00000001 /* Rx timestamp valid */
 #define E1000_TSYNCRXCTL_TYPE_MASK	0x0000000E /* Rx type mask */
+#define E1000_TSYNCRXCTL_TYPE_L2_V2	0x00
+#define E1000_TSYNCRXCTL_TYPE_L4_V1	0x02
+#define E1000_TSYNCRXCTL_TYPE_L2_L4_V2	0x04
+#define E1000_TSYNCRXCTL_TYPE_ALL	0x08
+#define E1000_TSYNCRXCTL_TYPE_EVENT_V2	0x0A
 #define E1000_TSYNCRXCTL_ENABLED	0x00000010 /* enable Rx timestamping */
 #define E1000_TSYNCRXCTL_SYSCFI		0x00000020 /* Sys clock frequency */
 
+#define E1000_RXMTRL_PTP_V1_SYNC_MESSAGE	0x00000000
+#define E1000_RXMTRL_PTP_V1_DELAY_REQ_MESSAGE	0x00010000
+
+#define E1000_RXMTRL_PTP_V2_SYNC_MESSAGE	0x00000000
+#define E1000_RXMTRL_PTP_V2_DELAY_REQ_MESSAGE	0x01000000
+
 #define E1000_TIMINCA_INCPERIOD_SHIFT	24
 #define E1000_TIMINCA_INCVALUE_MASK	0x00FFFFFF
 
diff --git a/drivers/net/ethernet/intel/e1000e/e1000.h b/drivers/net/ethernet/intel/e1000e/e1000.h
index dea9e55..bd58761 100644
--- a/drivers/net/ethernet/intel/e1000e/e1000.h
+++ b/drivers/net/ethernet/intel/e1000e/e1000.h
@@ -43,7 +43,8 @@ 
 #include <linux/if_vlan.h>
 #include <linux/clocksource.h>
 #include <linux/net_tstamp.h>
-
+#include <linux/ptp_clock_kernel.h>
+#include <linux/ptp_classify.h>
 #include "hw.h"
 
 struct e1000_info;
@@ -413,6 +414,8 @@  struct e1000_adapter {
 	spinlock_t systim_lock;	/* protects SYSTIML/H regsters */
 	struct cyclecounter cc;
 	struct timecounter tc;
+	struct ptp_clock *ptp_clock;
+	struct ptp_clock_info ptp_clock_info;
 };
 
 struct e1000_info {
@@ -427,6 +430,8 @@  struct e1000_info {
 	const struct e1000_nvm_operations *nvm_ops;
 };
 
+s32 e1000e_get_base_timinca(struct e1000_adapter *adapter, u32 *timinca);
+
 /* The system time is maintained by a 64-bit counter comprised of the 32-bit
  * SYSTIMH and SYSTIML registers.  How the counter increments (and therefore
  * its resolution) is based on the contents of the TIMINCA register - it
@@ -704,6 +709,8 @@  extern s32 e1000_phy_force_speed_duplex_ife(struct e1000_hw *hw);
 extern s32 e1000_check_polarity_igp(struct e1000_hw *hw);
 extern bool e1000_check_phy_82574(struct e1000_hw *hw);
 extern s32 e1000_read_emi_reg_locked(struct e1000_hw *hw, u16 addr, u16 *data);
+extern void e1000e_ptp_init(struct e1000_adapter *adapter);
+extern void e1000e_ptp_remove(struct e1000_adapter *adapter);
 
 static inline s32 e1000_phy_hw_reset(struct e1000_hw *hw)
 {
diff --git a/drivers/net/ethernet/intel/e1000e/ethtool.c b/drivers/net/ethernet/intel/e1000e/ethtool.c
index f268cbc..56beae6 100644
--- a/drivers/net/ethernet/intel/e1000e/ethtool.c
+++ b/drivers/net/ethernet/intel/e1000e/ethtool.c
@@ -2200,8 +2200,20 @@  static int e1000e_get_ts_info(struct net_device *netdev,
 	info->tx_types = (1 << HWTSTAMP_TX_OFF) | (1 << HWTSTAMP_TX_ON);
 
 	info->rx_filters = ((1 << HWTSTAMP_FILTER_NONE) |
+			    (1 << HWTSTAMP_FILTER_PTP_V1_L4_SYNC) |
+			    (1 << HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ) |
+			    (1 << HWTSTAMP_FILTER_PTP_V2_L4_SYNC) |
+			    (1 << HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ) |
+			    (1 << HWTSTAMP_FILTER_PTP_V2_L2_SYNC) |
+			    (1 << HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ) |
+			    (1 << HWTSTAMP_FILTER_PTP_V2_EVENT) |
+			    (1 << HWTSTAMP_FILTER_PTP_V2_SYNC) |
+			    (1 << HWTSTAMP_FILTER_PTP_V2_DELAY_REQ) |
 			    (1 << HWTSTAMP_FILTER_ALL));
 
+	if (adapter->ptp_clock)
+		info->phc_index = ptp_clock_index(adapter->ptp_clock);
+
 	return 0;
 }
 
diff --git a/drivers/net/ethernet/intel/e1000e/hw.h b/drivers/net/ethernet/intel/e1000e/hw.h
index 8e7e803..84e6beb 100644
--- a/drivers/net/ethernet/intel/e1000e/hw.h
+++ b/drivers/net/ethernet/intel/e1000e/hw.h
@@ -251,6 +251,8 @@  enum e1e_registers {
 	E1000_TSYNCRXCTL = 0x0B620, /* Rx Time Sync Control register - RW */
 	E1000_RXSTMPL   = 0x0B624, /* Rx timestamp Low - RO */
 	E1000_RXSTMPH   = 0x0B628, /* Rx timestamp High - RO */
+	E1000_RXMTRL    = 0x0B634, /* Timesync Rx EtherType and Msg Type - RW */
+	E1000_RXUDP     = 0x0B638, /* Timesync Rx UDP Port - RW */
 };
 
 #define E1000_MAX_PHY_ADDR		4
diff --git a/drivers/net/ethernet/intel/e1000e/netdev.c b/drivers/net/ethernet/intel/e1000e/netdev.c
index c15b7e4..7d019c4 100644
--- a/drivers/net/ethernet/intel/e1000e/netdev.c
+++ b/drivers/net/ethernet/intel/e1000e/netdev.c
@@ -3413,7 +3413,7 @@  static void e1000e_setup_rss_hash(struct e1000_adapter *adapter)
  * Get attributes for incrementing the System Time Register SYSTIML/H at
  * the default base frequency, and set the cyclecounter shift value.
  **/
-static s32 e1000e_get_base_timinca(struct e1000_adapter *adapter, u32 *timinca)
+s32 e1000e_get_base_timinca(struct e1000_adapter *adapter, u32 *timinca)
 {
 	struct e1000_hw *hw = &adapter->hw;
 	u32 incvalue, incperiod, shift;
@@ -3485,6 +3485,10 @@  static int e1000e_config_hwtstamp(struct e1000_adapter *adapter)
 	struct hwtstamp_config *config = &adapter->hwtstamp_config;
 	u32 tsync_tx_ctl = E1000_TSYNCTXCTL_ENABLED;
 	u32 tsync_rx_ctl = E1000_TSYNCRXCTL_ENABLED;
+	u32 rxmtrl = 0;
+	u16 rxudp = 0;
+	bool is_l4 = false;
+	bool is_l2 = false;
 	u32 regval;
 	s32 ret_val;
 
@@ -3509,7 +3513,69 @@  static int e1000e_config_hwtstamp(struct e1000_adapter *adapter)
 	case HWTSTAMP_FILTER_NONE:
 		tsync_rx_ctl = 0;
 		break;
+	case HWTSTAMP_FILTER_PTP_V1_L4_SYNC:
+		tsync_rx_ctl |= E1000_TSYNCRXCTL_TYPE_L4_V1;
+		rxmtrl = E1000_RXMTRL_PTP_V1_SYNC_MESSAGE;
+		is_l4 = true;
+		break;
+	case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ:
+		tsync_rx_ctl |= E1000_TSYNCRXCTL_TYPE_L4_V1;
+		rxmtrl = E1000_RXMTRL_PTP_V1_DELAY_REQ_MESSAGE;
+		is_l4 = true;
+		break;
+	case HWTSTAMP_FILTER_PTP_V2_L2_SYNC:
+		/* Also time stamps V2 L2 Path Delay Request/Response */
+		tsync_rx_ctl |= E1000_TSYNCRXCTL_TYPE_L2_V2;
+		rxmtrl = E1000_RXMTRL_PTP_V2_SYNC_MESSAGE;
+		is_l2 = true;
+		break;
+	case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ:
+		/* Also time stamps V2 L2 Path Delay Request/Response. */
+		tsync_rx_ctl |= E1000_TSYNCRXCTL_TYPE_L2_V2;
+		rxmtrl = E1000_RXMTRL_PTP_V2_DELAY_REQ_MESSAGE;
+		is_l2 = true;
+		break;
+	case HWTSTAMP_FILTER_PTP_V2_L4_SYNC:
+		/* Hardware cannot filter just V2 L4 Sync messages;
+		 * fall-through to V2 (both L2 and L4) Sync.
+		 */
+	case HWTSTAMP_FILTER_PTP_V2_SYNC:
+		/* Also time stamps V2 Path Delay Request/Response. */
+		tsync_rx_ctl |= E1000_TSYNCRXCTL_TYPE_L2_L4_V2;
+		rxmtrl = E1000_RXMTRL_PTP_V2_SYNC_MESSAGE;
+		is_l2 = true;
+		is_l4 = true;
+		break;
+	case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ:
+		/* Hardware cannot filter just V2 L4 Delay Request messages;
+		 * fall-through to V2 (both L2 and L4) Delay Request.
+		 */
+	case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ:
+		/* Also time stamps V2 Path Delay Request/Response. */
+		tsync_rx_ctl |= E1000_TSYNCRXCTL_TYPE_L2_L4_V2;
+		rxmtrl = E1000_RXMTRL_PTP_V2_DELAY_REQ_MESSAGE;
+		is_l2 = true;
+		is_l4 = true;
+		break;
+	case HWTSTAMP_FILTER_PTP_V2_L4_EVENT:
+	case HWTSTAMP_FILTER_PTP_V2_L2_EVENT:
+		/* Hardware cannot filter just V2 L4 or L2 Event messages;
+		 * fall-through to all V2 (both L2 and L4) Events.
+		 */
+	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;
+	case HWTSTAMP_FILTER_PTP_V1_L4_EVENT:
+		/* For V1, the hardware can only filter Sync messages or
+		 * Delay Request messages but not both so fall-through to
+		 * time stamp all packets.
+		 */
 	case HWTSTAMP_FILTER_ALL:
+		is_l2 = true;
+		is_l4 = true;
 		tsync_rx_ctl |= E1000_TSYNCRXCTL_TYPE_ALL;
 		config->rx_filter = HWTSTAMP_FILTER_ALL;
 		break;
@@ -3541,6 +3607,22 @@  static int e1000e_config_hwtstamp(struct e1000_adapter *adapter)
 		return -EAGAIN;
 	}
 
+	/* L2: define ethertype filter for time stamped packets */
+	if (is_l2)
+		rxmtrl |= ETH_P_1588;
+
+	/* define which PTP packets get time stamped */
+	ew32(RXMTRL, rxmtrl);
+
+	/* Filter by destination port */
+	if (is_l4) {
+		rxudp = PTP_EV_PORT;
+		cpu_to_be16s(&rxudp);
+	}
+	ew32(RXUDP, rxudp);
+
+	e1e_flush();
+
 	/* Clear TSYNCRXCTL_VALID & TSYNCTXCTL_VALID bit */
 	regval = er32(RXSTMPH);
 	regval = er32(TXSTMPH);
@@ -5662,6 +5744,24 @@  static int e1000e_hwtstamp_ioctl(struct net_device *netdev, struct ifreq *ifr)
 
 	config = adapter->hwtstamp_config;
 
+	switch (config.rx_filter) {
+	case HWTSTAMP_FILTER_PTP_V2_L4_SYNC:
+	case HWTSTAMP_FILTER_PTP_V2_L2_SYNC:
+	case HWTSTAMP_FILTER_PTP_V2_SYNC:
+	case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ:
+	case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ:
+	case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ:
+		/* With V2 type filters which specify a Sync or Delay Request,
+		 * Path Delay Request/Response messages are also time stamped
+		 * by hardware so notify the caller the requested packets plus
+		 * some others are time stamped.
+		 */
+		config.rx_filter = HWTSTAMP_FILTER_SOME;
+		break;
+	default:
+		break;
+	}
+
 	return copy_to_user(ifr->ifr_data, &config,
 			    sizeof(config)) ? -EFAULT : 0;
 }
@@ -6669,6 +6769,9 @@  static int e1000_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
 	/* carrier off reporting is important to ethtool even BEFORE open */
 	netif_carrier_off(netdev);
 
+	/* init PTP hardware clock */
+	e1000e_ptp_init(adapter);
+
 	e1000_print_device_info(adapter);
 
 	if (pci_dev_run_wake(pdev))
@@ -6717,6 +6820,8 @@  static void e1000_remove(struct pci_dev *pdev)
 	struct e1000_adapter *adapter = netdev_priv(netdev);
 	bool down = test_bit(__E1000_DOWN, &adapter->state);
 
+	e1000e_ptp_remove(adapter);
+
 	/* The timers may be rescheduled, so explicitly disable them
 	 * from being rescheduled.
 	 */
diff --git a/drivers/net/ethernet/intel/e1000e/ptp.c b/drivers/net/ethernet/intel/e1000e/ptp.c
new file mode 100644
index 0000000..0cf3317
--- /dev/null
+++ b/drivers/net/ethernet/intel/e1000e/ptp.c
@@ -0,0 +1,276 @@ 
+/*******************************************************************************
+
+  Intel PRO/1000 Linux driver
+  Copyright(c) 1999 - 2012 Intel Corporation.
+
+  This program is free software; you can redistribute it and/or modify it
+  under the terms and conditions of the GNU General Public License,
+  version 2, as published by the Free Software Foundation.
+
+  This program is distributed in the hope it will be useful, but WITHOUT
+  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+  more details.
+
+  You should have received a copy of the GNU General Public License along with
+  this program; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+
+  The full GNU General Public License is included in this distribution in
+  the file called "COPYING".
+
+  Contact Information:
+  Linux NICS <linux.nics@intel.com>
+  e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
+  Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+
+*******************************************************************************/
+
+/* PTP 1588 Hardware Clock (PHC)
+ * Derived from PTP Hardware Clock driver for Intel 82576 and 82580 (igb)
+ * Copyright (C) 2011 Richard Cochran <richardcochran@gmail.com>
+ */
+
+#include "e1000.h"
+
+/**
+ * e1000e_phc_adjfreq - adjust the frequency of the hardware clock
+ * @ptp: ptp clock structure
+ * @delta: Desired frequency change in parts per billion
+ *
+ * Adjust the frequency of the PHC cycle counter by the indicated delta from
+ * the base frequency.
+ **/
+static int e1000e_phc_adjfreq(struct ptp_clock_info *ptp, s32 delta)
+{
+	struct e1000_adapter *adapter = container_of(ptp, struct e1000_adapter,
+						     ptp_clock_info);
+	struct e1000_hw *hw = &adapter->hw;
+	bool neg_adj = false;
+	u64 adjustment;
+	u32 timinca, incvalue;
+	s32 ret_val;
+
+	if ((delta > ptp->max_adj) || (delta <= -1000000000))
+		return -EINVAL;
+
+	if (delta < 0) {
+		neg_adj = true;
+		delta = -delta;
+	}
+
+	/* Get the System Time Register SYSTIM base frequency */
+	ret_val = e1000e_get_base_timinca(adapter, &timinca);
+	if (ret_val)
+		return ret_val;
+
+	incvalue = timinca & E1000_TIMINCA_INCVALUE_MASK;
+
+	adjustment = incvalue;
+	adjustment *= delta;
+	adjustment = div_u64(adjustment, 1000000000);
+
+	incvalue = neg_adj ? (incvalue - adjustment) : (incvalue + adjustment);
+
+	timinca &= ~E1000_TIMINCA_INCVALUE_MASK;
+	timinca |= incvalue;
+
+	ew32(TIMINCA, timinca);
+
+	return 0;
+}
+
+/**
+ * e1000e_phc_adjtime - Shift the time of the hardware clock
+ * @ptp: ptp clock structure
+ * @delta: Desired change in nanoseconds
+ *
+ * Adjust the timer by resetting the timecounter structure.
+ **/
+static int e1000e_phc_adjtime(struct ptp_clock_info *ptp, s64 delta)
+{
+	struct e1000_adapter *adapter = container_of(ptp, struct e1000_adapter,
+						     ptp_clock_info);
+	unsigned long flags;
+	s64 now;
+
+	spin_lock_irqsave(&adapter->systim_lock, flags);
+	now = timecounter_read(&adapter->tc);
+	now += delta;
+	timecounter_init(&adapter->tc, &adapter->cc, now);
+	spin_unlock_irqrestore(&adapter->systim_lock, flags);
+
+	return 0;
+}
+
+/**
+ * e1000e_phc_gettime - Reads the current time from the hardware clock
+ * @ptp: ptp clock structure
+ * @ts: timespec structure to hold the current time value
+ *
+ * Read the timecounter and return the correct value in ns after converting
+ * it into a struct timespec.
+ **/
+static int e1000e_phc_gettime(struct ptp_clock_info *ptp, struct timespec *ts)
+{
+	struct e1000_adapter *adapter = container_of(ptp, struct e1000_adapter,
+						     ptp_clock_info);
+	unsigned long flags;
+	u32 remainder;
+	u64 ns;
+
+	spin_lock_irqsave(&adapter->systim_lock, flags);
+	ns = timecounter_read(&adapter->tc);
+	spin_unlock_irqrestore(&adapter->systim_lock, flags);
+
+	ts->tv_sec = div_u64_rem(ns, NSEC_PER_SEC, &remainder);
+	ts->tv_nsec = remainder;
+
+	return 0;
+}
+
+/**
+ * e1000e_phc_settime - Set the current time on the hardware clock
+ * @ptp: ptp clock structure
+ * @ts: timespec containing the new time for the cycle counter
+ *
+ * Reset the timecounter to use a new base value instead of the kernel
+ * wall timer value.
+ **/
+static int e1000e_phc_settime(struct ptp_clock_info *ptp,
+			      const struct timespec *ts)
+{
+	struct e1000_adapter *adapter = container_of(ptp, struct e1000_adapter,
+						     ptp_clock_info);
+	unsigned long flags;
+	u64 ns;
+
+	ns = ts->tv_sec * NSEC_PER_SEC;
+	ns += ts->tv_nsec;
+
+	/* reset the timecounter */
+	spin_lock_irqsave(&adapter->systim_lock, flags);
+	timecounter_init(&adapter->tc, &adapter->cc, ns);
+	spin_unlock_irqrestore(&adapter->systim_lock, flags);
+
+	return 0;
+}
+
+/**
+ * e1000e_phc_enable - enable or disable an ancillary feature
+ * @ptp: ptp clock structure
+ * @request: Desired resource to enable or disable
+ * @on: Caller passes one to enable or zero to disable
+ *
+ * Enable (or disable) ancillary features of the PHC subsystem.
+ * Currently, no ancillary features are supported.
+ **/
+static int e1000e_phc_enable(struct ptp_clock_info *ptp,
+			     struct ptp_clock_request *request, int on)
+{
+	return -EOPNOTSUPP;
+}
+
+static void e1000e_systim_overflow_work(struct work_struct *work)
+{
+	struct e1000_adapter *adapter = container_of(work, struct e1000_adapter,
+						     systim_overflow_work.work);
+	struct e1000_hw *hw = &adapter->hw;
+	struct timespec ts;
+
+	adapter->ptp_clock_info.gettime(&adapter->ptp_clock_info, &ts);
+
+	e_dbg("SYSTIM overflow check at %ld.%09lu\n", ts.tv_sec, ts.tv_nsec);
+
+	schedule_delayed_work(&adapter->systim_overflow_work,
+			      E1000_SYSTIM_OVERFLOW_PERIOD);
+}
+
+static struct ptp_clock_info e1000e_ptp_clock_info = {
+	.owner		= THIS_MODULE,
+	.n_alarm	= 0,
+	.n_ext_ts	= 0,
+	.n_per_out	= 0,
+	.pps		= 0,
+	.adjfreq	= e1000e_phc_adjfreq,
+	.adjtime	= e1000e_phc_adjtime,
+	.gettime	= e1000e_phc_gettime,
+	.settime	= e1000e_phc_settime,
+	.enable		= e1000e_phc_enable,
+};
+
+/**
+ * e1000e_ptp_init - initialize PTP for devices which support it
+ * @adapter: board private structure
+ *
+ * This function performs the required steps for enabling PTP support.
+ * If PTP support has already been loaded it simply calls the cyclecounter
+ * init routine and exits.
+ **/
+void e1000e_ptp_init(struct e1000_adapter *adapter)
+{
+	struct e1000_hw *hw = &adapter->hw;
+
+	adapter->ptp_clock = NULL;
+
+	if (!(adapter->flags & FLAG_HAS_HW_TIMESTAMP))
+		return;
+
+	adapter->ptp_clock_info = e1000e_ptp_clock_info;
+
+	snprintf(adapter->ptp_clock_info.name,
+		 sizeof(adapter->ptp_clock_info.name), "%pm",
+		 adapter->netdev->perm_addr);
+
+	switch (hw->mac.type) {
+	case e1000_pch2lan:
+	case e1000_pch_lpt:
+		if ((hw->mac.type != e1000_pch_lpt) ||
+		    (er32(TSYNCRXCTL) & E1000_TSYNCRXCTL_SYSCFI)) {
+			adapter->ptp_clock_info.max_adj = 24000000 - 1;
+			break;
+		}
+		/* fall-through */
+	case e1000_82574:
+	case e1000_82583:
+		adapter->ptp_clock_info.max_adj = 600000000 - 1;
+		break;
+	default:
+		break;
+	}
+
+	INIT_DELAYED_WORK(&adapter->systim_overflow_work,
+			  e1000e_systim_overflow_work);
+
+	schedule_delayed_work(&adapter->systim_overflow_work,
+			      E1000_SYSTIM_OVERFLOW_PERIOD);
+
+	adapter->ptp_clock = ptp_clock_register(&adapter->ptp_clock_info,
+						&adapter->pdev->dev);
+	if (IS_ERR(adapter->ptp_clock)) {
+		adapter->ptp_clock = NULL;
+		e_err("ptp_clock_register failed\n");
+	} else {
+		e_info("registered PHC clock\n");
+	}
+}
+
+/**
+ * e1000e_ptp_remove - disable PTP device and stop the overflow check
+ * @adapter: board private structure
+ *
+ * Stop the PTP support, and cancel the delayed work.
+ **/
+void e1000e_ptp_remove(struct e1000_adapter *adapter)
+{
+	if (!(adapter->flags & FLAG_HAS_HW_TIMESTAMP))
+		return;
+
+	cancel_delayed_work_sync(&adapter->systim_overflow_work);
+
+	if (adapter->ptp_clock) {
+		ptp_clock_unregister(adapter->ptp_clock);
+		adapter->ptp_clock = NULL;
+		e_info("removed PHC\n");
+	}
+}