diff mbox

[net-next.git,2/9] stmmac: add IEEE 1588-2002 PTP support

Message ID 1362653419-1047-3-git-send-email-peppe.cavallaro@st.com
State Changes Requested, archived
Delegated to: David Miller
Headers show

Commit Message

Giuseppe CAVALLARO March 7, 2013, 10:50 a.m. UTC
From: Rayagond Kokatanur <rayagond@vayavyalabs.com>

This patch enhances the stmmac driver to support IEEE 1588-2002
PTP (Precision Time Protocol) version 1.

IEEE 1588-2002 standard defines a protocol, Precision Time
Protocol(PTP),
which enables precise synchronization of clocks in measurement and
control systems implemented with technologies such as network
communication,local computing, & distributed objects.

HW Timestamp support can be enabled while configuring the Kernel and
the Koption is: STMMAC_USE_HWSTAMP
At runtime, the support is verified by looking at the HW capability
register.

Signed-off-by: Rayagond Kokatanur <rayagond@vayavyalabs.com>
Hacked-by: Giuseppe Cavallaro <peppe.cavallaro@st.com>
---
 drivers/net/ethernet/stmicro/stmmac/Kconfig        |   11 +
 drivers/net/ethernet/stmicro/stmmac/Makefile       |    1 +
 drivers/net/ethernet/stmicro/stmmac/chain_mode.c   |   23 ++-
 drivers/net/ethernet/stmicro/stmmac/common.h       |   27 ++-
 drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c    |    1 -
 drivers/net/ethernet/stmicro/stmmac/enh_desc.c     |   37 +++
 drivers/net/ethernet/stmicro/stmmac/norm_desc.c    |   34 +++
 drivers/net/ethernet/stmicro/stmmac/ring_mode.c    |   14 +-
 drivers/net/ethernet/stmicro/stmmac/stmmac.h       |    8 +
 .../net/ethernet/stmicro/stmmac/stmmac_hwstamp.c   |  129 ++++++++++
 drivers/net/ethernet/stmicro/stmmac/stmmac_main.c  |  255 +++++++++++++++++++-
 drivers/net/ethernet/stmicro/stmmac/stmmac_ptp.h   |   72 ++++++
 12 files changed, 588 insertions(+), 24 deletions(-)
 create mode 100644 drivers/net/ethernet/stmicro/stmmac/stmmac_hwstamp.c
 create mode 100644 drivers/net/ethernet/stmicro/stmmac/stmmac_ptp.h

Comments

Richard Cochran March 8, 2013, 6:34 a.m. UTC | #1
I have a few comments, below.

On Thu, Mar 07, 2013 at 11:50:12AM +0100, Giuseppe CAVALLARO wrote:
> From: Rayagond Kokatanur <rayagond@vayavyalabs.com>
> 
> This patch enhances the stmmac driver to support IEEE 1588-2002
> PTP (Precision Time Protocol) version 1.
> 
> IEEE 1588-2002 standard defines a protocol, Precision Time
> Protocol(PTP),
> which enables precise synchronization of clocks in measurement and
> control systems implemented with technologies such as network
> communication,local computing, & distributed objects.
> 
> HW Timestamp support can be enabled while configuring the Kernel and
> the Koption is: STMMAC_USE_HWSTAMP
> At runtime, the support is verified by looking at the HW capability
> register.

As davem said, since you have the ability to detect this at run time,
then there is no need for a kconfig option.

> Signed-off-by: Rayagond Kokatanur <rayagond@vayavyalabs.com>
> Hacked-by: Giuseppe Cavallaro <peppe.cavallaro@st.com>
> ---
>  drivers/net/ethernet/stmicro/stmmac/Kconfig        |   11 +
>  drivers/net/ethernet/stmicro/stmmac/Makefile       |    1 +
>  drivers/net/ethernet/stmicro/stmmac/chain_mode.c   |   23 ++-
>  drivers/net/ethernet/stmicro/stmmac/common.h       |   27 ++-
>  drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c    |    1 -
>  drivers/net/ethernet/stmicro/stmmac/enh_desc.c     |   37 +++
>  drivers/net/ethernet/stmicro/stmmac/norm_desc.c    |   34 +++
>  drivers/net/ethernet/stmicro/stmmac/ring_mode.c    |   14 +-
>  drivers/net/ethernet/stmicro/stmmac/stmmac.h       |    8 +
>  .../net/ethernet/stmicro/stmmac/stmmac_hwstamp.c   |  129 ++++++++++
>  drivers/net/ethernet/stmicro/stmmac/stmmac_main.c  |  255 +++++++++++++++++++-
>  drivers/net/ethernet/stmicro/stmmac/stmmac_ptp.h   |   72 ++++++
>  12 files changed, 588 insertions(+), 24 deletions(-)
>  create mode 100644 drivers/net/ethernet/stmicro/stmmac/stmmac_hwstamp.c
>  create mode 100644 drivers/net/ethernet/stmicro/stmmac/stmmac_ptp.h
> 
> diff --git a/drivers/net/ethernet/stmicro/stmmac/Kconfig b/drivers/net/ethernet/stmicro/stmmac/Kconfig
> index c0ea838..ef703b3 100644
> --- a/drivers/net/ethernet/stmicro/stmmac/Kconfig
> +++ b/drivers/net/ethernet/stmicro/stmmac/Kconfig
> @@ -71,5 +71,16 @@ config STMMAC_CHAINED
>  
>  endchoice
>  
> +config STMMAC_USE_HWSTAMP
> +	bool "Use IEEE 1588 Precision Time Protocol"
> +	depends on STMMAC_ETH
> +	select PTP_1588_CLOCK
> +	default n
> +	---help---
> +	  Enable this option to support the IEEE 1588 Precision Time Protocol
> +	  (PTP) and HW Timestamps.
> +	  At runtime, on new chip generations, the hardware capability
> +	  register will be used to verify if either the IEEE 1588-2008 Advanced
> +	  Timestamping (PTPv2) or IEEE 1588-2002 (PTPv1) is actually supported.
>  
>  endif
> diff --git a/drivers/net/ethernet/stmicro/stmmac/Makefile b/drivers/net/ethernet/stmicro/stmmac/Makefile
> index c8e8ea6..cc97c07 100644
> --- a/drivers/net/ethernet/stmicro/stmmac/Makefile
> +++ b/drivers/net/ethernet/stmicro/stmmac/Makefile
> @@ -3,6 +3,7 @@ stmmac-$(CONFIG_STMMAC_RING) += ring_mode.o
>  stmmac-$(CONFIG_STMMAC_CHAINED) += chain_mode.o
>  stmmac-$(CONFIG_STMMAC_PLATFORM) += stmmac_platform.o
>  stmmac-$(CONFIG_STMMAC_PCI) += stmmac_pci.o
> +stmmac-$(CONFIG_STMMAC_USE_HWSTAMP) += stmmac_hwstamp.o
>  stmmac-objs:= stmmac_main.o stmmac_ethtool.o stmmac_mdio.o	\
>  	      dwmac_lib.o dwmac1000_core.o  dwmac1000_dma.o	\
>  	      dwmac100_core.o dwmac100_dma.o enh_desc.o  norm_desc.o \
> diff --git a/drivers/net/ethernet/stmicro/stmmac/chain_mode.c b/drivers/net/ethernet/stmicro/stmmac/chain_mode.c
> index ad3d75f..dd60f6b 100644
> --- a/drivers/net/ethernet/stmicro/stmmac/chain_mode.c
> +++ b/drivers/net/ethernet/stmicro/stmmac/chain_mode.c
> @@ -92,16 +92,35 @@ static unsigned int stmmac_is_jumbo_frm(int len, int enh_desc)
>  	return ret;
>  }
>  
> -static void stmmac_refill_desc3(int bfsize, struct dma_desc *p)
> +static void stmmac_refill_desc3(void *priv_ptr, struct dma_desc *p)
>  {
> +	struct stmmac_priv *priv = (struct stmmac_priv *)priv_ptr;
> +
> +	if (priv->hwts_rx_en)
> +		/* NOTE: Device will overwrite des3 with timestamp value if
> +		 * 1588-2002 time stamping is enabled, hence reinitialize it
> +		 * to keep explicit chaining in the descriptor.
> +		 */
> +		p->des3 = (unsigned int)(priv->dma_rx +
> +			((priv->dirty_rx) + 1) % priv->dma_rx_size);
>  }
>  
>  static void stmmac_init_desc3(int des3_as_data_buf, struct dma_desc *p)
>  {
>  }
>  
> -static void stmmac_clean_desc3(struct dma_desc *p)
> +static void stmmac_clean_desc3(void *priv_ptr, struct dma_desc *p,
> +				int tstamp_taken)
>  {
> +	struct stmmac_priv *priv = (struct stmmac_priv *)priv_ptr;
> +
> +	if (tstamp_taken && priv->hw->desc->get_tx_ls(p))
> +		/* NOTE: Device will overwrite des3 with timestamp value if
> +		 * 1588-2002 time stamping is enabled, hence reinitialize it
> +		 * to keep explicit chaining in the descriptor.
> +		 */
> +		p->des3 = (unsigned int)(priv->dma_tx +
> +			((priv->dirty_tx + 1) % priv->dma_tx_size));
>  }
>  
>  static void stmmac_init_dma_chain(struct dma_desc *des, dma_addr_t phy_addr,
> diff --git a/drivers/net/ethernet/stmicro/stmmac/common.h b/drivers/net/ethernet/stmicro/stmmac/common.h
> index 186d148..fa8ff9b 100644
> --- a/drivers/net/ethernet/stmicro/stmmac/common.h
> +++ b/drivers/net/ethernet/stmicro/stmmac/common.h
> @@ -290,6 +290,16 @@ struct stmmac_desc_ops {
>  	/* Return the reception status looking at the RDES1 */
>  	int (*rx_status) (void *data, struct stmmac_extra_stats *x,
>  			  struct dma_desc *p);
> +	/* Set tx timestamp enable bit */
> +	void (*enable_tx_timestamp) (struct dma_desc *p);
> +	/* get tx timestamp status */
> +	int (*get_tx_timestamp_status) (struct dma_desc *p);

> +	/* get tx/rx timestamp low value */
> +	u32 (*get_timestamp_low) (struct dma_desc *p);
> +	/* get tx/rx timestamp high value */
> +	u32 (*get_timestamp_high) (struct dma_desc *p);

These two separate functions should be combined into one.

> +	/* get rx timestamp status */
> +	int (*get_rx_timestamp_status) (struct dma_desc *p);
>  };
>  
>  struct stmmac_dma_ops {
> @@ -346,6 +356,15 @@ struct stmmac_ops {
>  	void (*set_eee_pls) (void __iomem *ioaddr, int link);
>  };
>  
> +#ifdef CONFIG_STMMAC_USE_HWSTAMP
> +struct stmmac_hwtimestamp {
> +	void (*config_hw_tstamping) (void __iomem *ioaddr, u32 data);
> +	void (*config_sub_second_increment) (void __iomem *ioaddr);
> +	int (*init_systime) (void __iomem *ioaddr, u32 sec, u32 nsec);
> +	int (*config_addend)(void __iomem *ioaddr, u32 addend);
> +};
> +#endif
> +
>  struct mac_link {
>  	int port;
>  	int duplex;
> @@ -360,11 +379,11 @@ struct mii_regs {
>  struct stmmac_ring_mode_ops {
>  	unsigned int (*is_jumbo_frm) (int len, int ehn_desc);
>  	unsigned int (*jumbo_frm) (void *priv, struct sk_buff *skb, int csum);
> -	void (*refill_desc3) (int bfsize, struct dma_desc *p);
> +	void (*refill_desc3) (void *priv, struct dma_desc *p);
>  	void (*init_desc3) (int des3_as_data_buf, struct dma_desc *p);
>  	void (*init_dma_chain) (struct dma_desc *des, dma_addr_t phy_addr,
>  				unsigned int size);
> -	void (*clean_desc3) (struct dma_desc *p);
> +	void (*clean_desc3) (void *priv, struct dma_desc *p, int tstamp_taken);
>  	int (*set_16kib_bfsize) (int mtu);
>  };
>  
> @@ -373,6 +392,9 @@ struct mac_device_info {
>  	const struct stmmac_desc_ops	*desc;
>  	const struct stmmac_dma_ops	*dma;
>  	const struct stmmac_ring_mode_ops	*ring;
> +#ifdef CONFIG_STMMAC_USE_HWSTAMP
> +	const struct stmmac_hwtimestamp	*ptp;
> +#endif
>  	struct mii_regs mii;	/* MII register Addresses */
>  	struct mac_link link;
>  	unsigned int synopsys_uid;
> @@ -390,5 +412,4 @@ extern void stmmac_set_mac(void __iomem *ioaddr, bool enable);
>  
>  extern void dwmac_dma_flush_tx_fifo(void __iomem *ioaddr);
>  extern const struct stmmac_ring_mode_ops ring_mode_ops;
> -
>  #endif /* __COMMON_H__ */
> diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c b/drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c
> index 491d7e9..8c4ea93 100644
> --- a/drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c
> +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c
> @@ -286,4 +286,3 @@ void stmmac_get_mac_addr(void __iomem *ioaddr, unsigned char *addr,
>  	addr[4] = hi_addr & 0xff;
>  	addr[5] = (hi_addr >> 8) & 0xff;
>  }
> -
> diff --git a/drivers/net/ethernet/stmicro/stmmac/enh_desc.c b/drivers/net/ethernet/stmicro/stmmac/enh_desc.c
> index 2fc8ef9..4d635d5 100644
> --- a/drivers/net/ethernet/stmicro/stmmac/enh_desc.c
> +++ b/drivers/net/ethernet/stmicro/stmmac/enh_desc.c
> @@ -323,6 +323,38 @@ static int enh_desc_get_rx_frame_len(struct dma_desc *p, int rx_coe_type)
>  		return p->des01.erx.frame_length;
>  }
>  
> +static void enh_desc_enable_tx_timestamp(struct dma_desc *p)
> +{
> +	p->des01.etx.time_stamp_enable = 1;
> +}
> +
> +static int enh_desc_get_tx_timestamp_status(struct dma_desc *p)
> +{
> +	return p->des01.etx.time_stamp_status;
> +}
> +
> +static u32 enh_desc_get_timestamp_low(struct dma_desc *p)
> +{
> +	/* FIXME if Enhance descriptor with 8 DWORDS is enabled */
> +	return p->des2;
> +}
> +
> +static u32 enh_desc_get_timestamp_high(struct dma_desc *p)
> +{
> +	/* FIXME if Enhance descriptor with 8 DWORDS is enabled */
> +	return p->des3;
> +}
> +
> +static int enh_desc_get_rx_timestamp_status(struct dma_desc *p)
> +{
> +	/* FIXME if Enhance descriptor with 8 DWORDS is enabled */

Why not fix these FIXMEs for the next respin?

> +	if ((p->des2 == 0xffffffff) && (p->des3 == 0xffffffff))
> +		/* timestamp is currupted, hence don't store it */

corrupted

> +		return 0;
> +	else
> +		return 1;
> +}
> +
>  const struct stmmac_desc_ops enh_desc_ops = {
>  	.tx_status = enh_desc_get_tx_status,
>  	.rx_status = enh_desc_get_rx_status,
> @@ -339,4 +371,9 @@ const struct stmmac_desc_ops enh_desc_ops = {
>  	.set_tx_owner = enh_desc_set_tx_owner,
>  	.set_rx_owner = enh_desc_set_rx_owner,
>  	.get_rx_frame_len = enh_desc_get_rx_frame_len,
> +	.enable_tx_timestamp = enh_desc_enable_tx_timestamp,
> +	.get_tx_timestamp_status = enh_desc_get_tx_timestamp_status,
> +	.get_timestamp_low = enh_desc_get_timestamp_low,
> +	.get_timestamp_high = enh_desc_get_timestamp_high,
> +	.get_rx_timestamp_status = enh_desc_get_rx_timestamp_status,
>  };
> diff --git a/drivers/net/ethernet/stmicro/stmmac/norm_desc.c b/drivers/net/ethernet/stmicro/stmmac/norm_desc.c
> index 68962c5..edb5e88 100644
> --- a/drivers/net/ethernet/stmicro/stmmac/norm_desc.c
> +++ b/drivers/net/ethernet/stmicro/stmmac/norm_desc.c
> @@ -215,6 +215,35 @@ static int ndesc_get_rx_frame_len(struct dma_desc *p, int rx_coe_type)
>  		return p->des01.rx.frame_length;
>  }
>  
> +static void ndesc_enable_tx_timestamp(struct dma_desc *p)
> +{
> +	p->des01.tx.time_stamp_enable = 1;
> +}
> +
> +static int ndesc_get_tx_timestamp_status(struct dma_desc *p)
> +{
> +	return p->des01.tx.time_stamp_status;
> +}
> +
> +static u32 ndesc_get_timestamp_low(struct dma_desc *p)
> +{
> +	return p->des2;
> +}
> +
> +static u32 ndesc_get_timestamp_high(struct dma_desc *p)
> +{
> +	return p->des3;
> +}
> +
> +static int ndesc_get_rx_timestamp_status(struct dma_desc *p)
> +{
> +	if ((p->des2 == 0xffffffff) && (p->des3 == 0xffffffff))
> +		/* timestamp is currupted, hence don't store it */

corrupted

> +		return 0;
> +	else
> +		return 1;
> +}
> +
>  const struct stmmac_desc_ops ndesc_ops = {
>  	.tx_status = ndesc_get_tx_status,
>  	.rx_status = ndesc_get_rx_status,
> @@ -231,4 +260,9 @@ const struct stmmac_desc_ops ndesc_ops = {
>  	.set_tx_owner = ndesc_set_tx_owner,
>  	.set_rx_owner = ndesc_set_rx_owner,
>  	.get_rx_frame_len = ndesc_get_rx_frame_len,
> +	.enable_tx_timestamp = ndesc_enable_tx_timestamp,
> +	.get_tx_timestamp_status = ndesc_get_tx_timestamp_status,
> +	.get_timestamp_low = ndesc_get_timestamp_low,
> +	.get_timestamp_high = ndesc_get_timestamp_high,
> +	.get_rx_timestamp_status = ndesc_get_rx_timestamp_status,
>  };
> diff --git a/drivers/net/ethernet/stmicro/stmmac/ring_mode.c b/drivers/net/ethernet/stmicro/stmmac/ring_mode.c
> index 839e349..94a1c2f 100644
> --- a/drivers/net/ethernet/stmicro/stmmac/ring_mode.c
> +++ b/drivers/net/ethernet/stmicro/stmmac/ring_mode.c
> @@ -85,11 +85,14 @@ static unsigned int stmmac_is_jumbo_frm(int len, int enh_desc)
>  	return ret;
>  }
>  
> -static void stmmac_refill_desc3(int bfsize, struct dma_desc *p)
> +static void stmmac_refill_desc3(void *priv_ptr, struct dma_desc *p)
>  {
> -	/* Fill DES3 in case of RING mode */
> -	if (bfsize >= BUF_SIZE_8KiB)
> -		p->des3 = p->des2 + BUF_SIZE_8KiB;
> +	struct stmmac_priv *priv = (struct stmmac_priv *)priv_ptr;
> +
> +	if (unlikely(priv->plat->has_gmac))
> +		/* Fill DES3 in case of RING mode */
> +		if (priv->dma_buf_sz >= BUF_SIZE_8KiB)
> +			p->des3 = p->des2 + BUF_SIZE_8KiB;
>  }
>  
>  /* In ring mode we need to fill the desc3 because it is used
> @@ -105,7 +108,8 @@ static void stmmac_init_dma_chain(struct dma_desc *des, dma_addr_t phy_addr,
>  {
>  }
>  
> -static void stmmac_clean_desc3(struct dma_desc *p)
> +static void stmmac_clean_desc3(void *priv_ptr, struct dma_desc *p,
> +				int tstamp_taken)
>  {
>  	if (unlikely(p->des3))
>  		p->des3 = 0;
> diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac.h b/drivers/net/ethernet/stmicro/stmmac/stmmac.h
> index 013a7d5..665f2a2 100644
> --- a/drivers/net/ethernet/stmicro/stmmac/stmmac.h
> +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac.h
> @@ -94,6 +94,11 @@ struct stmmac_priv {
>  	u32 tx_coal_timer;
>  	int use_riwt;
>  	u32 rx_riwt;
> +#ifdef CONFIG_STMMAC_USE_HWSTAMP
> +	int hwts_tx_en;
> +	int hwts_rx_en;
> +	unsigned int default_addend;
> +#endif
>  };
>  
>  extern int phyaddr;
> @@ -103,6 +108,9 @@ extern int stmmac_mdio_register(struct net_device *ndev);
>  extern void stmmac_set_ethtool_ops(struct net_device *netdev);
>  extern const struct stmmac_desc_ops enh_desc_ops;
>  extern const struct stmmac_desc_ops ndesc_ops;
> +#ifdef CONFIG_STMMAC_USE_HWSTAMP
> +extern const struct stmmac_hwtimestamp stmmac_ptp;
> +#endif
>  int stmmac_freeze(struct net_device *ndev);
>  int stmmac_restore(struct net_device *ndev);
>  int stmmac_resume(struct net_device *ndev);
> diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_hwstamp.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_hwstamp.c
> new file mode 100644
> index 0000000..be9e399
> --- /dev/null
> +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_hwstamp.c
> @@ -0,0 +1,129 @@
> +/*******************************************************************************
> +  Copyright (C) 2013  Vayavya Labs Pvt Ltd
> +
> +  This implements all the API for managing HW timestamp & PTP.
> +
> +  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".
> +
> +  Author: Rayagond Kokatanur <rayagond@vayavyalabs.com>
> +  Author: Giuseppe Cavallaro <peppe.cavallaro@st.com>
> +*******************************************************************************/
> +
> +#include <linux/io.h>
> +#include <linux/delay.h>
> +#include "common.h"
> +#include "stmmac_ptp.h"
> +
> +static void stmmac_config_hw_tstamping(void __iomem *ioaddr, u32 data)
> +{
> +	writel(data, ioaddr + PTP_TCR);
> +}
> +
> +static void stmmac_config_sub_second_increment(void __iomem *ioaddr)
> +{
> +	u32 value = readl(ioaddr + PTP_TCR);
> +	unsigned long data;
> +
> +	/* Convert the ptp_clock to nano second
> +	 * formula = (1/ptp_clock) * 1000000000
> +	 * where, ptp_clock = 50MHz for FINE correction method &
> +	 * ptp_clock = STMMAC_SYSCLOCK for COARSE correction method
> +	 */

What are these coarse/fine method? Can't we always use the fine one?

> +	if (value & PTP_TCR_TSCFUPDT)
> +		data = (1000000000ULL / 50000000);
> +	else
> +		data = (1000000000ULL / STMMAC_SYSCLOCK);
> +
> +	writel(data, ioaddr + PTP_SSIR);
> +}
> +
> +static int stmmac_init_systime(void __iomem *ioaddr, u32 sec, u32 nsec)
> +{
> +	int limit;
> +	u32 value;
> +
> +	/* wait for previous(if any) system time initialize to complete */
> +	limit = 100;
> +	while (limit--) {
> +		if (!(readl(ioaddr + PTP_TCR) & PTP_TCR_TSINIT))
> +			break;
> +		mdelay(10);
> +	}
> +
> +	if (limit < 0)
> +		return -EBUSY;
> +

Ugh, this is terrible...

> +	writel(sec, ioaddr + PTP_STSUR);
> +	writel(nsec, ioaddr + PTP_STNSUR);
> +	/* issue command to initialize the system time value */
> +	value = readl(ioaddr + PTP_TCR);
> +	value |= PTP_TCR_TSINIT;
> +	writel(value, ioaddr + PTP_TCR);
> +
> +	/* wait for present system time initialize to complete */
> +	limit = 100;
> +	while (limit--) {
> +		if (!(readl(ioaddr + PTP_TCR) & PTP_TCR_TSINIT))
> +			break;
> +		mdelay(10);
> +	}
> +	if (limit < 0)
> +		return -EBUSY;

... for a number of reasons.

1. You are polling for 100*10ms = one second, and the caller has
   disabled interrupts! This is way too long. Is the hardware really
   that slow? If so, then you need to use delayed work or find some
   other way not to block.

2. You are waiting both before and after for the status bit. Pick one
   or the other. You don't need both.

This same pattern is repeated a few times here, and it needs fixing in
each case.

> +
> +	return 0;
> +}
> +
> +static int stmmac_config_addend(void __iomem *ioaddr, u32 addend)
> +{
> +	u32 value;
> +	int limit;
> +
> +	/* wait for previous (if any) addend update to complete */
> +	limit = 100;
> +	while (limit--) {
> +		if (!(readl(ioaddr + PTP_TCR) & PTP_TCR_TSADDREG))
> +			break;
> +		mdelay(10);
> +	}
> +	if (limit < 0)
> +		return -EBUSY;
> +
> +	writel(addend, ioaddr + PTP_TAR);
> +	/* issue command to update the addend value */
> +	value = readl(ioaddr + PTP_TCR);
> +	value |= PTP_TCR_TSADDREG;
> +	writel(value, ioaddr + PTP_TCR);
> +
> +	/* wait for present addend update to complete */
> +	limit = 100;
> +	while (limit--) {
> +		if (!(readl(ioaddr + PTP_TCR) & PTP_TCR_TSADDREG))
> +			break;
> +		mdelay(10);
> +	}
> +	if (limit < 0)
> +		return -EBUSY;
> +
> +	return 0;
> +}
> +
> +const struct stmmac_hwtimestamp stmmac_ptp = {
> +	.config_hw_tstamping = stmmac_config_hw_tstamping,
> +	.init_systime = stmmac_init_systime,
> +	.config_sub_second_increment = stmmac_config_sub_second_increment,
> +	.config_addend = stmmac_config_addend,
> +};
> diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
> index 260af93..3bbd554 100644
> --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
> +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
> @@ -47,6 +47,10 @@
>  #include <linux/debugfs.h>
>  #include <linux/seq_file.h>
>  #endif
> +#ifdef CONFIG_STMMAC_USE_HWSTAMP
> +#include <linux/net_tstamp.h>
> +#include "stmmac_ptp.h"
> +#endif
>  #include "stmmac.h"
>  
>  #undef STMMAC_DEBUG
> @@ -304,6 +308,192 @@ static void stmmac_eee_adjust(struct stmmac_priv *priv)
>  		priv->hw->mac->set_eee_pls(priv->ioaddr, priv->phydev->link);
>  }
>  
> +#ifdef CONFIG_STMMAC_USE_HWSTAMP
> +/* stmmac_get_tx_hwtstamp:
> + * @priv : pointer to private device structure.
> + * @p : pointer to desc structure.
> + * @skb : the socket buffer
> + * Description :
> + * This function will read timestamp from the descriptor & pass it to stack.
> + * and also perform some sanity checks.
> + * Return value :
> + * 1 if time stamp is taken & 0 if time stamp is not taken.
> + */
> +static unsigned int stmmac_get_tx_hwtstamp(struct stmmac_priv *priv,
> +					   struct dma_desc *p,
> +					   struct sk_buff *skb)
> +{
> +	struct skb_shared_hwtstamps shhwtstamp;
> +	u64 ns;
> +
> +	if (!priv->hwts_tx_en)
> +		return 0;
> +
> +	/* if skb doesn't support hw tstamp */
> +	if (likely(!(skb_shinfo(skb)->tx_flags & SKBTX_IN_PROGRESS)))
> +		return 0;
> +
> +	/* check tx tstamp status */
> +	if (!priv->hw->desc->get_tx_timestamp_status(p))
> +		return 0;
> +
> +	/* get the valid tstamp */
> +	ns = priv->hw->desc->get_timestamp_low(p);
> +	/* convert high/sec time stamp value to nanosecond */
> +	ns += priv->hw->desc->get_timestamp_high(p) * 1000000000ULL;
> +
> +	memset(&shhwtstamp, 0, sizeof(struct skb_shared_hwtstamps));
> +	shhwtstamp.hwtstamp = ns_to_ktime(ns);
> +	/* pass tstamp to stack */
> +	skb_tstamp_tx(skb, &shhwtstamp);
> +
> +	return 1;
> +}
> +
> +/* stmmac_get_rx_hwtstamp:
> + * @priv : pointer to private device structure.
> + * @p : pointer to desc structure.
> + * @skb : the socket buffer
> + * Description :
> + * This function will read received packet's timestamp from the descriptor
> + * and pass it to stack. It also perform some sanity checks.
> + */
> +static void stmmac_get_rx_hwtstamp(struct stmmac_priv *priv, struct dma_desc *p,
> +				   struct sk_buff *skb)
> +{
> +	struct skb_shared_hwtstamps *shhwtstamp = NULL;
> +	u64 ns;
> +
> +	if (!priv->hwts_rx_en)
> +		return;
> +
> +	/* if rx tstamp is not valid */
> +	if (!priv->hw->desc->get_rx_timestamp_status(p))
> +		return;
> +
> +	/* get valid tstamp */
> +	ns = priv->hw->desc->get_timestamp_low(p);
> +	/* convert high/sec time stamp value to nanosecond */
> +	ns += priv->hw->desc->get_timestamp_high(p) * 1000000000ULL;
> +	shhwtstamp = skb_hwtstamps(skb);
> +	memset(shhwtstamp, 0, sizeof(struct skb_shared_hwtstamps));
> +	shhwtstamp->hwtstamp = ns_to_ktime(ns);
> +}
> +
> +/**
> + *  stmmac_hwtstamp_ioctl - control hardware timestamping.
> + *  @dev: device pointer.
> + *  @ifr: An IOCTL specefic structure, that can contain a pointer to
> + *  a proprietary structure used to pass information to the driver.
> + *  Description:
> + *  This function configures the MAC to enable/disable both outgoing(TX)
> + *  and incoming(RX) packets time stamping based on user input.
> + *  Return Value:
> + *  0 on success and an appropriate -ve integer on failure.
> + */
> +static int stmmac_hwtstamp_ioctl(struct net_device *dev, struct ifreq *ifr)
> +{
> +	struct stmmac_priv *priv = netdev_priv(dev);
> +	struct hwtstamp_config config;
> +	struct timespec now;
> +	u64 temp = 0;

You add this new code here, but you change it all around again a few
patches later. Please just submit the final, combined version.

Thanks,
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
Giuseppe CAVALLARO March 8, 2013, 7:02 a.m. UTC | #2
On 3/8/2013 7:34 AM, Richard Cochran wrote:
> I have a few comments, below.

thanks for them.

>> HW Timestamp support can be enabled while configuring the Kernel and
>> the Koption is: STMMAC_USE_HWSTAMP
>> At runtime, the support is verified by looking at the HW capability
>> register.
>
> As davem said, since you have the ability to detect this at run time,
> then there is no need for a kconfig option.

I'll try to give you more details about this replying to D. Miller

[snip]

>> +	/* get tx/rx timestamp low value */
>> +	u32 (*get_timestamp_low) (struct dma_desc *p);
>> +	/* get tx/rx timestamp high value */
>> +	u32 (*get_timestamp_high) (struct dma_desc *p);
>
> These two separate functions should be combined into one.

ok

>> +	/* get rx timestamp status */
>> +	int (*get_rx_timestamp_status) (struct dma_desc *p);

>> +
>> +static int enh_desc_get_rx_timestamp_status(struct dma_desc *p)
>> +{
>> +	/* FIXME if Enhance descriptor with 8 DWORDS is enabled */
>
> Why not fix these FIXMEs for the next respin?

This is fixed in the patch #5 where we use the extended descriptors
for PTP2.

>> +	if ((p->des2 == 0xffffffff) && (p->des3 == 0xffffffff))
>> +		/* timestamp is currupted, hence don't store it */
>
> corrupted

thanks


>> +	/* Convert the ptp_clock to nano second
>> +	 * formula = (1/ptp_clock) * 1000000000
>> +	 * where, ptp_clock = 50MHz for FINE correction method &
>> +	 * ptp_clock = STMMAC_SYSCLOCK for COARSE correction method
>> +	 */
>
> What are these coarse/fine method? Can't we always use the fine one?

This is explained in the "4.1.2 System Time Register Module" of Synopsys 
Databook. Summarizing, the MAC can have an optional module and use this 
coarse correction method. In the fine correction method, a slave clock’s 
frequency drift with respect to the master clock is corrected over a 
period of time instead of in one clock, as in coarse correction.

Pls, Rayagond feels free to provide more details...

>> +	if (value & PTP_TCR_TSCFUPDT)
>> +		data = (1000000000ULL / 50000000);
>> +	else
>> +		data = (1000000000ULL / STMMAC_SYSCLOCK);
>> +
>> +	writel(data, ioaddr + PTP_SSIR);
>> +}
>> +
>> +static int stmmac_init_systime(void __iomem *ioaddr, u32 sec, u32 nsec)
>> +{
>> +	int limit;
>> +	u32 value;
>> +
>> +	/* wait for previous(if any) system time initialize to complete */
>> +	limit = 100;
>> +	while (limit--) {
>> +		if (!(readl(ioaddr + PTP_TCR) & PTP_TCR_TSINIT))
>> +			break;
>> +		mdelay(10);
>> +	}
>> +
>> +	if (limit < 0)
>> +		return -EBUSY;
>> +
>
> Ugh, this is terrible...
>
>> +	writel(sec, ioaddr + PTP_STSUR);
>> +	writel(nsec, ioaddr + PTP_STNSUR);
>> +	/* issue command to initialize the system time value */
>> +	value = readl(ioaddr + PTP_TCR);
>> +	value |= PTP_TCR_TSINIT;
>> +	writel(value, ioaddr + PTP_TCR);
>> +
>> +	/* wait for present system time initialize to complete */
>> +	limit = 100;
>> +	while (limit--) {
>> +		if (!(readl(ioaddr + PTP_TCR) & PTP_TCR_TSINIT))
>> +			break;
>> +		mdelay(10);
>> +	}
>> +	if (limit < 0)
>> +		return -EBUSY;
>
> ... for a number of reasons.
>
> 1. You are polling for 100*10ms = one second, and the caller has
>     disabled interrupts! This is way too long. Is the hardware really
>     that slow? If so, then you need to use delayed work or find some
>     other way not to block.
>
> 2. You are waiting both before and after for the status bit. Pick one
>     or the other. You don't need both.
>
> This same pattern is repeated a few times here, and it needs fixing in
> each case.

This is for the Author. I've no HW where test. Rayagond tested all on 
his side. Pls Rayagond fixes it and gives me the new code.

>> +	struct stmmac_priv *priv = netdev_priv(dev);
>> +	struct hwtstamp_config config;
>> +	struct timespec now;
>> +	u64 temp = 0;
>
> You add this new code here, but you change it all around again a few
> patches later. Please just submit the final, combined version.

we kept these separately because the patch #5 (for example) depends on
another one that adds the extended descriptor support. Also If I add
all the code in a single patch this will be very big. I had some
problems to review all separately. So I suspect that if we merge all
in a single patch this will not help (especially myself). At any rate,
tell me if you prefer to have a single patch. I can do that.

peppe
>
> Thanks,
> 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
Rayagond K March 8, 2013, 7:51 a.m. UTC | #3
On Fri, Mar 8, 2013 at 12:32 PM, Giuseppe CAVALLARO
<peppe.cavallaro@st.com> wrote:
>
> On 3/8/2013 7:34 AM, Richard Cochran wrote:
>>
>> I have a few comments, below.
>
>
> thanks for them.
>
>
>>> HW Timestamp support can be enabled while configuring the Kernel and
>>> the Koption is: STMMAC_USE_HWSTAMP
>>> At runtime, the support is verified by looking at the HW capability
>>> register.
>>
>>
>> As davem said, since you have the ability to detect this at run time,
>> then there is no need for a kconfig option.
>
>
> I'll try to give you more details about this replying to D. Miller
>
> [snip]
>
>
>>> +       /* get tx/rx timestamp low value */
>>> +       u32 (*get_timestamp_low) (struct dma_desc *p);
>>> +       /* get tx/rx timestamp high value */
>>> +       u32 (*get_timestamp_high) (struct dma_desc *p);
>>
>>
>> These two separate functions should be combined into one.
>
>
> ok
>
>
>>> +       /* get rx timestamp status */
>>> +       int (*get_rx_timestamp_status) (struct dma_desc *p);
>
>
>>> +
>>> +static int enh_desc_get_rx_timestamp_status(struct dma_desc *p)
>>> +{
>>> +       /* FIXME if Enhance descriptor with 8 DWORDS is enabled */
>>
>>
>> Why not fix these FIXMEs for the next respin?
>
>
> This is fixed in the patch #5 where we use the extended descriptors
> for PTP2.
>
>
>>> +       if ((p->des2 == 0xffffffff) && (p->des3 == 0xffffffff))
>>> +               /* timestamp is currupted, hence don't store it */
>>
>>
>> corrupted
>
>
> thanks
>
>
>
>>> +       /* Convert the ptp_clock to nano second
>>> +        * formula = (1/ptp_clock) * 1000000000
>>> +        * where, ptp_clock = 50MHz for FINE correction method &
>>> +        * ptp_clock = STMMAC_SYSCLOCK for COARSE correction method
>>> +        */
>>
>>
>> What are these coarse/fine method? Can't we always use the fine one?
>
>
> This is explained in the "4.1.2 System Time Register Module" of Synopsys
> Databook. Summarizing, the MAC can have an optional module and use this
> coarse correction method. In the fine correction method, a slave clock’s
> frequency drift with respect to the master clock is corrected over a period
> of time instead of in one clock, as in coarse correction.
>
> Pls, Rayagond feels free to provide more details...

Yes Synopsys core support two method for correcting the system time ie
COARSE method  and FINE method.

In the fine correction method, a slave clock’s frequency drift with
respect to the master clock (as defined in IEEE 1588) is corrected
over a period of time instead of in one clock, as in coarse
correction. This helps maintain linear time and does not introduce
drastic changes (or a large jitter) in the reference time between PTP
Sync message intervals.

And updating the SSNR (Sub Second Increment Register) depends on which
method is selected during the initialization.

>
>>> +       if (value & PTP_TCR_TSCFUPDT)
>>> +               data = (1000000000ULL / 50000000);
>>> +       else
>>> +               data = (1000000000ULL / STMMAC_SYSCLOCK);
>>> +
>>> +       writel(data, ioaddr + PTP_SSIR);
>>> +}
>>> +
>>> +static int stmmac_init_systime(void __iomem *ioaddr, u32 sec, u32 nsec)
>>> +{
>>> +       int limit;
>>> +       u32 value;
>>> +
>>> +       /* wait for previous(if any) system time initialize to complete
>>> */
>>> +       limit = 100;
>>> +       while (limit--) {
>>> +               if (!(readl(ioaddr + PTP_TCR) & PTP_TCR_TSINIT))
>>> +                       break;
>>> +               mdelay(10);
>>> +       }
>>> +
>>> +       if (limit < 0)
>>> +               return -EBUSY;
>>> +
>>
>>
>> Ugh, this is terrible...
>>
>>> +       writel(sec, ioaddr + PTP_STSUR);
>>> +       writel(nsec, ioaddr + PTP_STNSUR);
>>> +       /* issue command to initialize the system time value */
>>> +       value = readl(ioaddr + PTP_TCR);
>>> +       value |= PTP_TCR_TSINIT;
>>> +       writel(value, ioaddr + PTP_TCR);
>>> +
>>> +       /* wait for present system time initialize to complete */
>>> +       limit = 100;
>>> +       while (limit--) {
>>> +               if (!(readl(ioaddr + PTP_TCR) & PTP_TCR_TSINIT))
>>> +                       break;
>>> +               mdelay(10);
>>> +       }
>>> +       if (limit < 0)
>>> +               return -EBUSY;
>>
>>
>> ... for a number of reasons.
>>
>> 1. You are polling for 100*10ms = one second, and the caller has
>>     disabled interrupts! This is way too long. Is the hardware really
>>     that slow? If so, then you need to use delayed work or find some
>>     other way not to block.

Sorry for this, during testing I just used maximum delay as there was
bug in the HW initially. But after fixing the HW issue I noticed that
HW is not that much slow. We will reduce the delay in all functions.

>>
>> 2. You are waiting both before and after for the status bit. Pick one
>>     or the other. You don't need both.
>>
>> This same pattern is repeated a few times here, and it needs fixing in
>> each case.

Sure will change it. I added just to make sure double check here.
Practically first wait is not required, will remove this.

>
>
> This is for the Author. I've no HW where test. Rayagond tested all on his
> side. Pls Rayagond fixes it and gives me the new code.
>
>
>>> +       struct stmmac_priv *priv = netdev_priv(dev);
>>> +       struct hwtstamp_config config;
>>> +       struct timespec now;
>>> +       u64 temp = 0;
>>
>>
>> You add this new code here, but you change it all around again a few
>> patches later. Please just submit the final, combined version.
>
>
> we kept these separately because the patch #5 (for example) depends on
> another one that adds the extended descriptor support. Also If I add
> all the code in a single patch this will be very big. I had some
> problems to review all separately. So I suspect that if we merge all
> in a single patch this will not help (especially myself). At any rate,
> tell me if you prefer to have a single patch. I can do that.
>
> peppe
>>
>>
>> Thanks,
>> Richard
>>
>

Thanks
Rayagond.
--
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
Richard Cochran March 10, 2013, 12:25 p.m. UTC | #4
On Fri, Mar 08, 2013 at 08:02:21AM +0100, Giuseppe CAVALLARO wrote:
> On 3/8/2013 7:34 AM, Richard Cochran wrote:

> >>+
> >>+static int enh_desc_get_rx_timestamp_status(struct dma_desc *p)
> >>+{
> >>+	/* FIXME if Enhance descriptor with 8 DWORDS is enabled */
> >
> >Why not fix these FIXMEs for the next respin?
> 
> This is fixed in the patch #5 where we use the extended descriptors
> for PTP2.

If possible, it is nice for the reviewers and for the logic of the
patch series to order the changes so that these FIXMEs go away.
 
> >>+	struct hwtstamp_config config;
> >>+	struct timespec now;
> >>+	u64 temp = 0;
> >
> >You add this new code here, but you change it all around again a few
> >patches later. Please just submit the final, combined version.
> 
> we kept these separately because the patch #5 (for example) depends on
> another one that adds the extended descriptor support. Also If I add
> all the code in a single patch this will be very big. I had some
> problems to review all separately. So I suspect that if we merge all
> in a single patch this will not help (especially myself). At any rate,
> tell me if you prefer to have a single patch. I can do that.

I am not asking for bigger patches. It is good to arrange your patches
in small steps, since that makes both reviewing and bisecting easier.

However, for brand new code, I find it quite annoying to read one
patch, and then to have it all re-written in the next one. (If a new
function *only* grows during a patch series, that is easy to follow.)

So what I would like to see is a logical, understandable series of
small steps, but when new code appears, it is the real, final form.

Thanks,
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
Richard Cochran March 10, 2013, 12:31 p.m. UTC | #5
On Fri, Mar 08, 2013 at 01:15:00PM +0530, Rayagond K wrote:
> On Fri, Mar 8, 2013 at 12:32 PM, Giuseppe CAVALLARO <peppe.cavallaro@st.com>wrote:
> > On 3/8/2013 7:34 AM, Richard Cochran wrote:

> >> What are these coarse/fine method? Can't we always use the fine one?
> >>
> >
> > This is explained in the "4.1.2 System Time Register Module" of Synopsys
> > Databook. Summarizing, the MAC can have an optional module and use this
> > coarse correction method. In the fine correction method, a slave clock’s
> > frequency drift with respect to the master clock is corrected over a period
> > of time instead of in one clock, as in coarse correction.
> >
> > Pls, Rayagond feels free to provide more details...
> 
> 
> Yes Synopsys core support two method for correcting the system time
> ie COARSE method  and FINE method.
> 
> In the fine correction method, a slave clock’s frequency drift with respect
> to the master clock (as defined in IEEE 1588) is corrected over a period of
> time instead of in one clock, as in coarse correction. This helps maintain
> linear time and does not introduce drastic changes (or a large jitter) in
> the reference time between PTP Sync message intervals.

So it sounds like to me that you only should offer the COARSE
method. Smoothly changing the frequency of the clock is the job of the
clock servo which is running in user space.

Although some clocks (like this one and the phyter, for example) have
the ability to smoothly correct a given offset over a given time, we
do not have any user/kernel API for making use of this feature.

Thanks,
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
Richard Cochran March 10, 2013, 12:40 p.m. UTC | #6
On Sun, Mar 10, 2013 at 01:25:54PM +0100, Richard Cochran wrote:
> So what I would like to see is a logical, understandable series of
> small steps, but when new code appears, it is the real, final form.

Here is the order of patches that has made sense for other cards:

1. add hw time stamping support (all possible kinds) via so_timestamping
2. add ptp hardware clock support via ptp_clock_register

Thanks,
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
Giuseppe CAVALLARO March 11, 2013, 6:35 a.m. UTC | #7
On 3/10/2013 1:25 PM, Richard Cochran wrote:
> On Fri, Mar 08, 2013 at 08:02:21AM +0100, Giuseppe CAVALLARO wrote:
>> On 3/8/2013 7:34 AM, Richard Cochran wrote:
>
>>>> +
>>>> +static int enh_desc_get_rx_timestamp_status(struct dma_desc *p)
>>>> +{
>>>> +	/* FIXME if Enhance descriptor with 8 DWORDS is enabled */
>>>
>>> Why not fix these FIXMEs for the next respin?
>>
>> This is fixed in the patch #5 where we use the extended descriptors
>> for PTP2.
>
> If possible, it is nice for the reviewers and for the logic of the
> patch series to order the changes so that these FIXMEs go away.
>
>>>> +	struct hwtstamp_config config;
>>>> +	struct timespec now;
>>>> +	u64 temp = 0;
>>>
>>> You add this new code here, but you change it all around again a few
>>> patches later. Please just submit the final, combined version.
>>
>> we kept these separately because the patch #5 (for example) depends on
>> another one that adds the extended descriptor support. Also If I add
>> all the code in a single patch this will be very big. I had some
>> problems to review all separately. So I suspect that if we merge all
>> in a single patch this will not help (especially myself). At any rate,
>> tell me if you prefer to have a single patch. I can do that.
>
> I am not asking for bigger patches. It is good to arrange your patches
> in small steps, since that makes both reviewing and bisecting easier.

I tried to do that :-(

>
> However, for brand new code, I find it quite annoying to read one
> patch, and then to have it all re-written in the next one. (If a new
> function *only* grows during a patch series, that is easy to follow.)
>
> So what I would like to see is a logical, understandable series of
> small steps, but when new code appears, it is the real, final form.

Ok, I'll try to better re-organize the patches in the next version.

Thx a lot
Peppe

>
> Thanks,
> 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
>

--
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
diff mbox

Patch

diff --git a/drivers/net/ethernet/stmicro/stmmac/Kconfig b/drivers/net/ethernet/stmicro/stmmac/Kconfig
index c0ea838..ef703b3 100644
--- a/drivers/net/ethernet/stmicro/stmmac/Kconfig
+++ b/drivers/net/ethernet/stmicro/stmmac/Kconfig
@@ -71,5 +71,16 @@  config STMMAC_CHAINED
 
 endchoice
 
+config STMMAC_USE_HWSTAMP
+	bool "Use IEEE 1588 Precision Time Protocol"
+	depends on STMMAC_ETH
+	select PTP_1588_CLOCK
+	default n
+	---help---
+	  Enable this option to support the IEEE 1588 Precision Time Protocol
+	  (PTP) and HW Timestamps.
+	  At runtime, on new chip generations, the hardware capability
+	  register will be used to verify if either the IEEE 1588-2008 Advanced
+	  Timestamping (PTPv2) or IEEE 1588-2002 (PTPv1) is actually supported.
 
 endif
diff --git a/drivers/net/ethernet/stmicro/stmmac/Makefile b/drivers/net/ethernet/stmicro/stmmac/Makefile
index c8e8ea6..cc97c07 100644
--- a/drivers/net/ethernet/stmicro/stmmac/Makefile
+++ b/drivers/net/ethernet/stmicro/stmmac/Makefile
@@ -3,6 +3,7 @@  stmmac-$(CONFIG_STMMAC_RING) += ring_mode.o
 stmmac-$(CONFIG_STMMAC_CHAINED) += chain_mode.o
 stmmac-$(CONFIG_STMMAC_PLATFORM) += stmmac_platform.o
 stmmac-$(CONFIG_STMMAC_PCI) += stmmac_pci.o
+stmmac-$(CONFIG_STMMAC_USE_HWSTAMP) += stmmac_hwstamp.o
 stmmac-objs:= stmmac_main.o stmmac_ethtool.o stmmac_mdio.o	\
 	      dwmac_lib.o dwmac1000_core.o  dwmac1000_dma.o	\
 	      dwmac100_core.o dwmac100_dma.o enh_desc.o  norm_desc.o \
diff --git a/drivers/net/ethernet/stmicro/stmmac/chain_mode.c b/drivers/net/ethernet/stmicro/stmmac/chain_mode.c
index ad3d75f..dd60f6b 100644
--- a/drivers/net/ethernet/stmicro/stmmac/chain_mode.c
+++ b/drivers/net/ethernet/stmicro/stmmac/chain_mode.c
@@ -92,16 +92,35 @@  static unsigned int stmmac_is_jumbo_frm(int len, int enh_desc)
 	return ret;
 }
 
-static void stmmac_refill_desc3(int bfsize, struct dma_desc *p)
+static void stmmac_refill_desc3(void *priv_ptr, struct dma_desc *p)
 {
+	struct stmmac_priv *priv = (struct stmmac_priv *)priv_ptr;
+
+	if (priv->hwts_rx_en)
+		/* NOTE: Device will overwrite des3 with timestamp value if
+		 * 1588-2002 time stamping is enabled, hence reinitialize it
+		 * to keep explicit chaining in the descriptor.
+		 */
+		p->des3 = (unsigned int)(priv->dma_rx +
+			((priv->dirty_rx) + 1) % priv->dma_rx_size);
 }
 
 static void stmmac_init_desc3(int des3_as_data_buf, struct dma_desc *p)
 {
 }
 
-static void stmmac_clean_desc3(struct dma_desc *p)
+static void stmmac_clean_desc3(void *priv_ptr, struct dma_desc *p,
+				int tstamp_taken)
 {
+	struct stmmac_priv *priv = (struct stmmac_priv *)priv_ptr;
+
+	if (tstamp_taken && priv->hw->desc->get_tx_ls(p))
+		/* NOTE: Device will overwrite des3 with timestamp value if
+		 * 1588-2002 time stamping is enabled, hence reinitialize it
+		 * to keep explicit chaining in the descriptor.
+		 */
+		p->des3 = (unsigned int)(priv->dma_tx +
+			((priv->dirty_tx + 1) % priv->dma_tx_size));
 }
 
 static void stmmac_init_dma_chain(struct dma_desc *des, dma_addr_t phy_addr,
diff --git a/drivers/net/ethernet/stmicro/stmmac/common.h b/drivers/net/ethernet/stmicro/stmmac/common.h
index 186d148..fa8ff9b 100644
--- a/drivers/net/ethernet/stmicro/stmmac/common.h
+++ b/drivers/net/ethernet/stmicro/stmmac/common.h
@@ -290,6 +290,16 @@  struct stmmac_desc_ops {
 	/* Return the reception status looking at the RDES1 */
 	int (*rx_status) (void *data, struct stmmac_extra_stats *x,
 			  struct dma_desc *p);
+	/* Set tx timestamp enable bit */
+	void (*enable_tx_timestamp) (struct dma_desc *p);
+	/* get tx timestamp status */
+	int (*get_tx_timestamp_status) (struct dma_desc *p);
+	/* get tx/rx timestamp low value */
+	u32 (*get_timestamp_low) (struct dma_desc *p);
+	/* get tx/rx timestamp high value */
+	u32 (*get_timestamp_high) (struct dma_desc *p);
+	/* get rx timestamp status */
+	int (*get_rx_timestamp_status) (struct dma_desc *p);
 };
 
 struct stmmac_dma_ops {
@@ -346,6 +356,15 @@  struct stmmac_ops {
 	void (*set_eee_pls) (void __iomem *ioaddr, int link);
 };
 
+#ifdef CONFIG_STMMAC_USE_HWSTAMP
+struct stmmac_hwtimestamp {
+	void (*config_hw_tstamping) (void __iomem *ioaddr, u32 data);
+	void (*config_sub_second_increment) (void __iomem *ioaddr);
+	int (*init_systime) (void __iomem *ioaddr, u32 sec, u32 nsec);
+	int (*config_addend)(void __iomem *ioaddr, u32 addend);
+};
+#endif
+
 struct mac_link {
 	int port;
 	int duplex;
@@ -360,11 +379,11 @@  struct mii_regs {
 struct stmmac_ring_mode_ops {
 	unsigned int (*is_jumbo_frm) (int len, int ehn_desc);
 	unsigned int (*jumbo_frm) (void *priv, struct sk_buff *skb, int csum);
-	void (*refill_desc3) (int bfsize, struct dma_desc *p);
+	void (*refill_desc3) (void *priv, struct dma_desc *p);
 	void (*init_desc3) (int des3_as_data_buf, struct dma_desc *p);
 	void (*init_dma_chain) (struct dma_desc *des, dma_addr_t phy_addr,
 				unsigned int size);
-	void (*clean_desc3) (struct dma_desc *p);
+	void (*clean_desc3) (void *priv, struct dma_desc *p, int tstamp_taken);
 	int (*set_16kib_bfsize) (int mtu);
 };
 
@@ -373,6 +392,9 @@  struct mac_device_info {
 	const struct stmmac_desc_ops	*desc;
 	const struct stmmac_dma_ops	*dma;
 	const struct stmmac_ring_mode_ops	*ring;
+#ifdef CONFIG_STMMAC_USE_HWSTAMP
+	const struct stmmac_hwtimestamp	*ptp;
+#endif
 	struct mii_regs mii;	/* MII register Addresses */
 	struct mac_link link;
 	unsigned int synopsys_uid;
@@ -390,5 +412,4 @@  extern void stmmac_set_mac(void __iomem *ioaddr, bool enable);
 
 extern void dwmac_dma_flush_tx_fifo(void __iomem *ioaddr);
 extern const struct stmmac_ring_mode_ops ring_mode_ops;
-
 #endif /* __COMMON_H__ */
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c b/drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c
index 491d7e9..8c4ea93 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c
@@ -286,4 +286,3 @@  void stmmac_get_mac_addr(void __iomem *ioaddr, unsigned char *addr,
 	addr[4] = hi_addr & 0xff;
 	addr[5] = (hi_addr >> 8) & 0xff;
 }
-
diff --git a/drivers/net/ethernet/stmicro/stmmac/enh_desc.c b/drivers/net/ethernet/stmicro/stmmac/enh_desc.c
index 2fc8ef9..4d635d5 100644
--- a/drivers/net/ethernet/stmicro/stmmac/enh_desc.c
+++ b/drivers/net/ethernet/stmicro/stmmac/enh_desc.c
@@ -323,6 +323,38 @@  static int enh_desc_get_rx_frame_len(struct dma_desc *p, int rx_coe_type)
 		return p->des01.erx.frame_length;
 }
 
+static void enh_desc_enable_tx_timestamp(struct dma_desc *p)
+{
+	p->des01.etx.time_stamp_enable = 1;
+}
+
+static int enh_desc_get_tx_timestamp_status(struct dma_desc *p)
+{
+	return p->des01.etx.time_stamp_status;
+}
+
+static u32 enh_desc_get_timestamp_low(struct dma_desc *p)
+{
+	/* FIXME if Enhance descriptor with 8 DWORDS is enabled */
+	return p->des2;
+}
+
+static u32 enh_desc_get_timestamp_high(struct dma_desc *p)
+{
+	/* FIXME if Enhance descriptor with 8 DWORDS is enabled */
+	return p->des3;
+}
+
+static int enh_desc_get_rx_timestamp_status(struct dma_desc *p)
+{
+	/* FIXME if Enhance descriptor with 8 DWORDS is enabled */
+	if ((p->des2 == 0xffffffff) && (p->des3 == 0xffffffff))
+		/* timestamp is currupted, hence don't store it */
+		return 0;
+	else
+		return 1;
+}
+
 const struct stmmac_desc_ops enh_desc_ops = {
 	.tx_status = enh_desc_get_tx_status,
 	.rx_status = enh_desc_get_rx_status,
@@ -339,4 +371,9 @@  const struct stmmac_desc_ops enh_desc_ops = {
 	.set_tx_owner = enh_desc_set_tx_owner,
 	.set_rx_owner = enh_desc_set_rx_owner,
 	.get_rx_frame_len = enh_desc_get_rx_frame_len,
+	.enable_tx_timestamp = enh_desc_enable_tx_timestamp,
+	.get_tx_timestamp_status = enh_desc_get_tx_timestamp_status,
+	.get_timestamp_low = enh_desc_get_timestamp_low,
+	.get_timestamp_high = enh_desc_get_timestamp_high,
+	.get_rx_timestamp_status = enh_desc_get_rx_timestamp_status,
 };
diff --git a/drivers/net/ethernet/stmicro/stmmac/norm_desc.c b/drivers/net/ethernet/stmicro/stmmac/norm_desc.c
index 68962c5..edb5e88 100644
--- a/drivers/net/ethernet/stmicro/stmmac/norm_desc.c
+++ b/drivers/net/ethernet/stmicro/stmmac/norm_desc.c
@@ -215,6 +215,35 @@  static int ndesc_get_rx_frame_len(struct dma_desc *p, int rx_coe_type)
 		return p->des01.rx.frame_length;
 }
 
+static void ndesc_enable_tx_timestamp(struct dma_desc *p)
+{
+	p->des01.tx.time_stamp_enable = 1;
+}
+
+static int ndesc_get_tx_timestamp_status(struct dma_desc *p)
+{
+	return p->des01.tx.time_stamp_status;
+}
+
+static u32 ndesc_get_timestamp_low(struct dma_desc *p)
+{
+	return p->des2;
+}
+
+static u32 ndesc_get_timestamp_high(struct dma_desc *p)
+{
+	return p->des3;
+}
+
+static int ndesc_get_rx_timestamp_status(struct dma_desc *p)
+{
+	if ((p->des2 == 0xffffffff) && (p->des3 == 0xffffffff))
+		/* timestamp is currupted, hence don't store it */
+		return 0;
+	else
+		return 1;
+}
+
 const struct stmmac_desc_ops ndesc_ops = {
 	.tx_status = ndesc_get_tx_status,
 	.rx_status = ndesc_get_rx_status,
@@ -231,4 +260,9 @@  const struct stmmac_desc_ops ndesc_ops = {
 	.set_tx_owner = ndesc_set_tx_owner,
 	.set_rx_owner = ndesc_set_rx_owner,
 	.get_rx_frame_len = ndesc_get_rx_frame_len,
+	.enable_tx_timestamp = ndesc_enable_tx_timestamp,
+	.get_tx_timestamp_status = ndesc_get_tx_timestamp_status,
+	.get_timestamp_low = ndesc_get_timestamp_low,
+	.get_timestamp_high = ndesc_get_timestamp_high,
+	.get_rx_timestamp_status = ndesc_get_rx_timestamp_status,
 };
diff --git a/drivers/net/ethernet/stmicro/stmmac/ring_mode.c b/drivers/net/ethernet/stmicro/stmmac/ring_mode.c
index 839e349..94a1c2f 100644
--- a/drivers/net/ethernet/stmicro/stmmac/ring_mode.c
+++ b/drivers/net/ethernet/stmicro/stmmac/ring_mode.c
@@ -85,11 +85,14 @@  static unsigned int stmmac_is_jumbo_frm(int len, int enh_desc)
 	return ret;
 }
 
-static void stmmac_refill_desc3(int bfsize, struct dma_desc *p)
+static void stmmac_refill_desc3(void *priv_ptr, struct dma_desc *p)
 {
-	/* Fill DES3 in case of RING mode */
-	if (bfsize >= BUF_SIZE_8KiB)
-		p->des3 = p->des2 + BUF_SIZE_8KiB;
+	struct stmmac_priv *priv = (struct stmmac_priv *)priv_ptr;
+
+	if (unlikely(priv->plat->has_gmac))
+		/* Fill DES3 in case of RING mode */
+		if (priv->dma_buf_sz >= BUF_SIZE_8KiB)
+			p->des3 = p->des2 + BUF_SIZE_8KiB;
 }
 
 /* In ring mode we need to fill the desc3 because it is used
@@ -105,7 +108,8 @@  static void stmmac_init_dma_chain(struct dma_desc *des, dma_addr_t phy_addr,
 {
 }
 
-static void stmmac_clean_desc3(struct dma_desc *p)
+static void stmmac_clean_desc3(void *priv_ptr, struct dma_desc *p,
+				int tstamp_taken)
 {
 	if (unlikely(p->des3))
 		p->des3 = 0;
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac.h b/drivers/net/ethernet/stmicro/stmmac/stmmac.h
index 013a7d5..665f2a2 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac.h
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac.h
@@ -94,6 +94,11 @@  struct stmmac_priv {
 	u32 tx_coal_timer;
 	int use_riwt;
 	u32 rx_riwt;
+#ifdef CONFIG_STMMAC_USE_HWSTAMP
+	int hwts_tx_en;
+	int hwts_rx_en;
+	unsigned int default_addend;
+#endif
 };
 
 extern int phyaddr;
@@ -103,6 +108,9 @@  extern int stmmac_mdio_register(struct net_device *ndev);
 extern void stmmac_set_ethtool_ops(struct net_device *netdev);
 extern const struct stmmac_desc_ops enh_desc_ops;
 extern const struct stmmac_desc_ops ndesc_ops;
+#ifdef CONFIG_STMMAC_USE_HWSTAMP
+extern const struct stmmac_hwtimestamp stmmac_ptp;
+#endif
 int stmmac_freeze(struct net_device *ndev);
 int stmmac_restore(struct net_device *ndev);
 int stmmac_resume(struct net_device *ndev);
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_hwstamp.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_hwstamp.c
new file mode 100644
index 0000000..be9e399
--- /dev/null
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_hwstamp.c
@@ -0,0 +1,129 @@ 
+/*******************************************************************************
+  Copyright (C) 2013  Vayavya Labs Pvt Ltd
+
+  This implements all the API for managing HW timestamp & PTP.
+
+  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".
+
+  Author: Rayagond Kokatanur <rayagond@vayavyalabs.com>
+  Author: Giuseppe Cavallaro <peppe.cavallaro@st.com>
+*******************************************************************************/
+
+#include <linux/io.h>
+#include <linux/delay.h>
+#include "common.h"
+#include "stmmac_ptp.h"
+
+static void stmmac_config_hw_tstamping(void __iomem *ioaddr, u32 data)
+{
+	writel(data, ioaddr + PTP_TCR);
+}
+
+static void stmmac_config_sub_second_increment(void __iomem *ioaddr)
+{
+	u32 value = readl(ioaddr + PTP_TCR);
+	unsigned long data;
+
+	/* Convert the ptp_clock to nano second
+	 * formula = (1/ptp_clock) * 1000000000
+	 * where, ptp_clock = 50MHz for FINE correction method &
+	 * ptp_clock = STMMAC_SYSCLOCK for COARSE correction method
+	 */
+	if (value & PTP_TCR_TSCFUPDT)
+		data = (1000000000ULL / 50000000);
+	else
+		data = (1000000000ULL / STMMAC_SYSCLOCK);
+
+	writel(data, ioaddr + PTP_SSIR);
+}
+
+static int stmmac_init_systime(void __iomem *ioaddr, u32 sec, u32 nsec)
+{
+	int limit;
+	u32 value;
+
+	/* wait for previous(if any) system time initialize to complete */
+	limit = 100;
+	while (limit--) {
+		if (!(readl(ioaddr + PTP_TCR) & PTP_TCR_TSINIT))
+			break;
+		mdelay(10);
+	}
+
+	if (limit < 0)
+		return -EBUSY;
+
+	writel(sec, ioaddr + PTP_STSUR);
+	writel(nsec, ioaddr + PTP_STNSUR);
+	/* issue command to initialize the system time value */
+	value = readl(ioaddr + PTP_TCR);
+	value |= PTP_TCR_TSINIT;
+	writel(value, ioaddr + PTP_TCR);
+
+	/* wait for present system time initialize to complete */
+	limit = 100;
+	while (limit--) {
+		if (!(readl(ioaddr + PTP_TCR) & PTP_TCR_TSINIT))
+			break;
+		mdelay(10);
+	}
+	if (limit < 0)
+		return -EBUSY;
+
+	return 0;
+}
+
+static int stmmac_config_addend(void __iomem *ioaddr, u32 addend)
+{
+	u32 value;
+	int limit;
+
+	/* wait for previous (if any) addend update to complete */
+	limit = 100;
+	while (limit--) {
+		if (!(readl(ioaddr + PTP_TCR) & PTP_TCR_TSADDREG))
+			break;
+		mdelay(10);
+	}
+	if (limit < 0)
+		return -EBUSY;
+
+	writel(addend, ioaddr + PTP_TAR);
+	/* issue command to update the addend value */
+	value = readl(ioaddr + PTP_TCR);
+	value |= PTP_TCR_TSADDREG;
+	writel(value, ioaddr + PTP_TCR);
+
+	/* wait for present addend update to complete */
+	limit = 100;
+	while (limit--) {
+		if (!(readl(ioaddr + PTP_TCR) & PTP_TCR_TSADDREG))
+			break;
+		mdelay(10);
+	}
+	if (limit < 0)
+		return -EBUSY;
+
+	return 0;
+}
+
+const struct stmmac_hwtimestamp stmmac_ptp = {
+	.config_hw_tstamping = stmmac_config_hw_tstamping,
+	.init_systime = stmmac_init_systime,
+	.config_sub_second_increment = stmmac_config_sub_second_increment,
+	.config_addend = stmmac_config_addend,
+};
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
index 260af93..3bbd554 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
@@ -47,6 +47,10 @@ 
 #include <linux/debugfs.h>
 #include <linux/seq_file.h>
 #endif
+#ifdef CONFIG_STMMAC_USE_HWSTAMP
+#include <linux/net_tstamp.h>
+#include "stmmac_ptp.h"
+#endif
 #include "stmmac.h"
 
 #undef STMMAC_DEBUG
@@ -304,6 +308,192 @@  static void stmmac_eee_adjust(struct stmmac_priv *priv)
 		priv->hw->mac->set_eee_pls(priv->ioaddr, priv->phydev->link);
 }
 
+#ifdef CONFIG_STMMAC_USE_HWSTAMP
+/* stmmac_get_tx_hwtstamp:
+ * @priv : pointer to private device structure.
+ * @p : pointer to desc structure.
+ * @skb : the socket buffer
+ * Description :
+ * This function will read timestamp from the descriptor & pass it to stack.
+ * and also perform some sanity checks.
+ * Return value :
+ * 1 if time stamp is taken & 0 if time stamp is not taken.
+ */
+static unsigned int stmmac_get_tx_hwtstamp(struct stmmac_priv *priv,
+					   struct dma_desc *p,
+					   struct sk_buff *skb)
+{
+	struct skb_shared_hwtstamps shhwtstamp;
+	u64 ns;
+
+	if (!priv->hwts_tx_en)
+		return 0;
+
+	/* if skb doesn't support hw tstamp */
+	if (likely(!(skb_shinfo(skb)->tx_flags & SKBTX_IN_PROGRESS)))
+		return 0;
+
+	/* check tx tstamp status */
+	if (!priv->hw->desc->get_tx_timestamp_status(p))
+		return 0;
+
+	/* get the valid tstamp */
+	ns = priv->hw->desc->get_timestamp_low(p);
+	/* convert high/sec time stamp value to nanosecond */
+	ns += priv->hw->desc->get_timestamp_high(p) * 1000000000ULL;
+
+	memset(&shhwtstamp, 0, sizeof(struct skb_shared_hwtstamps));
+	shhwtstamp.hwtstamp = ns_to_ktime(ns);
+	/* pass tstamp to stack */
+	skb_tstamp_tx(skb, &shhwtstamp);
+
+	return 1;
+}
+
+/* stmmac_get_rx_hwtstamp:
+ * @priv : pointer to private device structure.
+ * @p : pointer to desc structure.
+ * @skb : the socket buffer
+ * Description :
+ * This function will read received packet's timestamp from the descriptor
+ * and pass it to stack. It also perform some sanity checks.
+ */
+static void stmmac_get_rx_hwtstamp(struct stmmac_priv *priv, struct dma_desc *p,
+				   struct sk_buff *skb)
+{
+	struct skb_shared_hwtstamps *shhwtstamp = NULL;
+	u64 ns;
+
+	if (!priv->hwts_rx_en)
+		return;
+
+	/* if rx tstamp is not valid */
+	if (!priv->hw->desc->get_rx_timestamp_status(p))
+		return;
+
+	/* get valid tstamp */
+	ns = priv->hw->desc->get_timestamp_low(p);
+	/* convert high/sec time stamp value to nanosecond */
+	ns += priv->hw->desc->get_timestamp_high(p) * 1000000000ULL;
+	shhwtstamp = skb_hwtstamps(skb);
+	memset(shhwtstamp, 0, sizeof(struct skb_shared_hwtstamps));
+	shhwtstamp->hwtstamp = ns_to_ktime(ns);
+}
+
+/**
+ *  stmmac_hwtstamp_ioctl - control hardware timestamping.
+ *  @dev: device pointer.
+ *  @ifr: An IOCTL specefic structure, that can contain a pointer to
+ *  a proprietary structure used to pass information to the driver.
+ *  Description:
+ *  This function configures the MAC to enable/disable both outgoing(TX)
+ *  and incoming(RX) packets time stamping based on user input.
+ *  Return Value:
+ *  0 on success and an appropriate -ve integer on failure.
+ */
+static int stmmac_hwtstamp_ioctl(struct net_device *dev, struct ifreq *ifr)
+{
+	struct stmmac_priv *priv = netdev_priv(dev);
+	struct hwtstamp_config config;
+	struct timespec now;
+	u64 temp = 0;
+
+	if (!priv->dma_cap.time_stamp) {
+		netdev_alert(priv->dev, "No HW time stamping supported\n");
+		priv->hwts_tx_en = 0;
+		priv->hwts_rx_en = 0;
+
+		return -EOPNOTSUPP;
+	}
+
+	if (copy_from_user(&config, ifr->ifr_data,
+		sizeof(struct hwtstamp_config)))
+		return -EFAULT;
+
+	pr_debug("%s config flags:0x%x, tx_type:0x%x, rx_filter:0x%x\n",
+		 __func__, config.flags, config.tx_type, config.rx_filter);
+
+	/* reserved for future extensions */
+	if (config.flags)
+		return -EINVAL;
+
+	switch (config.tx_type) {
+	case HWTSTAMP_TX_OFF:
+		priv->hwts_tx_en = 0;
+		break;
+	case HWTSTAMP_TX_ON:
+		priv->hwts_tx_en = 1;
+		break;
+	default:
+		return -ERANGE;
+	}
+
+	switch (config.rx_filter) {
+	case HWTSTAMP_FILTER_NONE:
+		config.rx_filter = HWTSTAMP_FILTER_NONE;
+		break;
+	default:
+		/* PTP v1, UDP, any kind of event packet */
+		config.rx_filter = HWTSTAMP_FILTER_PTP_V1_L4_EVENT;
+		break;
+	}
+	priv->hwts_rx_en = ((config.rx_filter == HWTSTAMP_FILTER_NONE) ? 0 : 1);
+
+	if (!priv->hwts_tx_en && !priv->hwts_rx_en)
+		priv->hw->ptp->config_hw_tstamping(priv->ioaddr, 0);
+	else {
+		priv->hw->ptp->config_hw_tstamping(priv->ioaddr,
+			(PTP_TCR_TSENA | PTP_TCR_TSCFUPDT));
+
+		/* program Sub Second Increment reg */
+		priv->hw->ptp->config_sub_second_increment(priv->ioaddr);
+
+		/* calculate default added value:
+		 * formula is :
+		 * addend = (2^32)/freq_div_ratio;
+		 * where, freq_div_ratio = STMMAC_SYSCLOCK/50MHz
+		 * hence, addend = ((2^32) * 50MHz)/STMMAC_SYSCLOCK;
+		 * NOTE: STMMAC_SYSCLOCK should be >= 50MHz to
+		 *       achive 20ns accuracy.
+		 *
+		 * 2^x * y == (y << x), hence
+		 * 2^32 * 50000000 ==> (50000000 << 32)
+		 */
+		temp = (u64)(50000000ULL << 32);
+		priv->default_addend = div_u64(temp, STMMAC_SYSCLOCK);
+		priv->hw->ptp->config_addend(priv->ioaddr,
+					     priv->default_addend);
+
+		/* initialize system time */
+		getnstimeofday(&now);
+		priv->hw->ptp->init_systime(priv->ioaddr, now.tv_sec,
+					    now.tv_nsec);
+	}
+
+	return copy_to_user(ifr->ifr_data, &config,
+			    sizeof(struct hwtstamp_config)) ? -EFAULT : 0;
+}
+
+static void stmmac_init_ptp(struct stmmac_priv *priv)
+{
+	if (priv->dma_cap.time_stamp)
+		pr_debug("IEEE 1588-2002 Time Stamp supported\n");
+	if (priv->dma_cap.atime_stamp)
+		pr_debug("IEEE 1588-2008 Advanced Time Stamp supported\n");
+
+	priv->hw->ptp = &stmmac_ptp;
+
+	priv->hwts_tx_en = 0;
+	priv->hwts_rx_en = 0;
+
+}
+#else
+#define stmmac_hwtstamp_ioctl(dev, ifr)	(-EOPNOTSUPP)
+#define stmmac_get_rx_hwtstamp(priv, p, skb)
+#define stmmac_get_tx_hwtstamp(priv, p, skb) 0
+#define stmmac_init_ptp(priv)
+#endif /* CONFIG_STMMAC_USE_HWSTAMP */
+
 /**
  * stmmac_adjust_link
  * @dev: net device structure
@@ -712,6 +902,7 @@  static void stmmac_tx_clean(struct stmmac_priv *priv)
 
 	while (priv->dirty_tx != priv->cur_tx) {
 		int last;
+		unsigned int ts = 0;
 		unsigned int entry = priv->dirty_tx % txsize;
 		struct sk_buff *skb = priv->tx_skbuff[entry];
 		struct dma_desc *p = priv->dma_tx + entry;
@@ -732,6 +923,8 @@  static void stmmac_tx_clean(struct stmmac_priv *priv)
 				priv->xstats.tx_pkt_n++;
 			} else
 				priv->dev->stats.tx_errors++;
+
+			ts = stmmac_get_tx_hwtstamp(priv, p, skb);
 		}
 		TX_DBG("%s: curr %d, dirty %d\n", __func__,
 			priv->cur_tx, priv->dirty_tx);
@@ -743,7 +936,7 @@  static void stmmac_tx_clean(struct stmmac_priv *priv)
 					 DMA_TO_DEVICE);
 			priv->tx_skbuff_dma[entry] = 0;
 		}
-		priv->hw->ring->clean_desc3(p);
+		priv->hw->ring->clean_desc3(priv, p, ts);
 
 		if (likely(skb != NULL)) {
 			dev_kfree_skb(skb);
@@ -1027,6 +1220,7 @@  static int stmmac_open(struct net_device *dev)
 		goto open_error;
 	}
 
+
 	/* Create and initialize the TX/RX descriptors chains. */
 	priv->dma_tx_size = STMMAC_ALIGN(dma_txsize);
 	priv->dma_rx_size = STMMAC_ALIGN(dma_rxsize);
@@ -1093,6 +1287,8 @@  static int stmmac_open(struct net_device *dev)
 
 	stmmac_mmc_setup(priv);
 
+	stmmac_init_ptp(priv);
+
 #ifdef CONFIG_STMMAC_DEBUG_FS
 	ret = stmmac_init_fs(dev);
 	if (ret < 0)
@@ -1325,7 +1521,17 @@  static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev)
 
 	dev->stats.tx_bytes += skb->len;
 
-	skb_tx_timestamp(skb);
+#ifdef CONFIG_STMMAC_USE_HWSTAMP
+	if (unlikely((skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) &&
+		     priv->hwts_tx_en)) {
+		/* declare that device is doing timestamping */
+		skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS;
+		priv->hw->desc->enable_tx_timestamp(first);
+	}
+
+	if (!priv->hwts_tx_en)
+#endif /* CONFIG_STMMAC_USE_HWSTAMP */
+		skb_tx_timestamp(skb);
 
 	priv->hw->dma->enable_dma_transmission(priv->ioaddr);
 
@@ -1357,8 +1563,7 @@  static inline void stmmac_rx_refill(struct stmmac_priv *priv)
 
 			(p + entry)->des2 = priv->rx_skbuff_dma[entry];
 
-			if (unlikely(priv->plat->has_gmac))
-				priv->hw->ring->refill_desc3(bfsize, p + entry);
+			priv->hw->ring->refill_desc3(priv, p + entry);
 
 			RX_DBG(KERN_INFO "\trefill entry #%d\n", entry);
 		}
@@ -1398,9 +1603,22 @@  static int stmmac_rx(struct stmmac_priv *priv, int limit)
 		/* read the status of the incoming frame */
 		status = (priv->hw->desc->rx_status(&priv->dev->stats,
 						    &priv->xstats, p));
-		if (unlikely(status == discard_frame))
+		if (unlikely(status == discard_frame)) {
 			priv->dev->stats.rx_errors++;
-		else {
+#ifdef CONFIG_STMMAC_USE_HWSTAMP
+			if (priv->hwts_rx_en) {
+				/* DESC2 & DESC3 will be overwitten by device
+				 * with timestamp value, hence reinitialize
+				 * them in stmmac_rx_refill() function so that
+				 * device can reuse it.
+				 */
+				priv->rx_skbuff[entry] = NULL;
+				dma_unmap_single(priv->device,
+					priv->rx_skbuff_dma[entry],
+					priv->dma_buf_sz, DMA_FROM_DEVICE);
+			}
+#endif /* CONFIG_STMMAC_USE_HWSTAMP */
+		} else {
 			struct sk_buff *skb;
 			int frame_len;
 
@@ -1429,6 +1647,8 @@  static int stmmac_rx(struct stmmac_priv *priv, int limit)
 			prefetch(skb->data - NET_IP_ALIGN);
 			priv->rx_skbuff[entry] = NULL;
 
+			stmmac_get_rx_hwtstamp(priv, p, skb);
+
 			skb_put(skb, frame_len);
 			dma_unmap_single(priv->device,
 					 priv->rx_skbuff_dma[entry],
@@ -1666,21 +1886,30 @@  static void stmmac_poll_controller(struct net_device *dev)
  *  a proprietary structure used to pass information to the driver.
  *  @cmd: IOCTL command
  *  Description:
- *  Currently there are no special functionality supported in IOCTL, just the
- *  phy_mii_ioctl(...) can be invoked.
+ *  Currently it supports just the phy_mii_ioctl(...) and HW time stamping.
  */
 static int stmmac_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
 {
 	struct stmmac_priv *priv = netdev_priv(dev);
-	int ret;
+	int ret = -EOPNOTSUPP;
 
 	if (!netif_running(dev))
 		return -EINVAL;
 
-	if (!priv->phydev)
-		return -EINVAL;
-
-	ret = phy_mii_ioctl(priv->phydev, rq, cmd);
+	switch (cmd) {
+	case SIOCGMIIPHY:
+	case SIOCGMIIREG:
+	case SIOCSMIIREG:
+		if (!priv->phydev)
+			return -EINVAL;
+		ret = phy_mii_ioctl(priv->phydev, rq, cmd);
+		break;
+	case SIOCSHWTSTAMP:
+		ret = stmmac_hwtstamp_ioctl(dev, rq);
+		break;
+	default:
+		break;
+	}
 
 	return ret;
 }
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_ptp.h b/drivers/net/ethernet/stmicro/stmmac/stmmac_ptp.h
new file mode 100644
index 0000000..d557696
--- /dev/null
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_ptp.h
@@ -0,0 +1,72 @@ 
+/******************************************************************************
+  PTP Header file
+
+  Copyright (C) 2013  Vayavya Labs Pvt Ltd
+
+  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".
+
+  Author: Rayagond Kokatanur <rayagond@vayavyalabs.com>
+******************************************************************************/
+
+#ifndef __STMMAC_PTP_H__
+#define __STMMAC_PTP_H__
+
+#define STMMAC_SYSCLOCK 62500000
+
+/* IEEE 1588 PTP register offsets */
+#define PTP_TCR		0x0700	/* Timestamp Control Reg */
+#define PTP_SSIR	0x0704	/* Sub-Second Increment Reg */
+#define PTP_STSR	0x0708	/* System Time – Seconds Regr */
+#define PTP_STNSR	0x070C	/* System Time – Nanoseconds Reg */
+#define PTP_STSUR	0x0710	/* System Time – Seconds Update Reg */
+#define PTP_STNSUR	0x0714	/* System Time – Nanoseconds Update Reg */
+#define PTP_TAR		0x0718	/* Timestamp Addend Reg */
+#define PTP_TTSR	0x071C	/* Target Time Seconds Reg */
+#define PTP_TTNSR	0x0720	/* Target Time Nanoseconds Reg */
+#define	PTP_STHWSR	0x0724	/* System Time - Higher Word Seconds Reg */
+#define PTP_TSR		0x0728	/* Timestamp Status */
+
+/* PTP TCR defines */
+#define PTP_TCR_TSENA		0x00000001 /* Timestamp Enable */
+#define PTP_TCR_TSCFUPDT	0x00000002 /* Timestamp Fine/Coarse Update */
+#define PTP_TCR_TSINIT		0x00000004 /* Timestamp Initialize */
+#define PTP_TCR_TSUPDT		0x00000008 /* Timestamp Update */
+/* Timestamp Interrupt Trigger Enable */
+#define PTP_TCR_TSTRIG		0x00000010
+#define PTP_TCR_TSADDREG	0x00000020 /* Addend Reg Update */
+#define PTP_TCR_TSENALL		0x00000100 /* Enable Timestamp for All Frames */
+/* Timestamp Digital or Binary Rollover Control */
+#define PTP_TCR_TSCTRLSSR	0x00000200
+
+/* Enable PTP packet Processing for Version 2 Format */
+#define PTP_TCR_TSVER2ENA	0x00000400
+/* Enable Processing of PTP over Ethernet Frames */
+#define PTP_TCR_TSIPENA		0x00000800
+/* Enable Processing of PTP Frames Sent over IPv6-UDP */
+#define PTP_TCR_TSIPV6ENA	0x00001000
+/* Enable Processing of PTP Frames Sent over IPv4-UDP */
+#define PTP_TCR_TSIPV4ENA	0x00002000
+/* Enable Timestamp Snapshot for Event Messages */
+#define PTP_TCR_TSEVNTENA	0x00004000
+/* Enable Snapshot for Messages Relevant to Master */
+#define PTP_TCR_TSMSTRENA	0x00008000
+/* Select PTP packets for Taking Snapshots */
+#define PTP_TCR_SNAPTYPSEL_1	0x00010000
+/* Enable MAC address for PTP Frame Filtering */
+#define PTP_TCR_TSENMACADDR	0x00040000
+
+#endif /* __STMMAC_PTP_H__ */