diff options
Diffstat (limited to 'drivers/net/ethernet/stmicro')
20 files changed, 1986 insertions, 395 deletions
diff --git a/drivers/net/ethernet/stmicro/stmmac/Kconfig b/drivers/net/ethernet/stmicro/stmmac/Kconfig index c0ea838c78d..f695a50bac4 100644 --- a/drivers/net/ethernet/stmicro/stmmac/Kconfig +++ b/drivers/net/ethernet/stmicro/stmmac/Kconfig @@ -5,6 +5,7 @@ config STMMAC_ETH  	select MII  	select PHYLIB  	select CRC32 +	select PTP_1588_CLOCK  	---help---  	  This is the driver for the Ethernet IPs are built around a  	  Synopsys IP Core and only tested on the STMicroelectronics @@ -54,22 +55,4 @@ config STMMAC_DA  	  By default, the DMA arbitration scheme is based on Round-robin  	  (rx:tx priority is 1:1). -choice -	prompt "Select the DMA TX/RX descriptor operating modes" -	depends on STMMAC_ETH -	---help--- -	  This driver supports DMA descriptor to operate both in dual buffer -	  (RING) and linked-list(CHAINED) mode. In RING mode each descriptor -	  points to two data buffer pointers whereas in CHAINED mode they -	  points to only one data buffer pointer. - -config STMMAC_RING -	bool "Enable Descriptor Ring Mode" - -config STMMAC_CHAINED -	bool "Enable Descriptor Chained Mode" - -endchoice - -  endif diff --git a/drivers/net/ethernet/stmicro/stmmac/Makefile b/drivers/net/ethernet/stmicro/stmmac/Makefile index c8e8ea60ac1..356a9dd32be 100644 --- a/drivers/net/ethernet/stmicro/stmmac/Makefile +++ b/drivers/net/ethernet/stmicro/stmmac/Makefile @@ -1,9 +1,7 @@  obj-$(CONFIG_STMMAC_ETH) += stmmac.o -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-objs:= stmmac_main.o stmmac_ethtool.o stmmac_mdio.o	\ -	      dwmac_lib.o dwmac1000_core.o  dwmac1000_dma.o	\ +stmmac-objs:= stmmac_main.o stmmac_ethtool.o stmmac_mdio.o ring_mode.o	\ +	      chain_mode.o dwmac_lib.o dwmac1000_core.o  dwmac1000_dma.o \  	      dwmac100_core.o dwmac100_dma.o enh_desc.o  norm_desc.o \ -	      mmc_core.o $(stmmac-y) +	      mmc_core.o stmmac_hwtstamp.o stmmac_ptp.o $(stmmac-y) diff --git a/drivers/net/ethernet/stmicro/stmmac/chain_mode.c b/drivers/net/ethernet/stmicro/stmmac/chain_mode.c index 0668659803e..37a3f93b487 100644 --- a/drivers/net/ethernet/stmicro/stmmac/chain_mode.c +++ b/drivers/net/ethernet/stmicro/stmmac/chain_mode.c @@ -28,7 +28,7 @@  #include "stmmac.h" -unsigned int stmmac_jumbo_frm(void *p, struct sk_buff *skb, int csum) +static unsigned int stmmac_jumbo_frm(void *p, struct sk_buff *skb, int csum)  {  	struct stmmac_priv *priv = (struct stmmac_priv *) p;  	unsigned int txsize = priv->dma_tx_size; @@ -47,7 +47,8 @@ unsigned int stmmac_jumbo_frm(void *p, struct sk_buff *skb, int csum)  	desc->des2 = dma_map_single(priv->device, skb->data,  				    bmax, DMA_TO_DEVICE); -	priv->hw->desc->prepare_tx_desc(desc, 1, bmax, csum); +	priv->tx_skbuff_dma[entry] = desc->des2; +	priv->hw->desc->prepare_tx_desc(desc, 1, bmax, csum, STMMAC_CHAIN_MODE);  	while (len != 0) {  		entry = (++priv->cur_tx) % txsize; @@ -57,8 +58,9 @@ unsigned int stmmac_jumbo_frm(void *p, struct sk_buff *skb, int csum)  			desc->des2 = dma_map_single(priv->device,  						    (skb->data + bmax * i),  						    bmax, DMA_TO_DEVICE); -			priv->hw->desc->prepare_tx_desc(desc, 0, bmax, -							csum); +			priv->tx_skbuff_dma[entry] = desc->des2; +			priv->hw->desc->prepare_tx_desc(desc, 0, bmax, csum, +							STMMAC_CHAIN_MODE);  			priv->hw->desc->set_tx_owner(desc);  			priv->tx_skbuff[entry] = NULL;  			len -= bmax; @@ -67,8 +69,9 @@ unsigned int stmmac_jumbo_frm(void *p, struct sk_buff *skb, int csum)  			desc->des2 = dma_map_single(priv->device,  						    (skb->data + bmax * i), len,  						    DMA_TO_DEVICE); -			priv->hw->desc->prepare_tx_desc(desc, 0, len, -							csum); +			priv->tx_skbuff_dma[entry] = desc->des2; +			priv->hw->desc->prepare_tx_desc(desc, 0, len, csum, +							STMMAC_CHAIN_MODE);  			priv->hw->desc->set_tx_owner(desc);  			priv->tx_skbuff[entry] = NULL;  			len = 0; @@ -89,49 +92,70 @@ 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_init_desc3(int des3_as_data_buf, struct dma_desc *p) -{ -} - -static void stmmac_clean_desc3(struct dma_desc *p) -{ -} - -static void stmmac_init_dma_chain(struct dma_desc *des, dma_addr_t phy_addr, -				  unsigned int size) +static void stmmac_init_dma_chain(void *des, dma_addr_t phy_addr, +				  unsigned int size, unsigned int extend_desc)  {  	/*  	 * In chained mode the des3 points to the next element in the ring.  	 * The latest element has to point to the head.  	 */  	int i; -	struct dma_desc *p = des;  	dma_addr_t dma_phy = phy_addr; -	for (i = 0; i < (size - 1); i++) { -		dma_phy += sizeof(struct dma_desc); -		p->des3 = (unsigned int)dma_phy; -		p++; +	if (extend_desc) { +		struct dma_extended_desc *p = (struct dma_extended_desc *) des; +		for (i = 0; i < (size - 1); i++) { +			dma_phy += sizeof(struct dma_extended_desc); +			p->basic.des3 = (unsigned int)dma_phy; +			p++; +		} +		p->basic.des3 = (unsigned int)phy_addr; + +	} else { +		struct dma_desc *p = (struct dma_desc *) des; +		for (i = 0; i < (size - 1); i++) { +			dma_phy += sizeof(struct dma_desc); +			p->des3 = (unsigned int)dma_phy; +			p++; +		} +		p->des3 = (unsigned int)phy_addr;  	} -	p->des3 = (unsigned int)phy_addr;  } -static int stmmac_set_16kib_bfsize(int mtu) +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 && !priv->extend_desc) +		/* 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_phy + +					 (((priv->dirty_rx) + 1) % +					 priv->dma_rx_size) * +					 sizeof(struct dma_desc)); +} + +static void stmmac_clean_desc3(void *priv_ptr, struct dma_desc *p)  { -	/* Not supported */ -	return 0; +	struct stmmac_priv *priv = (struct stmmac_priv *)priv_ptr; + +	if (priv->hw->desc->get_tx_ls(p) && !priv->extend_desc) +		/* 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_phy + +					 (((priv->dirty_tx + 1) % +					 priv->dma_tx_size) * +					 sizeof(struct dma_desc)));  } -const struct stmmac_ring_mode_ops ring_mode_ops = { +const struct stmmac_chain_mode_ops chain_mode_ops = { +	.init = stmmac_init_dma_chain,  	.is_jumbo_frm = stmmac_is_jumbo_frm,  	.jumbo_frm = stmmac_jumbo_frm,  	.refill_desc3 = stmmac_refill_desc3, -	.init_desc3 = stmmac_init_desc3, -	.init_dma_chain = stmmac_init_dma_chain,  	.clean_desc3 = stmmac_clean_desc3, -	.set_16kib_bfsize = stmmac_set_16kib_bfsize,  }; diff --git a/drivers/net/ethernet/stmicro/stmmac/common.h b/drivers/net/ethernet/stmicro/stmmac/common.h index 186d1480612..ad7e20a9875 100644 --- a/drivers/net/ethernet/stmicro/stmmac/common.h +++ b/drivers/net/ethernet/stmicro/stmmac/common.h @@ -117,6 +117,36 @@ struct stmmac_extra_stats {  	unsigned long irq_rx_path_in_lpi_mode_n;  	unsigned long irq_rx_path_exit_lpi_mode_n;  	unsigned long phy_eee_wakeup_error_n; +	/* Extended RDES status */ +	unsigned long ip_hdr_err; +	unsigned long ip_payload_err; +	unsigned long ip_csum_bypassed; +	unsigned long ipv4_pkt_rcvd; +	unsigned long ipv6_pkt_rcvd; +	unsigned long rx_msg_type_ext_no_ptp; +	unsigned long rx_msg_type_sync; +	unsigned long rx_msg_type_follow_up; +	unsigned long rx_msg_type_delay_req; +	unsigned long rx_msg_type_delay_resp; +	unsigned long rx_msg_type_pdelay_req; +	unsigned long rx_msg_type_pdelay_resp; +	unsigned long rx_msg_type_pdelay_follow_up; +	unsigned long ptp_frame_type; +	unsigned long ptp_ver; +	unsigned long timestamp_dropped; +	unsigned long av_pkt_rcvd; +	unsigned long av_tagged_pkt_rcvd; +	unsigned long vlan_tag_priority_val; +	unsigned long l3_filter_match; +	unsigned long l4_filter_match; +	unsigned long l3_l4_filter_no_match; +	/* PCS */ +	unsigned long irq_pcs_ane_n; +	unsigned long irq_pcs_link_n; +	unsigned long irq_rgmii_n; +	unsigned long pcs_link; +	unsigned long pcs_duplex; +	unsigned long pcs_speed;  };  /* CSR Frequency Access Defines*/ @@ -138,6 +168,12 @@ struct stmmac_extra_stats {  #define FLOW_TX		2  #define FLOW_AUTO	(FLOW_TX | FLOW_RX) +/* PCS defines */ +#define STMMAC_PCS_RGMII	(1 << 0) +#define STMMAC_PCS_SGMII	(1 << 1) +#define STMMAC_PCS_TBI		(1 << 2) +#define STMMAC_PCS_RTBI		(1 << 3) +  #define SF_DMA_MODE 1 /* DMA STORE-AND-FORWARD Operation Mode */  /* DAM HW feature register fields */ @@ -194,17 +230,25 @@ enum dma_irq_status {  	handle_tx = 0x8,  }; -enum core_specific_irq_mask { -	core_mmc_tx_irq = 1, -	core_mmc_rx_irq = 2, -	core_mmc_rx_csum_offload_irq = 4, -	core_irq_receive_pmt_irq = 8, -	core_irq_tx_path_in_lpi_mode = 16, -	core_irq_tx_path_exit_lpi_mode = 32, -	core_irq_rx_path_in_lpi_mode = 64, -	core_irq_rx_path_exit_lpi_mode = 128, +#define	CORE_IRQ_TX_PATH_IN_LPI_MODE	(1 << 1) +#define	CORE_IRQ_TX_PATH_EXIT_LPI_MODE	(1 << 2) +#define	CORE_IRQ_RX_PATH_IN_LPI_MODE	(1 << 3) +#define	CORE_IRQ_RX_PATH_EXIT_LPI_MODE	(1 << 4) + +#define	CORE_PCS_ANE_COMPLETE		(1 << 5) +#define	CORE_PCS_LINK_STATUS		(1 << 6) +#define	CORE_RGMII_IRQ			(1 << 7) + +struct rgmii_adv { +	unsigned int pause; +	unsigned int duplex; +	unsigned int lp_pause; +	unsigned int lp_duplex;  }; +#define STMMAC_PCS_PAUSE	1 +#define STMMAC_PCS_ASYM_PAUSE	2 +  /* DMA HW capabilities */  struct dma_features {  	unsigned int mbps_10_100; @@ -255,23 +299,26 @@ struct dma_features {  #define STMMAC_DEFAULT_LIT_LS_TIMER	0x3E8  #define STMMAC_DEFAULT_TWT_LS_TIMER	0x0 +#define STMMAC_CHAIN_MODE	0x1 +#define STMMAC_RING_MODE	0x2 +  struct stmmac_desc_ops {  	/* DMA RX descriptor ring initialization */ -	void (*init_rx_desc) (struct dma_desc *p, unsigned int ring_size, -			      int disable_rx_ic); +	void (*init_rx_desc) (struct dma_desc *p, int disable_rx_ic, int mode, +			      int end);  	/* DMA TX descriptor ring initialization */ -	void (*init_tx_desc) (struct dma_desc *p, unsigned int ring_size); +	void (*init_tx_desc) (struct dma_desc *p, int mode, int end);  	/* Invoked by the xmit function to prepare the tx descriptor */  	void (*prepare_tx_desc) (struct dma_desc *p, int is_fs, int len, -				 int csum_flag); +				 int csum_flag, int mode);  	/* Set/get the owner of the descriptor */  	void (*set_tx_owner) (struct dma_desc *p);  	int (*get_tx_owner) (struct dma_desc *p);  	/* Invoked by the xmit function to close the tx descriptor */  	void (*close_tx_desc) (struct dma_desc *p);  	/* Clean the tx descriptor as soon as the tx irq is received */ -	void (*release_tx_desc) (struct dma_desc *p); +	void (*release_tx_desc) (struct dma_desc *p, int mode);  	/* Clear interrupt on tx frame completion. When this bit is  	 * set an interrupt happens as soon as the frame is transmitted */  	void (*clear_tx_ic) (struct dma_desc *p); @@ -290,12 +337,22 @@ 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); +	void (*rx_extended_status) (void *data, struct stmmac_extra_stats *x, +				    struct dma_extended_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 timestamp value */ +	u64 (*get_timestamp) (void *desc, u32 ats); +	/* get rx timestamp status */ +	int (*get_rx_timestamp_status) (void *desc, u32 ats);  };  struct stmmac_dma_ops {  	/* DMA core initialization */  	int (*init) (void __iomem *ioaddr, int pbl, int fb, int mb, -		     int burst_len, u32 dma_tx, u32 dma_rx); +		     int burst_len, u32 dma_tx, u32 dma_rx, int atds);  	/* Dump DMA registers */  	void (*dump_regs) (void __iomem *ioaddr);  	/* Set tx/rx threshold in the csr6 register @@ -327,7 +384,8 @@ struct stmmac_ops {  	/* Dump MAC registers */  	void (*dump_regs) (void __iomem *ioaddr);  	/* Handle extra events on specific interrupts hw dependent */ -	int (*host_irq_status) (void __iomem *ioaddr); +	int (*host_irq_status) (void __iomem *ioaddr, +				struct stmmac_extra_stats *x);  	/* Multicast filter setting */  	void (*set_filter) (struct net_device *dev, int id);  	/* Flow control setting */ @@ -344,6 +402,18 @@ struct stmmac_ops {  	void (*reset_eee_mode) (void __iomem *ioaddr);  	void (*set_eee_timer) (void __iomem *ioaddr, int ls, int tw);  	void (*set_eee_pls) (void __iomem *ioaddr, int link); +	void (*ctrl_ane) (void __iomem *ioaddr, bool restart); +	void (*get_adv) (void __iomem *ioaddr, struct rgmii_adv *adv); +}; + +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); +	int (*adjust_systime)(void __iomem *ioaddr, u32 sec, u32 nsec, +			      int add_sub); +	u64 (*get_systime)(void __iomem *ioaddr);  };  struct mac_link { @@ -360,19 +430,28 @@ 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 (*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 (*refill_desc3) (void *priv, struct dma_desc *p); +	void (*init_desc3) (struct dma_desc *p); +	void (*clean_desc3) (void *priv, struct dma_desc *p);  	int (*set_16kib_bfsize) (int mtu);  }; +struct stmmac_chain_mode_ops { +	void (*init) (void *des, dma_addr_t phy_addr, unsigned int size, +		      unsigned int extend_desc); +	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) (void *priv, struct dma_desc *p); +	void (*clean_desc3) (void *priv, struct dma_desc *p); +}; +  struct mac_device_info {  	const struct stmmac_ops		*mac;  	const struct stmmac_desc_ops	*desc;  	const struct stmmac_dma_ops	*dma;  	const struct stmmac_ring_mode_ops	*ring; +	const struct stmmac_chain_mode_ops	*chain; +	const struct stmmac_hwtimestamp *ptp;  	struct mii_regs mii;	/* MII register Addresses */  	struct mac_link link;  	unsigned int synopsys_uid; @@ -390,5 +469,6 @@ 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; +extern const struct stmmac_chain_mode_ops chain_mode_ops;  #endif /* __COMMON_H__ */ diff --git a/drivers/net/ethernet/stmicro/stmmac/descs.h b/drivers/net/ethernet/stmicro/stmmac/descs.h index 223adf95fd0..2eca0c03303 100644 --- a/drivers/net/ethernet/stmicro/stmmac/descs.h +++ b/drivers/net/ethernet/stmicro/stmmac/descs.h @@ -24,6 +24,7 @@  #ifndef __DESCS_H__  #define __DESCS_H__ +/* Basic descriptor structure for normal and alternate descriptors */  struct dma_desc {  	/* Receive descriptor */  	union { @@ -60,7 +61,7 @@ struct dma_desc {  		} rx;  		struct {  			/* RDES0 */ -			u32 payload_csum_error:1; +			u32 rx_mac_addr:1;  			u32 crc_error:1;  			u32 dribbling:1;  			u32 error_gmii:1; @@ -162,13 +163,57 @@ struct dma_desc {  	unsigned int des3;  }; +/* Extended descriptor structure (supported by new SYNP GMAC generations) */ +struct dma_extended_desc { +	struct dma_desc basic; +	union { +		struct { +			u32 ip_payload_type:3; +			u32 ip_hdr_err:1; +			u32 ip_payload_err:1; +			u32 ip_csum_bypassed:1; +			u32 ipv4_pkt_rcvd:1; +			u32 ipv6_pkt_rcvd:1; +			u32 msg_type:4; +			u32 ptp_frame_type:1; +			u32 ptp_ver:1; +			u32 timestamp_dropped:1; +			u32 reserved:1; +			u32 av_pkt_rcvd:1; +			u32 av_tagged_pkt_rcvd:1; +			u32 vlan_tag_priority_val:3; +			u32 reserved3:3; +			u32 l3_filter_match:1; +			u32 l4_filter_match:1; +			u32 l3_l4_filter_no_match:2; +			u32 reserved4:4; +		} erx; +		struct { +			u32 reserved; +		} etx; +	} des4; +	unsigned int des5; /* Reserved */ +	unsigned int des6; /* Tx/Rx Timestamp Low */ +	unsigned int des7; /* Tx/Rx Timestamp High */ +}; +  /* Transmit checksum insertion control */  enum tdes_csum_insertion {  	cic_disabled = 0,	/* Checksum Insertion Control */  	cic_only_ip = 1,	/* Only IP header */ -	cic_no_pseudoheader = 2,	/* IP header but pseudoheader -					 * is not calculated */ +	/* IP header but pseudoheader is not calculated */ +	cic_no_pseudoheader = 2,  	cic_full = 3,		/* IP header and pseudoheader */  }; +/* Extended RDES4 definitions */ +#define RDES_EXT_NO_PTP			0 +#define RDES_EXT_SYNC			0x1 +#define RDES_EXT_FOLLOW_UP		0x2 +#define RDES_EXT_DELAY_REQ		0x3 +#define RDES_EXT_DELAY_RESP		0x4 +#define RDES_EXT_PDELAY_REQ		0x5 +#define RDES_EXT_PDELAY_RESP		0x6 +#define RDES_EXT_PDELAY_FOLLOW_UP	0x7 +  #endif /* __DESCS_H__ */ diff --git a/drivers/net/ethernet/stmicro/stmmac/descs_com.h b/drivers/net/ethernet/stmicro/stmmac/descs_com.h index 7ee9499a6e3..20f83fc9cf1 100644 --- a/drivers/net/ethernet/stmicro/stmmac/descs_com.h +++ b/drivers/net/ethernet/stmicro/stmmac/descs_com.h @@ -30,26 +30,28 @@  #ifndef __DESC_COM_H__  #define __DESC_COM_H__ -#if defined(CONFIG_STMMAC_RING) -static inline void ehn_desc_rx_set_on_ring_chain(struct dma_desc *p, int end) +/* Specific functions used for Ring mode */ + +/* Enhanced descriptors */ +static inline void ehn_desc_rx_set_on_ring(struct dma_desc *p, int end)  {  	p->des01.erx.buffer2_size = BUF_SIZE_8KiB - 1;  	if (end)  		p->des01.erx.end_ring = 1;  } -static inline void ehn_desc_tx_set_on_ring_chain(struct dma_desc *p, int end) +static inline void ehn_desc_tx_set_on_ring(struct dma_desc *p, int end)  {  	if (end)  		p->des01.etx.end_ring = 1;  } -static inline void enh_desc_end_tx_desc(struct dma_desc *p, int ter) +static inline void enh_desc_end_tx_desc_on_ring(struct dma_desc *p, int ter)  {  	p->des01.etx.end_ring = ter;  } -static inline void enh_set_tx_desc_len(struct dma_desc *p, int len) +static inline void enh_set_tx_desc_len_on_ring(struct dma_desc *p, int len)  {  	if (unlikely(len > BUF_SIZE_4KiB)) {  		p->des01.etx.buffer1_size = BUF_SIZE_4KiB; @@ -58,25 +60,26 @@ static inline void enh_set_tx_desc_len(struct dma_desc *p, int len)  		p->des01.etx.buffer1_size = len;  } -static inline void ndesc_rx_set_on_ring_chain(struct dma_desc *p, int end) +/* Normal descriptors */ +static inline void ndesc_rx_set_on_ring(struct dma_desc *p, int end)  {  	p->des01.rx.buffer2_size = BUF_SIZE_2KiB - 1;  	if (end)  		p->des01.rx.end_ring = 1;  } -static inline void ndesc_tx_set_on_ring_chain(struct dma_desc *p, int end) +static inline void ndesc_tx_set_on_ring(struct dma_desc *p, int end)  {  	if (end)  		p->des01.tx.end_ring = 1;  } -static inline void ndesc_end_tx_desc(struct dma_desc *p, int ter) +static inline void ndesc_end_tx_desc_on_ring(struct dma_desc *p, int ter)  {  	p->des01.tx.end_ring = ter;  } -static inline void norm_set_tx_desc_len(struct dma_desc *p, int len) +static inline void norm_set_tx_desc_len_on_ring(struct dma_desc *p, int len)  {  	if (unlikely(len > BUF_SIZE_2KiB)) {  		p->des01.etx.buffer1_size = BUF_SIZE_2KiB - 1; @@ -85,47 +88,48 @@ static inline void norm_set_tx_desc_len(struct dma_desc *p, int len)  		p->des01.tx.buffer1_size = len;  } -#else +/* Specific functions used for Chain mode */ -static inline void ehn_desc_rx_set_on_ring_chain(struct dma_desc *p, int end) +/* Enhanced descriptors */ +static inline void ehn_desc_rx_set_on_chain(struct dma_desc *p, int end)  {  	p->des01.erx.second_address_chained = 1;  } -static inline void ehn_desc_tx_set_on_ring_chain(struct dma_desc *p, int end) +static inline void ehn_desc_tx_set_on_chain(struct dma_desc *p, int end)  {  	p->des01.etx.second_address_chained = 1;  } -static inline void enh_desc_end_tx_desc(struct dma_desc *p, int ter) +static inline void enh_desc_end_tx_desc_on_chain(struct dma_desc *p, int ter)  {  	p->des01.etx.second_address_chained = 1;  } -static inline void enh_set_tx_desc_len(struct dma_desc *p, int len) +static inline void enh_set_tx_desc_len_on_chain(struct dma_desc *p, int len)  {  	p->des01.etx.buffer1_size = len;  } -static inline void ndesc_rx_set_on_ring_chain(struct dma_desc *p, int end) +/* Normal descriptors */ +static inline void ndesc_rx_set_on_chain(struct dma_desc *p, int end)  {  	p->des01.rx.second_address_chained = 1;  } -static inline void ndesc_tx_set_on_ring_chain(struct dma_desc *p, int ring_size) +static inline void ndesc_tx_set_on_chain(struct dma_desc *p, int +						 ring_size)  {  	p->des01.tx.second_address_chained = 1;  } -static inline void ndesc_end_tx_desc(struct dma_desc *p, int ter) +static inline void ndesc_end_tx_desc_on_chain(struct dma_desc *p, int ter)  {  	p->des01.tx.second_address_chained = 1;  } -static inline void norm_set_tx_desc_len(struct dma_desc *p, int len) +static inline void norm_set_tx_desc_len_on_chain(struct dma_desc *p, int len)  {  	p->des01.tx.buffer1_size = len;  } -#endif -  #endif /* __DESC_COM_H__ */ diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac1000.h b/drivers/net/ethernet/stmicro/stmmac/dwmac1000.h index 7ad56afd632..57f4e8f607e 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac1000.h +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac1000.h @@ -89,13 +89,46 @@ enum power_event {  				(reg * 8))  #define GMAC_MAX_PERFECT_ADDRESSES	32 +/* PCS registers (AN/TBI/SGMII/RGMII) offset */  #define GMAC_AN_CTRL	0x000000c0	/* AN control */  #define GMAC_AN_STATUS	0x000000c4	/* AN status */  #define GMAC_ANE_ADV	0x000000c8	/* Auto-Neg. Advertisement */ -#define GMAC_ANE_LINK	0x000000cc	/* Auto-Neg. link partener ability */ +#define GMAC_ANE_LPA	0x000000cc	/* Auto-Neg. link partener ability */  #define GMAC_ANE_EXP	0x000000d0	/* ANE expansion */  #define GMAC_TBI	0x000000d4	/* TBI extend status */ -#define GMAC_GMII_STATUS 0x000000d8	/* S/R-GMII status */ +#define GMAC_S_R_GMII	0x000000d8	/* SGMII RGMII status */ + +/* AN Configuration defines */ +#define GMAC_AN_CTRL_RAN	0x00000200 /* Restart Auto-Negotiation */ +#define GMAC_AN_CTRL_ANE	0x00001000 /* Auto-Negotiation Enable */ +#define GMAC_AN_CTRL_ELE	0x00004000 /* External Loopback Enable */ +#define GMAC_AN_CTRL_ECD	0x00010000 /* Enable Comma Detect */ +#define GMAC_AN_CTRL_LR	0x00020000 /* Lock to Reference */ +#define GMAC_AN_CTRL_SGMRAL	0x00040000 /* SGMII RAL Control */ + +/* AN Status defines */ +#define GMAC_AN_STATUS_LS	0x00000004 /* Link Status 0:down 1:up */ +#define GMAC_AN_STATUS_ANA	0x00000008 /* Auto-Negotiation Ability */ +#define GMAC_AN_STATUS_ANC	0x00000020 /* Auto-Negotiation Complete */ +#define GMAC_AN_STATUS_ES	0x00000100 /* Extended Status */ + +/* Register 54 (SGMII/RGMII status register) */ +#define GMAC_S_R_GMII_LINK		0x8 +#define GMAC_S_R_GMII_SPEED		0x5 +#define GMAC_S_R_GMII_SPEED_SHIFT	0x1 +#define GMAC_S_R_GMII_MODE		0x1 +#define GMAC_S_R_GMII_SPEED_125		2 +#define GMAC_S_R_GMII_SPEED_25		1 + +/* Common ADV and LPA defines */ +#define GMAC_ANE_FD		(1 << 5) +#define GMAC_ANE_HD		(1 << 6) +#define GMAC_ANE_PSE		(3 << 7) +#define GMAC_ANE_PSE_SHIFT	7 + + /* GMAC Configuration defines */ +#define GMAC_CONTROL_TC	0x01000000 /* Transmit Conf. in RGMII/SGMII */ +#define GMAC_CONTROL_WD	0x00800000 /* Disable Watchdog on receive */  /* GMAC Configuration defines */  #define GMAC_CONTROL_TC	0x01000000	/* Transmit Conf. in RGMII/SGMII */ @@ -155,6 +188,7 @@ enum inter_frame_gap {  /* Programmable burst length (passed thorugh platform)*/  #define DMA_BUS_MODE_PBL_MASK	0x00003f00	/* Programmable Burst Len */  #define DMA_BUS_MODE_PBL_SHIFT	8 +#define DMA_BUS_MODE_ATDS	0x00000080	/* Alternate Descriptor Size */  enum rx_tx_priority_ratio {  	double_ratio = 0x00004000,	/*2:1 */ @@ -230,5 +264,7 @@ enum rtc_control {  #define GMAC_MMC_TX_INTR   0x108  #define GMAC_MMC_RX_CSUM_OFFLOAD   0x208 + +  extern const struct stmmac_dma_ops dwmac1000_dma_ops;  #endif /* __DWMAC1000_H__ */ diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c index bfe02260549..29138da19db 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c @@ -28,6 +28,7 @@  #include <linux/crc32.h>  #include <linux/slab.h> +#include <linux/ethtool.h>  #include <asm/io.h>  #include "dwmac1000.h" @@ -193,59 +194,91 @@ static void dwmac1000_pmt(void __iomem *ioaddr, unsigned long mode)  	writel(pmt, ioaddr + GMAC_PMT);  } - -static int dwmac1000_irq_status(void __iomem *ioaddr) +static int dwmac1000_irq_status(void __iomem *ioaddr, +				struct stmmac_extra_stats *x)  {  	u32 intr_status = readl(ioaddr + GMAC_INT_STATUS); -	int status = 0; +	int ret = 0;  	/* Not used events (e.g. MMC interrupts) are not handled. */  	if ((intr_status & mmc_tx_irq)) {  		CHIP_DBG(KERN_INFO "GMAC: MMC tx interrupt: 0x%08x\n",  		    readl(ioaddr + GMAC_MMC_TX_INTR)); -		status |= core_mmc_tx_irq; +		x->mmc_tx_irq_n++;  	}  	if (unlikely(intr_status & mmc_rx_irq)) {  		CHIP_DBG(KERN_INFO "GMAC: MMC rx interrupt: 0x%08x\n",  		    readl(ioaddr + GMAC_MMC_RX_INTR)); -		status |= core_mmc_rx_irq; +		x->mmc_rx_irq_n++;  	}  	if (unlikely(intr_status & mmc_rx_csum_offload_irq)) {  		CHIP_DBG(KERN_INFO "GMAC: MMC rx csum offload: 0x%08x\n",  		    readl(ioaddr + GMAC_MMC_RX_CSUM_OFFLOAD)); -		status |= core_mmc_rx_csum_offload_irq; +		x->mmc_rx_csum_offload_irq_n++;  	}  	if (unlikely(intr_status & pmt_irq)) {  		CHIP_DBG(KERN_INFO "GMAC: received Magic frame\n");  		/* clear the PMT bits 5 and 6 by reading the PMT  		 * status register. */  		readl(ioaddr + GMAC_PMT); -		status |= core_irq_receive_pmt_irq; +		x->irq_receive_pmt_irq_n++;  	}  	/* MAC trx/rx EEE LPI entry/exit interrupts */  	if (intr_status & lpiis_irq) {  		/* Clean LPI interrupt by reading the Reg 12 */ -		u32 lpi_status = readl(ioaddr + LPI_CTRL_STATUS); +		ret = readl(ioaddr + LPI_CTRL_STATUS); -		if (lpi_status & LPI_CTRL_STATUS_TLPIEN) { +		if (ret & LPI_CTRL_STATUS_TLPIEN) {  			CHIP_DBG(KERN_INFO "GMAC TX entered in LPI\n"); -			status |= core_irq_tx_path_in_lpi_mode; +			x->irq_tx_path_in_lpi_mode_n++;  		} -		if (lpi_status & LPI_CTRL_STATUS_TLPIEX) { +		if (ret & LPI_CTRL_STATUS_TLPIEX) {  			CHIP_DBG(KERN_INFO "GMAC TX exit from LPI\n"); -			status |= core_irq_tx_path_exit_lpi_mode; +			x->irq_tx_path_exit_lpi_mode_n++;  		} -		if (lpi_status & LPI_CTRL_STATUS_RLPIEN) { +		if (ret & LPI_CTRL_STATUS_RLPIEN) {  			CHIP_DBG(KERN_INFO "GMAC RX entered in LPI\n"); -			status |= core_irq_rx_path_in_lpi_mode; +			x->irq_rx_path_in_lpi_mode_n++;  		} -		if (lpi_status & LPI_CTRL_STATUS_RLPIEX) { +		if (ret & LPI_CTRL_STATUS_RLPIEX) {  			CHIP_DBG(KERN_INFO "GMAC RX exit from LPI\n"); -			status |= core_irq_rx_path_exit_lpi_mode; +			x->irq_rx_path_exit_lpi_mode_n++; +		} +	} + +	if ((intr_status & pcs_ane_irq) || (intr_status & pcs_link_irq)) { +		CHIP_DBG(KERN_INFO "GMAC PCS ANE IRQ\n"); +		readl(ioaddr + GMAC_AN_STATUS); +		x->irq_pcs_ane_n++; +	} +	if (intr_status & rgmii_irq) { +		u32 status  = readl(ioaddr + GMAC_S_R_GMII); +		CHIP_DBG(KERN_INFO "GMAC RGMII/SGMII interrupt\n"); +		x->irq_rgmii_n++; + +		/* Save and dump the link status. */ +		if (status & GMAC_S_R_GMII_LINK) { +			int speed_value = (status & GMAC_S_R_GMII_SPEED) >> +					  GMAC_S_R_GMII_SPEED_SHIFT; +			x->pcs_duplex = (status & GMAC_S_R_GMII_MODE); + +			if (speed_value == GMAC_S_R_GMII_SPEED_125) +				x->pcs_speed = SPEED_1000; +			else if (speed_value == GMAC_S_R_GMII_SPEED_25) +				x->pcs_speed = SPEED_100; +			else +				x->pcs_speed = SPEED_10; + +			x->pcs_link = 1; +			pr_debug("Link is Up - %d/%s\n", (int) x->pcs_speed, +				 x->pcs_duplex ? "Full" : "Half"); +		} else { +			x->pcs_link = 0; +			pr_debug("Link is Down\n");  		}  	} -	return status; +	return ret;  }  static void  dwmac1000_set_eee_mode(void __iomem *ioaddr) @@ -297,6 +330,41 @@ static void  dwmac1000_set_eee_timer(void __iomem *ioaddr, int ls, int tw)  	writel(value, ioaddr + LPI_TIMER_CTRL);  } +static void dwmac1000_ctrl_ane(void __iomem *ioaddr, bool restart) +{ +	u32 value; + +	value = readl(ioaddr + GMAC_AN_CTRL); +	/* auto negotiation enable and External Loopback enable */ +	value = GMAC_AN_CTRL_ANE | GMAC_AN_CTRL_ELE; + +	if (restart) +		value |= GMAC_AN_CTRL_RAN; + +	writel(value, ioaddr + GMAC_AN_CTRL); +} + +static void dwmac1000_get_adv(void __iomem *ioaddr, struct rgmii_adv *adv) +{ +	u32 value = readl(ioaddr + GMAC_ANE_ADV); + +	if (value & GMAC_ANE_FD) +		adv->duplex = DUPLEX_FULL; +	if (value & GMAC_ANE_HD) +		adv->duplex |= DUPLEX_HALF; + +	adv->pause = (value & GMAC_ANE_PSE) >> GMAC_ANE_PSE_SHIFT; + +	value = readl(ioaddr + GMAC_ANE_LPA); + +	if (value & GMAC_ANE_FD) +		adv->lp_duplex = DUPLEX_FULL; +	if (value & GMAC_ANE_HD) +		adv->lp_duplex = DUPLEX_HALF; + +	adv->lp_pause = (value & GMAC_ANE_PSE) >> GMAC_ANE_PSE_SHIFT; +} +  static const struct stmmac_ops dwmac1000_ops = {  	.core_init = dwmac1000_core_init,  	.rx_ipc = dwmac1000_rx_ipc_enable, @@ -311,6 +379,8 @@ static const struct stmmac_ops dwmac1000_ops = {  	.reset_eee_mode =  dwmac1000_reset_eee_mode,  	.set_eee_timer =  dwmac1000_set_eee_timer,  	.set_eee_pls =  dwmac1000_set_eee_pls, +	.ctrl_ane = dwmac1000_ctrl_ane, +	.get_adv = dwmac1000_get_adv,  };  struct mac_device_info *dwmac1000_setup(void __iomem *ioaddr) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c index bf83c03bfd0..f1c4b2c00aa 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c @@ -30,8 +30,8 @@  #include "dwmac1000.h"  #include "dwmac_dma.h" -static int dwmac1000_dma_init(void __iomem *ioaddr, int pbl, int fb, -			      int mb, int burst_len, u32 dma_tx, u32 dma_rx) +static int dwmac1000_dma_init(void __iomem *ioaddr, int pbl, int fb, int mb, +			      int burst_len, u32 dma_tx, u32 dma_rx, int atds)  {  	u32 value = readl(ioaddr + DMA_BUS_MODE);  	int limit; @@ -73,6 +73,10 @@ static int dwmac1000_dma_init(void __iomem *ioaddr, int pbl, int fb,  #ifdef CONFIG_STMMAC_DA  	value |= DMA_BUS_MODE_DA;	/* Rx has priority over tx */  #endif + +	if (atds) +		value |= DMA_BUS_MODE_ATDS; +  	writel(value, ioaddr + DMA_BUS_MODE);  	/* In case of GMAC AXI configuration, program the DMA_AXI_BUS_MODE diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac100_core.c b/drivers/net/ethernet/stmicro/stmmac/dwmac100_core.c index f83210e7c22..cb86a58c1c5 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac100_core.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac100_core.c @@ -72,7 +72,8 @@ static int dwmac100_rx_ipc_enable(void __iomem *ioaddr)  	return 0;  } -static int dwmac100_irq_status(void __iomem *ioaddr) +static int dwmac100_irq_status(void __iomem *ioaddr, +			       struct stmmac_extra_stats *x)  {  	return 0;  } diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac100_dma.c b/drivers/net/ethernet/stmicro/stmmac/dwmac100_dma.c index c2b4d55a79b..e979a8b2ae4 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac100_dma.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac100_dma.c @@ -32,8 +32,8 @@  #include "dwmac100.h"  #include "dwmac_dma.h" -static int dwmac100_dma_init(void __iomem *ioaddr, int pbl, int fb, -			     int mb, int burst_len, u32 dma_tx, u32 dma_rx) +static int dwmac100_dma_init(void __iomem *ioaddr, int pbl, int fb, int mb, +			     int burst_len, u32 dma_tx, u32 dma_rx, int atds)  {  	u32 value = readl(ioaddr + DMA_BUS_MODE);  	int limit; diff --git a/drivers/net/ethernet/stmicro/stmmac/enh_desc.c b/drivers/net/ethernet/stmicro/stmmac/enh_desc.c index 2fc8ef95f97..0fbc8fafa70 100644 --- a/drivers/net/ethernet/stmicro/stmmac/enh_desc.c +++ b/drivers/net/ethernet/stmicro/stmmac/enh_desc.c @@ -150,6 +150,57 @@ static int enh_desc_coe_rdes0(int ipc_err, int type, int payload_err)  	return ret;  } +static void enh_desc_get_ext_status(void *data, struct stmmac_extra_stats *x, +				    struct dma_extended_desc *p) +{ +	if (unlikely(p->basic.des01.erx.rx_mac_addr)) { +		if (p->des4.erx.ip_hdr_err) +			x->ip_hdr_err++; +		if (p->des4.erx.ip_payload_err) +			x->ip_payload_err++; +		if (p->des4.erx.ip_csum_bypassed) +			x->ip_csum_bypassed++; +		if (p->des4.erx.ipv4_pkt_rcvd) +			x->ipv4_pkt_rcvd++; +		if (p->des4.erx.ipv6_pkt_rcvd) +			x->ipv6_pkt_rcvd++; +		if (p->des4.erx.msg_type == RDES_EXT_SYNC) +			x->rx_msg_type_sync++; +		else if (p->des4.erx.msg_type == RDES_EXT_FOLLOW_UP) +			x->rx_msg_type_follow_up++; +		else if (p->des4.erx.msg_type == RDES_EXT_DELAY_REQ) +			x->rx_msg_type_delay_req++; +		else if (p->des4.erx.msg_type == RDES_EXT_DELAY_RESP) +			x->rx_msg_type_delay_resp++; +		else if (p->des4.erx.msg_type == RDES_EXT_DELAY_REQ) +			x->rx_msg_type_pdelay_req++; +		else if (p->des4.erx.msg_type == RDES_EXT_PDELAY_RESP) +			x->rx_msg_type_pdelay_resp++; +		else if (p->des4.erx.msg_type == RDES_EXT_PDELAY_FOLLOW_UP) +			x->rx_msg_type_pdelay_follow_up++; +		else +			x->rx_msg_type_ext_no_ptp++; +		if (p->des4.erx.ptp_frame_type) +			x->ptp_frame_type++; +		if (p->des4.erx.ptp_ver) +			x->ptp_ver++; +		if (p->des4.erx.timestamp_dropped) +			x->timestamp_dropped++; +		if (p->des4.erx.av_pkt_rcvd) +			x->av_pkt_rcvd++; +		if (p->des4.erx.av_tagged_pkt_rcvd) +			x->av_tagged_pkt_rcvd++; +		if (p->des4.erx.vlan_tag_priority_val) +			x->vlan_tag_priority_val++; +		if (p->des4.erx.l3_filter_match) +			x->l3_filter_match++; +		if (p->des4.erx.l4_filter_match) +			x->l4_filter_match++; +		if (p->des4.erx.l3_l4_filter_no_match) +			x->l3_l4_filter_no_match++; +	} +} +  static int enh_desc_get_rx_status(void *data, struct stmmac_extra_stats *x,  				  struct dma_desc *p)  { @@ -198,7 +249,7 @@ static int enh_desc_get_rx_status(void *data, struct stmmac_extra_stats *x,  	 * At any rate, we need to understand if the CSUM hw computation is ok  	 * and report this info to the upper layers. */  	ret = enh_desc_coe_rdes0(p->des01.erx.ipc_csum_error, -		p->des01.erx.frame_type, p->des01.erx.payload_csum_error); +		p->des01.erx.frame_type, p->des01.erx.rx_mac_addr);  	if (unlikely(p->des01.erx.dribbling)) {  		CHIP_DBG(KERN_ERR "GMAC RX: dribbling error\n"); @@ -225,34 +276,32 @@ static int enh_desc_get_rx_status(void *data, struct stmmac_extra_stats *x,  		x->rx_vlan++;  	}  #endif +  	return ret;  } -static void enh_desc_init_rx_desc(struct dma_desc *p, unsigned int ring_size, -				  int disable_rx_ic) +static void enh_desc_init_rx_desc(struct dma_desc *p, int disable_rx_ic, +				  int mode, int end)  { -	int i; -	for (i = 0; i < ring_size; i++) { -		p->des01.erx.own = 1; -		p->des01.erx.buffer1_size = BUF_SIZE_8KiB - 1; +	p->des01.erx.own = 1; +	p->des01.erx.buffer1_size = BUF_SIZE_8KiB - 1; -		ehn_desc_rx_set_on_ring_chain(p, (i == ring_size - 1)); +	if (mode == STMMAC_CHAIN_MODE) +		ehn_desc_rx_set_on_chain(p, end); +	else +		ehn_desc_rx_set_on_ring(p, end); -		if (disable_rx_ic) -			p->des01.erx.disable_ic = 1; -		p++; -	} +	if (disable_rx_ic) +		p->des01.erx.disable_ic = 1;  } -static void enh_desc_init_tx_desc(struct dma_desc *p, unsigned int ring_size) +static void enh_desc_init_tx_desc(struct dma_desc *p, int mode, int end)  { -	int i; - -	for (i = 0; i < ring_size; i++) { -		p->des01.etx.own = 0; -		ehn_desc_tx_set_on_ring_chain(p, (i == ring_size - 1)); -		p++; -	} +	p->des01.etx.own = 0; +	if (mode == STMMAC_CHAIN_MODE) +		ehn_desc_tx_set_on_chain(p, end); +	else +		ehn_desc_tx_set_on_ring(p, end);  }  static int enh_desc_get_tx_owner(struct dma_desc *p) @@ -280,20 +329,26 @@ static int enh_desc_get_tx_ls(struct dma_desc *p)  	return p->des01.etx.last_segment;  } -static void enh_desc_release_tx_desc(struct dma_desc *p) +static void enh_desc_release_tx_desc(struct dma_desc *p, int mode)  {  	int ter = p->des01.etx.end_ring;  	memset(p, 0, offsetof(struct dma_desc, des2)); -	enh_desc_end_tx_desc(p, ter); +	if (mode == STMMAC_CHAIN_MODE) +		enh_desc_end_tx_desc_on_chain(p, ter); +	else +		enh_desc_end_tx_desc_on_ring(p, ter);  }  static void enh_desc_prepare_tx_desc(struct dma_desc *p, int is_fs, int len, -				     int csum_flag) +				     int csum_flag, int mode)  {  	p->des01.etx.first_segment = is_fs; -	enh_set_tx_desc_len(p, len); +	if (mode == STMMAC_CHAIN_MODE) +		enh_set_tx_desc_len_on_chain(p, len); +	else +		enh_set_tx_desc_len_on_ring(p, len);  	if (likely(csum_flag))  		p->des01.etx.checksum_insertion = cic_full; @@ -323,6 +378,49 @@ 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 u64 enh_desc_get_timestamp(void *desc, u32 ats) +{ +	u64 ns; + +	if (ats) { +		struct dma_extended_desc *p = (struct dma_extended_desc *)desc; +		ns = p->des6; +		/* convert high/sec time stamp value to nanosecond */ +		ns += p->des7 * 1000000000ULL; +	} else { +		struct dma_desc *p = (struct dma_desc *)desc; +		ns = p->des2; +		ns += p->des3 * 1000000000ULL; +	} + +	return ns; +} + +static int enh_desc_get_rx_timestamp_status(void *desc, u32 ats) +{ +	if (ats) { +		struct dma_extended_desc *p = (struct dma_extended_desc *)desc; +		return p->basic.des01.erx.ipc_csum_error; +	} else { +		struct dma_desc *p = (struct dma_desc *)desc; +		if ((p->des2 == 0xffffffff) && (p->des3 == 0xffffffff)) +			/* timestamp is corrupted, 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 +437,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, +	.rx_extended_status = enh_desc_get_ext_status, +	.enable_tx_timestamp = enh_desc_enable_tx_timestamp, +	.get_tx_timestamp_status = enh_desc_get_tx_timestamp_status, +	.get_timestamp = enh_desc_get_timestamp, +	.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 68962c549a2..7cbcea348c3 100644 --- a/drivers/net/ethernet/stmicro/stmmac/norm_desc.c +++ b/drivers/net/ethernet/stmicro/stmmac/norm_desc.c @@ -122,30 +122,28 @@ static int ndesc_get_rx_status(void *data, struct stmmac_extra_stats *x,  	return ret;  } -static void ndesc_init_rx_desc(struct dma_desc *p, unsigned int ring_size, -			       int disable_rx_ic) +static void ndesc_init_rx_desc(struct dma_desc *p, int disable_rx_ic, int mode, +			       int end)  { -	int i; -	for (i = 0; i < ring_size; i++) { -		p->des01.rx.own = 1; -		p->des01.rx.buffer1_size = BUF_SIZE_2KiB - 1; +	p->des01.rx.own = 1; +	p->des01.rx.buffer1_size = BUF_SIZE_2KiB - 1; -		ndesc_rx_set_on_ring_chain(p, (i == ring_size - 1)); +	if (mode == STMMAC_CHAIN_MODE) +		ndesc_rx_set_on_chain(p, end); +	else +		ndesc_rx_set_on_ring(p, end); -		if (disable_rx_ic) -			p->des01.rx.disable_ic = 1; -		p++; -	} +	if (disable_rx_ic) +		p->des01.rx.disable_ic = 1;  } -static void ndesc_init_tx_desc(struct dma_desc *p, unsigned int ring_size) +static void ndesc_init_tx_desc(struct dma_desc *p, int mode, int end)  { -	int i; -	for (i = 0; i < ring_size; i++) { -		p->des01.tx.own = 0; -		ndesc_tx_set_on_ring_chain(p, (i == (ring_size - 1))); -		p++; -	} +	p->des01.tx.own = 0; +	if (mode == STMMAC_CHAIN_MODE) +		ndesc_tx_set_on_chain(p, end); +	else +		ndesc_tx_set_on_ring(p, end);  }  static int ndesc_get_tx_owner(struct dma_desc *p) @@ -173,19 +171,25 @@ static int ndesc_get_tx_ls(struct dma_desc *p)  	return p->des01.tx.last_segment;  } -static void ndesc_release_tx_desc(struct dma_desc *p) +static void ndesc_release_tx_desc(struct dma_desc *p, int mode)  {  	int ter = p->des01.tx.end_ring;  	memset(p, 0, offsetof(struct dma_desc, des2)); -	ndesc_end_tx_desc(p, ter); +	if (mode == STMMAC_CHAIN_MODE) +		ndesc_end_tx_desc_on_chain(p, ter); +	else +		ndesc_end_tx_desc_on_ring(p, ter);  }  static void ndesc_prepare_tx_desc(struct dma_desc *p, int is_fs, int len, -				  int csum_flag) +				  int csum_flag, int mode)  {  	p->des01.tx.first_segment = is_fs; -	norm_set_tx_desc_len(p, len); +	if (mode == STMMAC_CHAIN_MODE) +		norm_set_tx_desc_len_on_chain(p, len); +	else +		norm_set_tx_desc_len_on_ring(p, len);  	if (likely(csum_flag))  		p->des01.tx.checksum_insertion = cic_full; @@ -215,6 +219,39 @@ 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 u64 ndesc_get_timestamp(void *desc, u32 ats) +{ +	struct dma_desc *p = (struct dma_desc *)desc; +	u64 ns; + +	ns = p->des2; +	/* convert high/sec time stamp value to nanosecond */ +	ns += p->des3 * 1000000000ULL; + +	return ns; +} + +static int ndesc_get_rx_timestamp_status(void *desc, u32 ats) +{ +	struct dma_desc *p = (struct dma_desc *)desc; + +	if ((p->des2 == 0xffffffff) && (p->des3 == 0xffffffff)) +		/* timestamp is corrupted, 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 +268,8 @@ 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 = ndesc_get_timestamp, +	.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 4b785e10f2e..d0265a7d5a5 100644 --- a/drivers/net/ethernet/stmicro/stmmac/ring_mode.c +++ b/drivers/net/ethernet/stmicro/stmmac/ring_mode.c @@ -48,25 +48,30 @@ static unsigned int stmmac_jumbo_frm(void *p, struct sk_buff *skb, int csum)  		desc->des2 = dma_map_single(priv->device, skb->data,  					    bmax, DMA_TO_DEVICE); +		priv->tx_skbuff_dma[entry] = desc->des2;  		desc->des3 = desc->des2 + BUF_SIZE_4KiB; -		priv->hw->desc->prepare_tx_desc(desc, 1, bmax, -						csum); +		priv->hw->desc->prepare_tx_desc(desc, 1, bmax, csum, +						STMMAC_RING_MODE);  		wmb();  		entry = (++priv->cur_tx) % txsize;  		desc = priv->dma_tx + entry;  		desc->des2 = dma_map_single(priv->device, skb->data + bmax,  					    len, DMA_TO_DEVICE); +		priv->tx_skbuff_dma[entry] = desc->des2;  		desc->des3 = desc->des2 + BUF_SIZE_4KiB; -		priv->hw->desc->prepare_tx_desc(desc, 0, len, csum); +		priv->hw->desc->prepare_tx_desc(desc, 0, len, csum, +						STMMAC_RING_MODE);  		wmb();  		priv->hw->desc->set_tx_owner(desc);  		priv->tx_skbuff[entry] = NULL;  	} else {  		desc->des2 = dma_map_single(priv->device, skb->data,  					    nopaged_len, DMA_TO_DEVICE); +		priv->tx_skbuff_dma[entry] = desc->des2;  		desc->des3 = desc->des2 + BUF_SIZE_4KiB; -		priv->hw->desc->prepare_tx_desc(desc, 1, nopaged_len, csum); +		priv->hw->desc->prepare_tx_desc(desc, 1, nopaged_len, csum, +						STMMAC_RING_MODE);  	}  	return entry; @@ -82,27 +87,23 @@ 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; -/* In ring mode we need to fill the desc3 because it is used - * as buffer */ -static void stmmac_init_desc3(int des3_as_data_buf, struct dma_desc *p) -{ -	if (unlikely(des3_as_data_buf)) -		p->des3 = p->des2 + BUF_SIZE_8KiB; +	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;  } -static void stmmac_init_dma_chain(struct dma_desc *des, dma_addr_t phy_addr, -				  unsigned int size) +/* In ring mode we need to fill the desc3 because it is used as buffer */ +static void stmmac_init_desc3(struct dma_desc *p)  { +	p->des3 = p->des2 + BUF_SIZE_8KiB;  } -static void stmmac_clean_desc3(struct dma_desc *p) +static void stmmac_clean_desc3(void *priv_ptr, struct dma_desc *p)  {  	if (unlikely(p->des3))  		p->des3 = 0; @@ -121,7 +122,6 @@ const struct stmmac_ring_mode_ops ring_mode_ops = {  	.jumbo_frm = stmmac_jumbo_frm,  	.refill_desc3 = stmmac_refill_desc3,  	.init_desc3 = stmmac_init_desc3, -	.init_dma_chain = stmmac_init_dma_chain,  	.clean_desc3 = stmmac_clean_desc3,  	.set_16kib_bfsize = stmmac_set_16kib_bfsize,  }; diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac.h b/drivers/net/ethernet/stmicro/stmmac/stmmac.h index b05df8983be..75f997b467a 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac.h +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac.h @@ -24,25 +24,29 @@  #define __STMMAC_H__  #define STMMAC_RESOURCE_NAME   "stmmaceth" -#define DRV_MODULE_VERSION	"Nov_2012" +#define DRV_MODULE_VERSION	"March_2013"  #include <linux/clk.h>  #include <linux/stmmac.h>  #include <linux/phy.h>  #include <linux/pci.h>  #include "common.h" +#include <linux/ptp_clock_kernel.h>  struct stmmac_priv {  	/* Frequently used values are kept adjacent for cache effect */ -	struct dma_desc *dma_tx ____cacheline_aligned; +	struct dma_desc *dma_tx ____cacheline_aligned;	/* Basic TX desc */ +	struct dma_extended_desc *dma_etx;	/* Extended TX descriptor */  	dma_addr_t dma_tx_phy;  	struct sk_buff **tx_skbuff; +	dma_addr_t *tx_skbuff_dma;  	unsigned int cur_tx;  	unsigned int dirty_tx;  	unsigned int dma_tx_size;  	int tx_coalesce; -	struct dma_desc *dma_rx ; +	struct dma_desc *dma_rx;		/* Basic RX descriptor */ +	struct dma_extended_desc *dma_erx;	/* Extended RX descriptor */  	unsigned int cur_rx;  	unsigned int dirty_rx;  	struct sk_buff **rx_skbuff; @@ -93,6 +97,16 @@ struct stmmac_priv {  	u32 tx_coal_timer;  	int use_riwt;  	u32 rx_riwt; +	unsigned int mode; +	int extend_desc; +	int pcs; +	int hwts_tx_en; +	int hwts_rx_en; +	unsigned int default_addend; +	u32 adv_ts; +	struct ptp_clock *ptp_clock; +	struct ptp_clock_info ptp_clock_ops; +	spinlock_t ptp_lock;  };  extern int phyaddr; @@ -102,6 +116,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; +extern const struct stmmac_hwtimestamp stmmac_ptp; +extern int stmmac_ptp_register(struct stmmac_priv *priv); +extern void stmmac_ptp_unregister(struct stmmac_priv *priv);  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_ethtool.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c index d1ac39c1b05..c5f9cb85c8e 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c @@ -27,6 +27,7 @@  #include <linux/interrupt.h>  #include <linux/mii.h>  #include <linux/phy.h> +#include <linux/net_tstamp.h>  #include <asm/io.h>  #include "stmmac.h" @@ -108,6 +109,33 @@ static const struct stmmac_stats stmmac_gstrings_stats[] = {  	STMMAC_STAT(irq_rx_path_in_lpi_mode_n),  	STMMAC_STAT(irq_rx_path_exit_lpi_mode_n),  	STMMAC_STAT(phy_eee_wakeup_error_n), +	/* Extended RDES status */ +	STMMAC_STAT(ip_hdr_err), +	STMMAC_STAT(ip_payload_err), +	STMMAC_STAT(ip_csum_bypassed), +	STMMAC_STAT(ipv4_pkt_rcvd), +	STMMAC_STAT(ipv6_pkt_rcvd), +	STMMAC_STAT(rx_msg_type_ext_no_ptp), +	STMMAC_STAT(rx_msg_type_sync), +	STMMAC_STAT(rx_msg_type_follow_up), +	STMMAC_STAT(rx_msg_type_delay_req), +	STMMAC_STAT(rx_msg_type_delay_resp), +	STMMAC_STAT(rx_msg_type_pdelay_req), +	STMMAC_STAT(rx_msg_type_pdelay_resp), +	STMMAC_STAT(rx_msg_type_pdelay_follow_up), +	STMMAC_STAT(ptp_frame_type), +	STMMAC_STAT(ptp_ver), +	STMMAC_STAT(timestamp_dropped), +	STMMAC_STAT(av_pkt_rcvd), +	STMMAC_STAT(av_tagged_pkt_rcvd), +	STMMAC_STAT(vlan_tag_priority_val), +	STMMAC_STAT(l3_filter_match), +	STMMAC_STAT(l4_filter_match), +	STMMAC_STAT(l3_l4_filter_no_match), +	/* PCS */ +	STMMAC_STAT(irq_pcs_ane_n), +	STMMAC_STAT(irq_pcs_link_n), +	STMMAC_STAT(irq_rgmii_n),  };  #define STMMAC_STATS_LEN ARRAY_SIZE(stmmac_gstrings_stats) @@ -219,6 +247,70 @@ static int stmmac_ethtool_getsettings(struct net_device *dev,  	struct stmmac_priv *priv = netdev_priv(dev);  	struct phy_device *phy = priv->phydev;  	int rc; + +	if ((priv->pcs & STMMAC_PCS_RGMII) || (priv->pcs & STMMAC_PCS_SGMII)) { +		struct rgmii_adv adv; + +		if (!priv->xstats.pcs_link) { +			ethtool_cmd_speed_set(cmd, SPEED_UNKNOWN); +			cmd->duplex = DUPLEX_UNKNOWN; +			return 0; +		} +		cmd->duplex = priv->xstats.pcs_duplex; + +		ethtool_cmd_speed_set(cmd, priv->xstats.pcs_speed); + +		/* Get and convert ADV/LP_ADV from the HW AN registers */ +		if (priv->hw->mac->get_adv) +			priv->hw->mac->get_adv(priv->ioaddr, &adv); +		else +			return -EOPNOTSUPP;	/* should never happen indeed */ + +		/* Encoding of PSE bits is defined in 802.3z, 37.2.1.4 */ + +		if (adv.pause & STMMAC_PCS_PAUSE) +			cmd->advertising |= ADVERTISED_Pause; +		if (adv.pause & STMMAC_PCS_ASYM_PAUSE) +			cmd->advertising |= ADVERTISED_Asym_Pause; +		if (adv.lp_pause & STMMAC_PCS_PAUSE) +			cmd->lp_advertising |= ADVERTISED_Pause; +		if (adv.lp_pause & STMMAC_PCS_ASYM_PAUSE) +			cmd->lp_advertising |= ADVERTISED_Asym_Pause; + +		/* Reg49[3] always set because ANE is always supported */ +		cmd->autoneg = ADVERTISED_Autoneg; +		cmd->supported |= SUPPORTED_Autoneg; +		cmd->advertising |= ADVERTISED_Autoneg; +		cmd->lp_advertising |= ADVERTISED_Autoneg; + +		if (adv.duplex) { +			cmd->supported |= (SUPPORTED_1000baseT_Full | +					   SUPPORTED_100baseT_Full | +					   SUPPORTED_10baseT_Full); +			cmd->advertising |= (ADVERTISED_1000baseT_Full | +					     ADVERTISED_100baseT_Full | +					     ADVERTISED_10baseT_Full); +		} else { +			cmd->supported |= (SUPPORTED_1000baseT_Half | +					   SUPPORTED_100baseT_Half | +					   SUPPORTED_10baseT_Half); +			cmd->advertising |= (ADVERTISED_1000baseT_Half | +					     ADVERTISED_100baseT_Half | +					     ADVERTISED_10baseT_Half); +		} +		if (adv.lp_duplex) +			cmd->lp_advertising |= (ADVERTISED_1000baseT_Full | +						ADVERTISED_100baseT_Full | +						ADVERTISED_10baseT_Full); +		else +			cmd->lp_advertising |= (ADVERTISED_1000baseT_Half | +						ADVERTISED_100baseT_Half | +						ADVERTISED_10baseT_Half); +		cmd->port = PORT_OTHER; + +		return 0; +	} +  	if (phy == NULL) {  		pr_err("%s: %s: PHY is not registered\n",  		       __func__, dev->name); @@ -243,6 +335,30 @@ static int stmmac_ethtool_setsettings(struct net_device *dev,  	struct phy_device *phy = priv->phydev;  	int rc; +	if ((priv->pcs & STMMAC_PCS_RGMII) || (priv->pcs & STMMAC_PCS_SGMII)) { +		u32 mask = ADVERTISED_Autoneg | ADVERTISED_Pause; + +		/* Only support ANE */ +		if (cmd->autoneg != AUTONEG_ENABLE) +			return -EINVAL; + +		if (cmd->autoneg == AUTONEG_ENABLE) { +			mask &= (ADVERTISED_1000baseT_Half | +			ADVERTISED_1000baseT_Full | +			ADVERTISED_100baseT_Half | +			ADVERTISED_100baseT_Full | +			ADVERTISED_10baseT_Half | +			ADVERTISED_10baseT_Full); + +			spin_lock(&priv->lock); +			if (priv->hw->mac->ctrl_ane) +				priv->hw->mac->ctrl_ane(priv->ioaddr, 1); +			spin_unlock(&priv->lock); +		} + +		return 0; +	} +  	spin_lock(&priv->lock);  	rc = phy_ethtool_sset(phy, cmd);  	spin_unlock(&priv->lock); @@ -312,6 +428,9 @@ stmmac_get_pauseparam(struct net_device *netdev,  {  	struct stmmac_priv *priv = netdev_priv(netdev); +	if (priv->pcs)	/* FIXME */ +		return; +  	spin_lock(&priv->lock);  	pause->rx_pause = 0; @@ -335,6 +454,9 @@ stmmac_set_pauseparam(struct net_device *netdev,  	int new_pause = FLOW_OFF;  	int ret = 0; +	if (priv->pcs)	/* FIXME */ +		return -EOPNOTSUPP; +  	spin_lock(&priv->lock);  	if (pause->rx_pause) @@ -604,6 +726,38 @@ static int stmmac_set_coalesce(struct net_device *dev,  	return 0;  } +static int stmmac_get_ts_info(struct net_device *dev, +			      struct ethtool_ts_info *info) +{ +	struct stmmac_priv *priv = netdev_priv(dev); + +	if ((priv->hwts_tx_en) && (priv->hwts_rx_en)) { + +		info->so_timestamping = SOF_TIMESTAMPING_TX_HARDWARE | +					SOF_TIMESTAMPING_RX_HARDWARE | +					SOF_TIMESTAMPING_RAW_HARDWARE; + +		if (priv->ptp_clock) +			info->phc_index = ptp_clock_index(priv->ptp_clock); + +		info->tx_types = (1 << HWTSTAMP_TX_OFF) | (1 << HWTSTAMP_TX_ON); + +		info->rx_filters = ((1 << HWTSTAMP_FILTER_NONE) | +				    (1 << HWTSTAMP_FILTER_PTP_V1_L4_EVENT) | +				    (1 << HWTSTAMP_FILTER_PTP_V1_L4_SYNC) | +				    (1 << HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ) | +				    (1 << HWTSTAMP_FILTER_PTP_V2_L4_EVENT) | +				    (1 << HWTSTAMP_FILTER_PTP_V2_L4_SYNC) | +				    (1 << HWTSTAMP_FILTER_PTP_V2_L4_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)); +		return 0; +	} else +		return ethtool_op_get_ts_info(dev, info); +} +  static const struct ethtool_ops stmmac_ethtool_ops = {  	.begin = stmmac_check_if_running,  	.get_drvinfo = stmmac_ethtool_getdrvinfo, @@ -623,7 +777,7 @@ static const struct ethtool_ops stmmac_ethtool_ops = {  	.get_eee = stmmac_ethtool_op_get_eee,  	.set_eee = stmmac_ethtool_op_set_eee,  	.get_sset_count	= stmmac_get_sset_count, -	.get_ts_info = ethtool_op_get_ts_info, +	.get_ts_info = stmmac_get_ts_info,  	.get_coalesce = stmmac_get_coalesce,  	.set_coalesce = stmmac_set_coalesce,  }; diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_hwtstamp.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_hwtstamp.c new file mode 100644 index 00000000000..def7e75e1d5 --- /dev/null +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_hwtstamp.c @@ -0,0 +1,148 @@ +/******************************************************************************* +  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. +	 */ +	data = (1000000000ULL / 50000000); + +	/* 0.465ns accuracy */ +	if (value & PTP_TCR_TSCTRLSSR) +		data = (data * 100) / 465; + +	writel(data, ioaddr + PTP_SSIR); +} + +static int stmmac_init_systime(void __iomem *ioaddr, u32 sec, u32 nsec) +{ +	int limit; +	u32 value; + +	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 = 10; +	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; + +	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 = 10; +	while (limit--) { +		if (!(readl(ioaddr + PTP_TCR) & PTP_TCR_TSADDREG)) +			break; +		mdelay(10); +	} +	if (limit < 0) +		return -EBUSY; + +	return 0; +} + +static int stmmac_adjust_systime(void __iomem *ioaddr, u32 sec, u32 nsec, +				 int add_sub) +{ +	u32 value; +	int limit; + +	writel(sec, ioaddr + PTP_STSUR); +	writel(((add_sub << PTP_STNSUR_ADDSUB_SHIFT) | nsec), +		ioaddr + PTP_STNSUR); +	/* issue command to initialize the system time value */ +	value = readl(ioaddr + PTP_TCR); +	value |= PTP_TCR_TSUPDT; +	writel(value, ioaddr + PTP_TCR); + +	/* wait for present system time adjust/update to complete */ +	limit = 10; +	while (limit--) { +		if (!(readl(ioaddr + PTP_TCR) & PTP_TCR_TSUPDT)) +			break; +		mdelay(10); +	} +	if (limit < 0) +		return -EBUSY; + +	return 0; +} + +static u64 stmmac_get_systime(void __iomem *ioaddr) +{ +	u64 ns; + +	ns = readl(ioaddr + PTP_STNSR); +	/* convert sec time value to nanosecond */ +	ns += readl(ioaddr + PTP_STSR) * 1000000000ULL; + +	return ns; +} + +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, +	.adjust_systime = stmmac_adjust_systime, +	.get_systime = stmmac_get_systime, +}; diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index 39c6c552463..6b26d31c268 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -47,6 +47,8 @@  #include <linux/debugfs.h>  #include <linux/seq_file.h>  #endif +#include <linux/net_tstamp.h> +#include "stmmac_ptp.h"  #include "stmmac.h"  #undef STMMAC_DEBUG @@ -130,6 +132,13 @@ module_param(eee_timer, int, S_IRUGO | S_IWUSR);  MODULE_PARM_DESC(eee_timer, "LPI tx expiration time in msec");  #define STMMAC_LPI_TIMER(x) (jiffies + msecs_to_jiffies(x)) +/* By default the driver will use the ring mode to manage tx and rx descriptors + * but passing this value so user can force to use the chain instead of the ring + */ +static unsigned int chain_mode; +module_param(chain_mode, int, S_IRUGO); +MODULE_PARM_DESC(chain_mode, "To use chain instead of ring mode"); +  static irqreturn_t stmmac_interrupt(int irq, void *dev_id);  #ifdef CONFIG_STMMAC_DEBUG_FS @@ -304,6 +313,339 @@ static void stmmac_eee_adjust(struct stmmac_priv *priv)  		priv->hw->mac->set_eee_pls(priv->ioaddr, priv->phydev->link);  } +/* stmmac_get_tx_hwtstamp: + * @priv : pointer to private device structure. + * @entry : descriptor index to be used. + * @skb : the socket buffer + * Description : + * This function will read timestamp from the descriptor & pass it to stack. + * and also perform some sanity checks. + */ +static void stmmac_get_tx_hwtstamp(struct stmmac_priv *priv, +				   unsigned int entry, +				   struct sk_buff *skb) +{ +	struct skb_shared_hwtstamps shhwtstamp; +	u64 ns; +	void *desc = NULL; + +	if (!priv->hwts_tx_en) +		return; + +	/* if skb doesn't support hw tstamp */ +	if (likely(!(skb_shinfo(skb)->tx_flags & SKBTX_IN_PROGRESS))) +		return; + +	if (priv->adv_ts) +		desc = (priv->dma_etx + entry); +	else +		desc = (priv->dma_tx + entry); + +	/* check tx tstamp status */ +	if (!priv->hw->desc->get_tx_timestamp_status((struct dma_desc *)desc)) +		return; + +	/* get the valid tstamp */ +	ns = priv->hw->desc->get_timestamp(desc, priv->adv_ts); + +	memset(&shhwtstamp, 0, sizeof(struct skb_shared_hwtstamps)); +	shhwtstamp.hwtstamp = ns_to_ktime(ns); +	/* pass tstamp to stack */ +	skb_tstamp_tx(skb, &shhwtstamp); + +	return; +} + +/* stmmac_get_rx_hwtstamp: + * @priv : pointer to private device structure. + * @entry : descriptor index to be used. + * @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, +				   unsigned int entry, +				   struct sk_buff *skb) +{ +	struct skb_shared_hwtstamps *shhwtstamp = NULL; +	u64 ns; +	void *desc = NULL; + +	if (!priv->hwts_rx_en) +		return; + +	if (priv->adv_ts) +		desc = (priv->dma_erx + entry); +	else +		desc = (priv->dma_rx + entry); + +	/* if rx tstamp is not valid */ +	if (!priv->hw->desc->get_rx_timestamp_status(desc, priv->adv_ts)) +		return; + +	/* get valid tstamp */ +	ns = priv->hw->desc->get_timestamp(desc, priv->adv_ts); +	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; +	u32 ptp_v2 = 0; +	u32 tstamp_all = 0; +	u32 ptp_over_ipv4_udp = 0; +	u32 ptp_over_ipv6_udp = 0; +	u32 ptp_over_ethernet = 0; +	u32 snap_type_sel = 0; +	u32 ts_master_en = 0; +	u32 ts_event_en = 0; +	u32 value = 0; + +	if (!(priv->dma_cap.time_stamp || priv->adv_ts)) { +		netdev_alert(priv->dev, "No support for HW time stamping\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; +	} + +	if (priv->adv_ts) { +		switch (config.rx_filter) { +		/* time stamp no incoming packet at all */ +		case HWTSTAMP_FILTER_NONE: +			config.rx_filter = HWTSTAMP_FILTER_NONE; +			break; + +		/* PTP v1, UDP, any kind of event packet */ +		case HWTSTAMP_FILTER_PTP_V1_L4_EVENT: +			config.rx_filter = HWTSTAMP_FILTER_PTP_V1_L4_EVENT; +			/* take time stamp for all event messages */ +			snap_type_sel = PTP_TCR_SNAPTYPSEL_1; + +			ptp_over_ipv4_udp = PTP_TCR_TSIPV4ENA; +			ptp_over_ipv6_udp = PTP_TCR_TSIPV6ENA; +			break; + +		/* PTP v1, UDP, Sync packet */ +		case HWTSTAMP_FILTER_PTP_V1_L4_SYNC: +			config.rx_filter = HWTSTAMP_FILTER_PTP_V1_L4_SYNC; +			/* take time stamp for SYNC messages only */ +			ts_event_en = PTP_TCR_TSEVNTENA; + +			ptp_over_ipv4_udp = PTP_TCR_TSIPV4ENA; +			ptp_over_ipv6_udp = PTP_TCR_TSIPV6ENA; +			break; + +		/* PTP v1, UDP, Delay_req packet */ +		case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ: +			config.rx_filter = HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ; +			/* take time stamp for Delay_Req messages only */ +			ts_master_en = PTP_TCR_TSMSTRENA; +			ts_event_en = PTP_TCR_TSEVNTENA; + +			ptp_over_ipv4_udp = PTP_TCR_TSIPV4ENA; +			ptp_over_ipv6_udp = PTP_TCR_TSIPV6ENA; +			break; + +		/* PTP v2, UDP, any kind of event packet */ +		case HWTSTAMP_FILTER_PTP_V2_L4_EVENT: +			config.rx_filter = HWTSTAMP_FILTER_PTP_V2_L4_EVENT; +			ptp_v2 = PTP_TCR_TSVER2ENA; +			/* take time stamp for all event messages */ +			snap_type_sel = PTP_TCR_SNAPTYPSEL_1; + +			ptp_over_ipv4_udp = PTP_TCR_TSIPV4ENA; +			ptp_over_ipv6_udp = PTP_TCR_TSIPV6ENA; +			break; + +		/* PTP v2, UDP, Sync packet */ +		case HWTSTAMP_FILTER_PTP_V2_L4_SYNC: +			config.rx_filter = HWTSTAMP_FILTER_PTP_V2_L4_SYNC; +			ptp_v2 = PTP_TCR_TSVER2ENA; +			/* take time stamp for SYNC messages only */ +			ts_event_en = PTP_TCR_TSEVNTENA; + +			ptp_over_ipv4_udp = PTP_TCR_TSIPV4ENA; +			ptp_over_ipv6_udp = PTP_TCR_TSIPV6ENA; +			break; + +		/* PTP v2, UDP, Delay_req packet */ +		case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ: +			config.rx_filter = HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ; +			ptp_v2 = PTP_TCR_TSVER2ENA; +			/* take time stamp for Delay_Req messages only */ +			ts_master_en = PTP_TCR_TSMSTRENA; +			ts_event_en = PTP_TCR_TSEVNTENA; + +			ptp_over_ipv4_udp = PTP_TCR_TSIPV4ENA; +			ptp_over_ipv6_udp = PTP_TCR_TSIPV6ENA; +			break; + +		/* PTP v2/802.AS1, any layer, any kind of event packet */ +		case HWTSTAMP_FILTER_PTP_V2_EVENT: +			config.rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT; +			ptp_v2 = PTP_TCR_TSVER2ENA; +			/* take time stamp for all event messages */ +			snap_type_sel = PTP_TCR_SNAPTYPSEL_1; + +			ptp_over_ipv4_udp = PTP_TCR_TSIPV4ENA; +			ptp_over_ipv6_udp = PTP_TCR_TSIPV6ENA; +			ptp_over_ethernet = PTP_TCR_TSIPENA; +			break; + +		/* PTP v2/802.AS1, any layer, Sync packet */ +		case HWTSTAMP_FILTER_PTP_V2_SYNC: +			config.rx_filter = HWTSTAMP_FILTER_PTP_V2_SYNC; +			ptp_v2 = PTP_TCR_TSVER2ENA; +			/* take time stamp for SYNC messages only */ +			ts_event_en = PTP_TCR_TSEVNTENA; + +			ptp_over_ipv4_udp = PTP_TCR_TSIPV4ENA; +			ptp_over_ipv6_udp = PTP_TCR_TSIPV6ENA; +			ptp_over_ethernet = PTP_TCR_TSIPENA; +			break; + +		/* PTP v2/802.AS1, any layer, Delay_req packet */ +		case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ: +			config.rx_filter = HWTSTAMP_FILTER_PTP_V2_DELAY_REQ; +			ptp_v2 = PTP_TCR_TSVER2ENA; +			/* take time stamp for Delay_Req messages only */ +			ts_master_en = PTP_TCR_TSMSTRENA; +			ts_event_en = PTP_TCR_TSEVNTENA; + +			ptp_over_ipv4_udp = PTP_TCR_TSIPV4ENA; +			ptp_over_ipv6_udp = PTP_TCR_TSIPV6ENA; +			ptp_over_ethernet = PTP_TCR_TSIPENA; +			break; + +		/* time stamp any incoming packet */ +		case HWTSTAMP_FILTER_ALL: +			config.rx_filter = HWTSTAMP_FILTER_ALL; +			tstamp_all = PTP_TCR_TSENALL; +			break; + +		default: +			return -ERANGE; +		} +	} else { +		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 { +		value = (PTP_TCR_TSENA | PTP_TCR_TSCFUPDT | PTP_TCR_TSCTRLSSR | +			tstamp_all | ptp_v2 | ptp_over_ethernet | +			ptp_over_ipv6_udp | ptp_over_ipv4_udp | ts_event_en | +			ts_master_en | snap_type_sel); + +		priv->hw->ptp->config_hw_tstamping(priv->ioaddr, value); + +		/* 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 int stmmac_init_ptp(struct stmmac_priv *priv) +{ +	if (!(priv->dma_cap.time_stamp || priv->dma_cap.atime_stamp)) +		return -EOPNOTSUPP; + +	if (netif_msg_hw(priv)) { +		if (priv->dma_cap.time_stamp) { +			pr_debug("IEEE 1588-2002 Time Stamp supported\n"); +			priv->adv_ts = 0; +		} +		if (priv->dma_cap.atime_stamp && priv->extend_desc) { +			pr_debug("IEEE 1588-2008 Advanced Time Stamp supported\n"); +			priv->adv_ts = 1; +		} +	} + +	priv->hw->ptp = &stmmac_ptp; +	priv->hwts_tx_en = 0; +	priv->hwts_rx_en = 0; + +	return stmmac_ptp_register(priv); +} + +static void stmmac_release_ptp(struct stmmac_priv *priv) +{ +	stmmac_ptp_unregister(priv); +} +  /**   * stmmac_adjust_link   * @dev: net device structure @@ -398,6 +740,24 @@ static void stmmac_adjust_link(struct net_device *dev)  	DBG(probe, DEBUG, "stmmac_adjust_link: exiting\n");  } +static void stmmac_check_pcs_mode(struct stmmac_priv *priv) +{ +	int interface = priv->plat->interface; + +	if (priv->dma_cap.pcs) { +		if ((interface & PHY_INTERFACE_MODE_RGMII) || +		    (interface & PHY_INTERFACE_MODE_RGMII_ID) || +		    (interface & PHY_INTERFACE_MODE_RGMII_RXID) || +		    (interface & PHY_INTERFACE_MODE_RGMII_TXID)) { +			pr_debug("STMMAC: PCS RGMII support enable\n"); +			priv->pcs = STMMAC_PCS_RGMII; +		} else if (interface & PHY_INTERFACE_MODE_SGMII) { +			pr_debug("STMMAC: PCS SGMII support enable\n"); +			priv->pcs = STMMAC_PCS_SGMII; +		} +	} +} +  /**   * stmmac_init_phy - PHY initialization   * @dev: net device structure @@ -461,29 +821,56 @@ static int stmmac_init_phy(struct net_device *dev)  }  /** - * display_ring + * stmmac_display_ring   * @p: pointer to the ring.   * @size: size of the ring. - * Description: display all the descriptors within the ring. + * Description: display the control/status and buffer descriptors.   */ -static void display_ring(struct dma_desc *p, int size) +static void stmmac_display_ring(void *head, int size, int extend_desc)  { -	struct tmp_s { -		u64 a; -		unsigned int b; -		unsigned int c; -	};  	int i; +	struct dma_extended_desc *ep = (struct dma_extended_desc *) head; +	struct dma_desc *p = (struct dma_desc *) head; +  	for (i = 0; i < size; i++) { -		struct tmp_s *x = (struct tmp_s *)(p + i); -		pr_info("\t%d [0x%x]: DES0=0x%x DES1=0x%x BUF1=0x%x BUF2=0x%x", -		       i, (unsigned int)virt_to_phys(&p[i]), -		       (unsigned int)(x->a), (unsigned int)((x->a) >> 32), -		       x->b, x->c); +		u64 x; +		if (extend_desc) { +			x = *(u64 *) ep; +			pr_info("%d [0x%x]: 0x%x 0x%x 0x%x 0x%x\n", +				i, (unsigned int) virt_to_phys(ep), +				(unsigned int) x, (unsigned int) (x >> 32), +				ep->basic.des2, ep->basic.des3); +			ep++; +		} else { +			x = *(u64 *) p; +			pr_info("%d [0x%x]: 0x%x 0x%x 0x%x 0x%x", +				i, (unsigned int) virt_to_phys(p), +				(unsigned int) x, (unsigned int) (x >> 32), +				p->des2, p->des3); +			p++; +		}  		pr_info("\n");  	}  } +static void stmmac_display_rings(struct stmmac_priv *priv) +{ +	unsigned int txsize = priv->dma_tx_size; +	unsigned int rxsize = priv->dma_rx_size; + +	if (priv->extend_desc) { +		pr_info("Extended RX descriptor ring:\n"); +		stmmac_display_ring((void *) priv->dma_erx, rxsize, 1); +		pr_info("Extended TX descriptor ring:\n"); +		stmmac_display_ring((void *) priv->dma_etx, txsize, 1); +	} else { +		pr_info("RX descriptor ring:\n"); +		stmmac_display_ring((void *)priv->dma_rx, rxsize, 0); +		pr_info("TX descriptor ring:\n"); +		stmmac_display_ring((void *)priv->dma_tx, txsize, 0); +	} +} +  static int stmmac_set_bfsize(int mtu, int bufsize)  {  	int ret = bufsize; @@ -500,6 +887,59 @@ static int stmmac_set_bfsize(int mtu, int bufsize)  	return ret;  } +static void stmmac_clear_descriptors(struct stmmac_priv *priv) +{ +	int i; +	unsigned int txsize = priv->dma_tx_size; +	unsigned int rxsize = priv->dma_rx_size; + +	/* Clear the Rx/Tx descriptors */ +	for (i = 0; i < rxsize; i++) +		if (priv->extend_desc) +			priv->hw->desc->init_rx_desc(&priv->dma_erx[i].basic, +						     priv->use_riwt, priv->mode, +						     (i == rxsize - 1)); +		else +			priv->hw->desc->init_rx_desc(&priv->dma_rx[i], +						     priv->use_riwt, priv->mode, +						     (i == rxsize - 1)); +	for (i = 0; i < txsize; i++) +		if (priv->extend_desc) +			priv->hw->desc->init_tx_desc(&priv->dma_etx[i].basic, +						     priv->mode, +						     (i == txsize - 1)); +		else +			priv->hw->desc->init_tx_desc(&priv->dma_tx[i], +						     priv->mode, +						     (i == txsize - 1)); +} + +static int stmmac_init_rx_buffers(struct stmmac_priv *priv, struct dma_desc *p, +				  int i) +{ +	struct sk_buff *skb; + +	skb = __netdev_alloc_skb(priv->dev, priv->dma_buf_sz + NET_IP_ALIGN, +				 GFP_KERNEL); +	if (unlikely(skb == NULL)) { +		pr_err("%s: Rx init fails; skb is NULL\n", __func__); +		return 1; +	} +	skb_reserve(skb, NET_IP_ALIGN); +	priv->rx_skbuff[i] = skb; +	priv->rx_skbuff_dma[i] = dma_map_single(priv->device, skb->data, +						priv->dma_buf_sz, +						DMA_FROM_DEVICE); + +	p->des2 = priv->rx_skbuff_dma[i]; + +	if ((priv->mode == STMMAC_RING_MODE) && +	    (priv->dma_buf_sz == BUF_SIZE_16KiB)) +		priv->hw->ring->init_desc3(p); + +	return 0; +} +  /**   * init_dma_desc_rings - init the RX/TX descriptor rings   * @dev: net device structure @@ -511,75 +951,70 @@ static void init_dma_desc_rings(struct net_device *dev)  {  	int i;  	struct stmmac_priv *priv = netdev_priv(dev); -	struct sk_buff *skb;  	unsigned int txsize = priv->dma_tx_size;  	unsigned int rxsize = priv->dma_rx_size; -	unsigned int bfsize; -	int dis_ic = 0; -	int des3_as_data_buf = 0; +	unsigned int bfsize = 0;  	/* Set the max buffer size according to the DESC mode  	 * and the MTU. Note that RING mode allows 16KiB bsize. */ -	bfsize = priv->hw->ring->set_16kib_bfsize(dev->mtu); +	if (priv->mode == STMMAC_RING_MODE) +		bfsize = priv->hw->ring->set_16kib_bfsize(dev->mtu); -	if (bfsize == BUF_SIZE_16KiB) -		des3_as_data_buf = 1; -	else +	if (bfsize < BUF_SIZE_16KiB)  		bfsize = stmmac_set_bfsize(dev->mtu, priv->dma_buf_sz);  	DBG(probe, INFO, "stmmac: txsize %d, rxsize %d, bfsize %d\n",  	    txsize, rxsize, bfsize); -	priv->rx_skbuff_dma = kmalloc_array(rxsize, sizeof(dma_addr_t), -					    GFP_KERNEL); -	priv->rx_skbuff = kmalloc_array(rxsize, sizeof(struct sk_buff *), -					GFP_KERNEL); -	priv->dma_rx = -	    (struct dma_desc *)dma_alloc_coherent(priv->device, -						  rxsize * +	if (priv->extend_desc) { +		priv->dma_erx = dma_alloc_coherent(priv->device, rxsize * +						   sizeof(struct +							  dma_extended_desc), +						   &priv->dma_rx_phy, +						   GFP_KERNEL); +		priv->dma_etx = dma_alloc_coherent(priv->device, txsize * +						   sizeof(struct +							  dma_extended_desc), +						   &priv->dma_tx_phy, +						   GFP_KERNEL); +		if ((!priv->dma_erx) || (!priv->dma_etx)) +			return; +	} else { +		priv->dma_rx = dma_alloc_coherent(priv->device, rxsize *  						  sizeof(struct dma_desc),  						  &priv->dma_rx_phy,  						  GFP_KERNEL); -	priv->tx_skbuff = kmalloc_array(txsize, sizeof(struct sk_buff *), -					GFP_KERNEL); -	priv->dma_tx = -	    (struct dma_desc *)dma_alloc_coherent(priv->device, -						  txsize * +		priv->dma_tx = dma_alloc_coherent(priv->device, txsize *  						  sizeof(struct dma_desc),  						  &priv->dma_tx_phy,  						  GFP_KERNEL); - -	if ((priv->dma_rx == NULL) || (priv->dma_tx == NULL)) { -		pr_err("%s:ERROR allocating the DMA Tx/Rx desc\n", __func__); -		return; +		if ((!priv->dma_rx) || (!priv->dma_tx)) +			return;  	} -	DBG(probe, INFO, "stmmac (%s) DMA desc: virt addr (Rx %p, " -	    "Tx %p)\n\tDMA phy addr (Rx 0x%08x, Tx 0x%08x)\n", -	    dev->name, priv->dma_rx, priv->dma_tx, -	    (unsigned int)priv->dma_rx_phy, (unsigned int)priv->dma_tx_phy); +	priv->rx_skbuff_dma = kmalloc_array(rxsize, sizeof(dma_addr_t), +					    GFP_KERNEL); +	priv->rx_skbuff = kmalloc_array(rxsize, sizeof(struct sk_buff *), +					GFP_KERNEL); +	priv->tx_skbuff_dma = kmalloc_array(txsize, sizeof(dma_addr_t), +					GFP_KERNEL); +	priv->tx_skbuff = kmalloc_array(txsize, sizeof(struct sk_buff *), +					GFP_KERNEL); +	if (netif_msg_drv(priv)) +		pr_debug("(%s) dma_rx_phy=0x%08x dma_tx_phy=0x%08x\n", __func__, +			 (u32) priv->dma_rx_phy, (u32) priv->dma_tx_phy);  	/* RX INITIALIZATION */ -	DBG(probe, INFO, "stmmac: SKB addresses:\n" -			 "skb\t\tskb data\tdma data\n"); - +	DBG(probe, INFO, "stmmac: SKB addresses:\nskb\t\tskb data\tdma data\n");  	for (i = 0; i < rxsize; i++) { -		struct dma_desc *p = priv->dma_rx + i; +		struct dma_desc *p; +		if (priv->extend_desc) +			p = &((priv->dma_erx + i)->basic); +		else +			p = priv->dma_rx + i; -		skb = __netdev_alloc_skb(dev, bfsize + NET_IP_ALIGN, -					 GFP_KERNEL); -		if (unlikely(skb == NULL)) { -			pr_err("%s: Rx init fails; skb is NULL\n", __func__); +		if (stmmac_init_rx_buffers(priv, p, i))  			break; -		} -		skb_reserve(skb, NET_IP_ALIGN); -		priv->rx_skbuff[i] = skb; -		priv->rx_skbuff_dma[i] = dma_map_single(priv->device, skb->data, -						bfsize, DMA_FROM_DEVICE); - -		p->des2 = priv->rx_skbuff_dma[i]; - -		priv->hw->ring->init_desc3(des3_as_data_buf, p);  		DBG(probe, INFO, "[%p]\t[%p]\t[%x]\n", priv->rx_skbuff[i],  			priv->rx_skbuff[i]->data, priv->rx_skbuff_dma[i]); @@ -589,32 +1024,40 @@ static void init_dma_desc_rings(struct net_device *dev)  	priv->dma_buf_sz = bfsize;  	buf_sz = bfsize; +	/* Setup the chained descriptor addresses */ +	if (priv->mode == STMMAC_CHAIN_MODE) { +		if (priv->extend_desc) { +			priv->hw->chain->init(priv->dma_erx, priv->dma_rx_phy, +					      rxsize, 1); +			priv->hw->chain->init(priv->dma_etx, priv->dma_tx_phy, +					      txsize, 1); +		} else { +			priv->hw->chain->init(priv->dma_rx, priv->dma_rx_phy, +					      rxsize, 0); +			priv->hw->chain->init(priv->dma_tx, priv->dma_tx_phy, +					      txsize, 0); +		} +	} +  	/* TX INITIALIZATION */  	for (i = 0; i < txsize; i++) { +		struct dma_desc *p; +		if (priv->extend_desc) +			p = &((priv->dma_etx + i)->basic); +		else +			p = priv->dma_tx + i; +		p->des2 = 0; +		priv->tx_skbuff_dma[i] = 0;  		priv->tx_skbuff[i] = NULL; -		priv->dma_tx[i].des2 = 0;  	} -	/* In case of Chained mode this sets the des3 to the next -	 * element in the chain */ -	priv->hw->ring->init_dma_chain(priv->dma_rx, priv->dma_rx_phy, rxsize); -	priv->hw->ring->init_dma_chain(priv->dma_tx, priv->dma_tx_phy, txsize); -  	priv->dirty_tx = 0;  	priv->cur_tx = 0; -	if (priv->use_riwt) -		dis_ic = 1; -	/* Clear the Rx/Tx descriptors */ -	priv->hw->desc->init_rx_desc(priv->dma_rx, rxsize, dis_ic); -	priv->hw->desc->init_tx_desc(priv->dma_tx, txsize); +	stmmac_clear_descriptors(priv); -	if (netif_msg_hw(priv)) { -		pr_info("RX descriptor ring:\n"); -		display_ring(priv->dma_rx, rxsize); -		pr_info("TX descriptor ring:\n"); -		display_ring(priv->dma_tx, txsize); -	} +	if (netif_msg_hw(priv)) +		stmmac_display_rings(priv);  }  static void dma_free_rx_skbufs(struct stmmac_priv *priv) @@ -637,13 +1080,20 @@ static void dma_free_tx_skbufs(struct stmmac_priv *priv)  	for (i = 0; i < priv->dma_tx_size; i++) {  		if (priv->tx_skbuff[i] != NULL) { -			struct dma_desc *p = priv->dma_tx + i; -			if (p->des2) -				dma_unmap_single(priv->device, p->des2, +			struct dma_desc *p; +			if (priv->extend_desc) +				p = &((priv->dma_etx + i)->basic); +			else +				p = priv->dma_tx + i; + +			if (priv->tx_skbuff_dma[i]) +				dma_unmap_single(priv->device, +						 priv->tx_skbuff_dma[i],  						 priv->hw->desc->get_tx_len(p),  						 DMA_TO_DEVICE);  			dev_kfree_skb_any(priv->tx_skbuff[i]);  			priv->tx_skbuff[i] = NULL; +			priv->tx_skbuff_dma[i] = 0;  		}  	}  } @@ -656,14 +1106,24 @@ static void free_dma_desc_resources(struct stmmac_priv *priv)  	/* Free the region of consistent memory previously allocated for  	 * the DMA */ -	dma_free_coherent(priv->device, -			  priv->dma_tx_size * sizeof(struct dma_desc), -			  priv->dma_tx, priv->dma_tx_phy); -	dma_free_coherent(priv->device, -			  priv->dma_rx_size * sizeof(struct dma_desc), -			  priv->dma_rx, priv->dma_rx_phy); +	if (!priv->extend_desc) { +		dma_free_coherent(priv->device, +				  priv->dma_tx_size * sizeof(struct dma_desc), +				  priv->dma_tx, priv->dma_tx_phy); +		dma_free_coherent(priv->device, +				  priv->dma_rx_size * sizeof(struct dma_desc), +				  priv->dma_rx, priv->dma_rx_phy); +	} else { +		dma_free_coherent(priv->device, priv->dma_tx_size * +				  sizeof(struct dma_extended_desc), +				  priv->dma_etx, priv->dma_tx_phy); +		dma_free_coherent(priv->device, priv->dma_rx_size * +				  sizeof(struct dma_extended_desc), +				  priv->dma_erx, priv->dma_rx_phy); +	}  	kfree(priv->rx_skbuff_dma);  	kfree(priv->rx_skbuff); +	kfree(priv->tx_skbuff_dma);  	kfree(priv->tx_skbuff);  } @@ -708,13 +1168,18 @@ static void stmmac_tx_clean(struct stmmac_priv *priv)  		int last;  		unsigned int entry = priv->dirty_tx % txsize;  		struct sk_buff *skb = priv->tx_skbuff[entry]; -		struct dma_desc *p = priv->dma_tx + entry; +		struct dma_desc *p; + +		if (priv->extend_desc) +			p = (struct dma_desc *) (priv->dma_etx + entry); +		else +			p = priv->dma_tx + entry;  		/* Check if the descriptor is owned by the DMA. */  		if (priv->hw->desc->get_tx_owner(p))  			break; -		/* Verify tx error by looking at the last segment */ +		/* Verify tx error by looking at the last segment. */  		last = priv->hw->desc->get_tx_ls(p);  		if (likely(last)) {  			int tx_error = @@ -726,22 +1191,27 @@ static void stmmac_tx_clean(struct stmmac_priv *priv)  				priv->xstats.tx_pkt_n++;  			} else  				priv->dev->stats.tx_errors++; + +			stmmac_get_tx_hwtstamp(priv, entry, skb);  		}  		TX_DBG("%s: curr %d, dirty %d\n", __func__,  			priv->cur_tx, priv->dirty_tx); -		if (likely(p->des2)) -			dma_unmap_single(priv->device, p->des2, +		if (likely(priv->tx_skbuff_dma[entry])) { +			dma_unmap_single(priv->device, +					 priv->tx_skbuff_dma[entry],  					 priv->hw->desc->get_tx_len(p),  					 DMA_TO_DEVICE); -		priv->hw->ring->clean_desc3(p); +			priv->tx_skbuff_dma[entry] = 0; +		} +		priv->hw->ring->clean_desc3(priv, p);  		if (likely(skb != NULL)) {  			dev_kfree_skb(skb);  			priv->tx_skbuff[entry] = NULL;  		} -		priv->hw->desc->release_tx_desc(p); +		priv->hw->desc->release_tx_desc(p, priv->mode);  		priv->dirty_tx++;  	} @@ -782,11 +1252,21 @@ static inline void stmmac_disable_dma_irq(struct stmmac_priv *priv)   */  static void stmmac_tx_err(struct stmmac_priv *priv)  { +	int i; +	int txsize = priv->dma_tx_size;  	netif_stop_queue(priv->dev);  	priv->hw->dma->stop_tx(priv->ioaddr);  	dma_free_tx_skbufs(priv); -	priv->hw->desc->init_tx_desc(priv->dma_tx, priv->dma_tx_size); +	for (i = 0; i < txsize; i++) +		if (priv->extend_desc) +			priv->hw->desc->init_tx_desc(&priv->dma_etx[i].basic, +						     priv->mode, +						     (i == txsize - 1)); +		else +			priv->hw->desc->init_tx_desc(&priv->dma_tx[i], +						     priv->mode, +						     (i == txsize - 1));  	priv->dirty_tx = 0;  	priv->cur_tx = 0;  	priv->hw->dma->start_tx(priv->ioaddr); @@ -860,6 +1340,14 @@ static void stmmac_selec_desc_mode(struct stmmac_priv *priv)  {  	if (priv->plat->enh_desc) {  		pr_info(" Enhanced/Alternate descriptors\n"); + +		/* GMAC older than 3.50 has no extended descriptors */ +		if (priv->synopsys_id >= DWMAC_CORE_3_50) { +			pr_info("\tEnabled extended descriptors\n"); +			priv->extend_desc = 1; +		} else +			pr_warn("Extended descriptors not supported\n"); +  		priv->hw->desc = &enh_desc_ops;  	} else {  		pr_info(" Normal descriptors\n"); @@ -946,6 +1434,7 @@ static int stmmac_init_dma_engine(struct stmmac_priv *priv)  {  	int pbl = DEFAULT_DMA_PBL, fixed_burst = 0, burst_len = 0;  	int mixed_burst = 0; +	int atds = 0;  	/* Some DMA parameters can be passed from the platform;  	 * in case of these are not passed we keep a default @@ -957,9 +1446,12 @@ static int stmmac_init_dma_engine(struct stmmac_priv *priv)  		burst_len = priv->plat->dma_cfg->burst_len;  	} +	if (priv->extend_desc && (priv->mode == STMMAC_RING_MODE)) +		atds = 1; +  	return priv->hw->dma->init(priv->ioaddr, pbl, fixed_burst, mixed_burst,  				   burst_len, priv->dma_tx_phy, -				   priv->dma_rx_phy); +				   priv->dma_rx_phy, atds);  }  /** @@ -1012,10 +1504,13 @@ static int stmmac_open(struct net_device *dev)  	stmmac_check_ether_addr(priv); -	ret = stmmac_init_phy(dev); -	if (unlikely(ret)) { -		pr_err("%s: Cannot attach to PHY (error: %d)\n", __func__, ret); -		goto open_error; +	if (!priv->pcs) { +		ret = stmmac_init_phy(dev); +		if (ret) { +			pr_err("%s: Cannot attach to PHY (error: %d)\n", +			       __func__, ret); +			goto open_error; +		}  	}  	/* Create and initialize the TX/RX descriptors chains. */ @@ -1084,6 +1579,10 @@ static int stmmac_open(struct net_device *dev)  	stmmac_mmc_setup(priv); +	ret = stmmac_init_ptp(priv); +	if (ret) +		pr_warn("%s: failed PTP initialisation\n", __func__); +  #ifdef CONFIG_STMMAC_DEBUG_FS  	ret = stmmac_init_fs(dev);  	if (ret < 0) @@ -1104,7 +1603,12 @@ static int stmmac_open(struct net_device *dev)  		phy_start(priv->phydev);  	priv->tx_lpi_timer = STMMAC_DEFAULT_TWT_LS_TIMER; -	priv->eee_enabled = stmmac_eee_init(priv); + +	/* Using PCS we cannot dial with the phy registers at this stage +	 * so we do not support extra feature like EEE. +	 */ +	if (!priv->pcs) +		priv->eee_enabled = stmmac_eee_init(priv);  	stmmac_init_tx_coalesce(priv); @@ -1113,6 +1617,9 @@ static int stmmac_open(struct net_device *dev)  		priv->hw->dma->rx_watchdog(priv->ioaddr, MAX_DMA_RIWT);  	} +	if (priv->pcs && priv->hw->mac->ctrl_ane) +		priv->hw->mac->ctrl_ane(priv->ioaddr, 0); +  	napi_enable(&priv->napi);  	netif_start_queue(dev); @@ -1184,6 +1691,8 @@ static int stmmac_release(struct net_device *dev)  #endif  	clk_disable_unprepare(priv->stmmac_clk); +	stmmac_release_ptp(priv); +  	return 0;  } @@ -1198,7 +1707,7 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev)  	struct stmmac_priv *priv = netdev_priv(dev);  	unsigned int txsize = priv->dma_tx_size;  	unsigned int entry; -	int i, csum_insertion = 0; +	int i, csum_insertion = 0, is_jumbo = 0;  	int nfrags = skb_shinfo(skb)->nr_frags;  	struct dma_desc *desc, *first;  	unsigned int nopaged_len = skb_headlen(skb); @@ -1233,7 +1742,11 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev)  	csum_insertion = (skb->ip_summed == CHECKSUM_PARTIAL); -	desc = priv->dma_tx + entry; +	if (priv->extend_desc) +		desc = (struct dma_desc *) (priv->dma_etx + entry); +	else +		desc = priv->dma_tx + entry; +  	first = desc;  #ifdef STMMAC_XMIT_DEBUG @@ -1244,28 +1757,46 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev)  #endif  	priv->tx_skbuff[entry] = skb; -	if (priv->hw->ring->is_jumbo_frm(skb->len, priv->plat->enh_desc)) { -		entry = priv->hw->ring->jumbo_frm(priv, skb, csum_insertion); -		desc = priv->dma_tx + entry; +	/* To program the descriptors according to the size of the frame */ +	if (priv->mode == STMMAC_RING_MODE) { +		is_jumbo = priv->hw->ring->is_jumbo_frm(skb->len, +							priv->plat->enh_desc); +		if (unlikely(is_jumbo)) +			entry = priv->hw->ring->jumbo_frm(priv, skb, +							  csum_insertion);  	} else { +		is_jumbo = priv->hw->chain->is_jumbo_frm(skb->len, +							priv->plat->enh_desc); +		if (unlikely(is_jumbo)) +			entry = priv->hw->chain->jumbo_frm(priv, skb, +							   csum_insertion); +	} +	if (likely(!is_jumbo)) {  		desc->des2 = dma_map_single(priv->device, skb->data,  					nopaged_len, DMA_TO_DEVICE); +		priv->tx_skbuff_dma[entry] = desc->des2;  		priv->hw->desc->prepare_tx_desc(desc, 1, nopaged_len, -						csum_insertion); -	} +						csum_insertion, priv->mode); +	} else +		desc = first;  	for (i = 0; i < nfrags; i++) {  		const skb_frag_t *frag = &skb_shinfo(skb)->frags[i];  		int len = skb_frag_size(frag);  		entry = (++priv->cur_tx) % txsize; -		desc = priv->dma_tx + entry; +		if (priv->extend_desc) +			desc = (struct dma_desc *) (priv->dma_etx + entry); +		else +			desc = priv->dma_tx + entry;  		TX_DBG("\t[entry %d] segment len: %d\n", entry, len);  		desc->des2 = skb_frag_dma_map(priv->device, frag, 0, len,  					      DMA_TO_DEVICE); +		priv->tx_skbuff_dma[entry] = desc->des2;  		priv->tx_skbuff[entry] = NULL; -		priv->hw->desc->prepare_tx_desc(desc, 0, len, csum_insertion); +		priv->hw->desc->prepare_tx_desc(desc, 0, len, csum_insertion, +						priv->mode);  		wmb();  		priv->hw->desc->set_tx_owner(desc);  		wmb(); @@ -1302,7 +1833,11 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev)  		       "first=%p, nfrags=%d\n",  		       (priv->cur_tx % txsize), (priv->dirty_tx % txsize),  		       entry, first, nfrags); -		display_ring(priv->dma_tx, txsize); +		if (priv->extend_desc) +			stmmac_display_ring((void *)priv->dma_etx, txsize, 1); +		else +			stmmac_display_ring((void *)priv->dma_tx, txsize, 0); +  		pr_info(">>> frame to be transmitted: ");  		print_pkt(skb->data, skb->len);  	} @@ -1314,7 +1849,15 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev)  	dev->stats.tx_bytes += skb->len; -	skb_tx_timestamp(skb); +	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) +		skb_tx_timestamp(skb);  	priv->hw->dma->enable_dma_transmission(priv->ioaddr); @@ -1327,10 +1870,16 @@ static inline void stmmac_rx_refill(struct stmmac_priv *priv)  {  	unsigned int rxsize = priv->dma_rx_size;  	int bfsize = priv->dma_buf_sz; -	struct dma_desc *p = priv->dma_rx;  	for (; priv->cur_rx - priv->dirty_rx > 0; priv->dirty_rx++) {  		unsigned int entry = priv->dirty_rx % rxsize; +		struct dma_desc *p; + +		if (priv->extend_desc) +			p = (struct dma_desc *) (priv->dma_erx + entry); +		else +			p = priv->dma_rx + entry; +  		if (likely(priv->rx_skbuff[entry] == NULL)) {  			struct sk_buff *skb; @@ -1344,15 +1893,14 @@ static inline void stmmac_rx_refill(struct stmmac_priv *priv)  			    dma_map_single(priv->device, skb->data, bfsize,  					   DMA_FROM_DEVICE); -			(p + entry)->des2 = priv->rx_skbuff_dma[entry]; +			p->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);  			RX_DBG(KERN_INFO "\trefill entry #%d\n", entry);  		}  		wmb(); -		priv->hw->desc->set_rx_owner(p + entry); +		priv->hw->desc->set_rx_owner(p);  		wmb();  	}  } @@ -1363,33 +1911,61 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit)  	unsigned int entry = priv->cur_rx % rxsize;  	unsigned int next_entry;  	unsigned int count = 0; -	struct dma_desc *p = priv->dma_rx + entry; -	struct dma_desc *p_next;  #ifdef STMMAC_RX_DEBUG  	if (netif_msg_hw(priv)) {  		pr_debug(">>> stmmac_rx: descriptor ring:\n"); -		display_ring(priv->dma_rx, rxsize); +		if (priv->extend_desc) +			stmmac_display_ring((void *) priv->dma_erx, rxsize, 1); +		else +			stmmac_display_ring((void *)priv->dma_rx, rxsize, 0);  	}  #endif -	while (!priv->hw->desc->get_rx_owner(p)) { +	while (count < limit) {  		int status; +		struct dma_desc *p, *p_next; -		if (count >= limit) +		if (priv->extend_desc) +			p = (struct dma_desc *) (priv->dma_erx + entry); +		else +			p = priv->dma_rx + entry ; + +		if (priv->hw->desc->get_rx_owner(p))  			break;  		count++;  		next_entry = (++priv->cur_rx) % rxsize; -		p_next = priv->dma_rx + next_entry; +		if (priv->extend_desc) +			p_next = (struct dma_desc *) (priv->dma_erx + +						      next_entry); +		else +			p_next = priv->dma_rx + next_entry; +  		prefetch(p_next);  		/* read the status of the incoming frame */ -		status = (priv->hw->desc->rx_status(&priv->dev->stats, -						    &priv->xstats, p)); -		if (unlikely(status == discard_frame)) +		status = priv->hw->desc->rx_status(&priv->dev->stats, +						   &priv->xstats, p); +		if ((priv->extend_desc) && (priv->hw->desc->rx_extended_status)) +			priv->hw->desc->rx_extended_status(&priv->dev->stats, +							   &priv->xstats, +							   priv->dma_erx + +							   entry); +		if (unlikely(status == discard_frame)) {  			priv->dev->stats.rx_errors++; -		else { +			if (priv->hwts_rx_en && !priv->extend_desc) { +				/* 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); +			} +		} else {  			struct sk_buff *skb;  			int frame_len; @@ -1418,6 +1994,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, entry, skb); +  			skb_put(skb, frame_len);  			dma_unmap_single(priv->device,  					 priv->rx_skbuff_dma[entry], @@ -1441,7 +2019,6 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit)  			priv->dev->stats.rx_bytes += frame_len;  		}  		entry = next_entry; -		p = p_next;	/* use prefetched values */  	}  	stmmac_rx_refill(priv); @@ -1604,30 +2181,14 @@ static irqreturn_t stmmac_interrupt(int irq, void *dev_id)  	/* To handle GMAC own interrupts */  	if (priv->plat->has_gmac) {  		int status = priv->hw->mac->host_irq_status((void __iomem *) -							    dev->base_addr); +							    dev->base_addr, +							    &priv->xstats);  		if (unlikely(status)) { -			if (status & core_mmc_tx_irq) -				priv->xstats.mmc_tx_irq_n++; -			if (status & core_mmc_rx_irq) -				priv->xstats.mmc_rx_irq_n++; -			if (status & core_mmc_rx_csum_offload_irq) -				priv->xstats.mmc_rx_csum_offload_irq_n++; -			if (status & core_irq_receive_pmt_irq) -				priv->xstats.irq_receive_pmt_irq_n++; -  			/* For LPI we need to save the tx status */ -			if (status & core_irq_tx_path_in_lpi_mode) { -				priv->xstats.irq_tx_path_in_lpi_mode_n++; +			if (status & CORE_IRQ_TX_PATH_IN_LPI_MODE)  				priv->tx_path_in_lpi_mode = true; -			} -			if (status & core_irq_tx_path_exit_lpi_mode) { -				priv->xstats.irq_tx_path_exit_lpi_mode_n++; +			if (status & CORE_IRQ_TX_PATH_EXIT_LPI_MODE)  				priv->tx_path_in_lpi_mode = false; -			} -			if (status & core_irq_rx_path_in_lpi_mode) -				priv->xstats.irq_rx_path_in_lpi_mode_n++; -			if (status & core_irq_rx_path_exit_lpi_mode) -				priv->xstats.irq_rx_path_exit_lpi_mode_n++;  		}  	} @@ -1655,21 +2216,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;  } @@ -1679,40 +2249,51 @@ static struct dentry *stmmac_fs_dir;  static struct dentry *stmmac_rings_status;  static struct dentry *stmmac_dma_cap; -static int stmmac_sysfs_ring_read(struct seq_file *seq, void *v) +static void sysfs_display_ring(void *head, int size, int extend_desc, +				struct seq_file *seq)  { -	struct tmp_s { -		u64 a; -		unsigned int b; -		unsigned int c; -	};  	int i; -	struct net_device *dev = seq->private; -	struct stmmac_priv *priv = netdev_priv(dev); +	struct dma_extended_desc *ep = (struct dma_extended_desc *) head; +	struct dma_desc *p = (struct dma_desc *) head; -	seq_printf(seq, "=======================\n"); -	seq_printf(seq, " RX descriptor ring\n"); -	seq_printf(seq, "=======================\n"); - -	for (i = 0; i < priv->dma_rx_size; i++) { -		struct tmp_s *x = (struct tmp_s *)(priv->dma_rx + i); -		seq_printf(seq, "[%d] DES0=0x%x DES1=0x%x BUF1=0x%x BUF2=0x%x", -			   i, (unsigned int)(x->a), -			   (unsigned int)((x->a) >> 32), x->b, x->c); +	for (i = 0; i < size; i++) { +		u64 x; +		if (extend_desc) { +			x = *(u64 *) ep; +			seq_printf(seq, "%d [0x%x]: 0x%x 0x%x 0x%x 0x%x\n", +				   i, (unsigned int) virt_to_phys(ep), +				   (unsigned int) x, (unsigned int) (x >> 32), +				   ep->basic.des2, ep->basic.des3); +			ep++; +		} else { +			x = *(u64 *) p; +			seq_printf(seq, "%d [0x%x]: 0x%x 0x%x 0x%x 0x%x\n", +				   i, (unsigned int) virt_to_phys(ep), +				   (unsigned int) x, (unsigned int) (x >> 32), +				   p->des2, p->des3); +			p++; +		}  		seq_printf(seq, "\n");  	} +} -	seq_printf(seq, "\n"); -	seq_printf(seq, "=======================\n"); -	seq_printf(seq, "  TX descriptor ring\n"); -	seq_printf(seq, "=======================\n"); +static int stmmac_sysfs_ring_read(struct seq_file *seq, void *v) +{ +	struct net_device *dev = seq->private; +	struct stmmac_priv *priv = netdev_priv(dev); +	unsigned int txsize = priv->dma_tx_size; +	unsigned int rxsize = priv->dma_rx_size; -	for (i = 0; i < priv->dma_tx_size; i++) { -		struct tmp_s *x = (struct tmp_s *)(priv->dma_tx + i); -		seq_printf(seq, "[%d] DES0=0x%x DES1=0x%x BUF1=0x%x BUF2=0x%x", -			   i, (unsigned int)(x->a), -			   (unsigned int)((x->a) >> 32), x->b, x->c); -		seq_printf(seq, "\n"); +	if (priv->extend_desc) { +		seq_printf(seq, "Extended RX descriptor ring:\n"); +		sysfs_display_ring((void *) priv->dma_erx, rxsize, 1, seq); +		seq_printf(seq, "Extended TX descriptor ring:\n"); +		sysfs_display_ring((void *) priv->dma_etx, txsize, 1, seq); +	} else { +		seq_printf(seq, "RX descriptor ring:\n"); +		sysfs_display_ring((void *)priv->dma_rx, rxsize, 0, seq); +		seq_printf(seq, "TX descriptor ring:\n"); +		sysfs_display_ring((void *)priv->dma_tx, txsize, 0, seq);  	}  	return 0; @@ -1877,7 +2458,7 @@ static const struct net_device_ops stmmac_netdev_ops = {   */  static int stmmac_hw_init(struct stmmac_priv *priv)  { -	int ret = 0; +	int ret;  	struct mac_device_info *mac;  	/* Identify the MAC HW device */ @@ -1892,12 +2473,23 @@ static int stmmac_hw_init(struct stmmac_priv *priv)  	priv->hw = mac; -	/* To use the chained or ring mode */ -	priv->hw->ring = &ring_mode_ops; -  	/* Get and dump the chip ID */  	priv->synopsys_id = stmmac_get_synopsys_id(priv); +	/* To use alternate (extended) or normal descriptor structures */ +	stmmac_selec_desc_mode(priv); + +	/* To use the chained or ring mode */ +	if (chain_mode)	{ +		priv->hw->chain = &chain_mode_ops; +		pr_info(" Chain mode enabled\n"); +		priv->mode = STMMAC_CHAIN_MODE; +	} else { +		priv->hw->ring = &ring_mode_ops; +		pr_info(" Ring mode enabled\n"); +		priv->mode = STMMAC_RING_MODE; +	} +  	/* Get the HW capability (new GMAC newer than 3.50a) */  	priv->hw_cap_support = stmmac_get_hw_features(priv);  	if (priv->hw_cap_support) { @@ -1921,9 +2513,6 @@ static int stmmac_hw_init(struct stmmac_priv *priv)  	} else  		pr_info(" No HW DMA feature register supported"); -	/* Select the enhnaced/normal descriptor structures */ -	stmmac_selec_desc_mode(priv); -  	/* Enable the IPC (Checksum Offload) and check if the feature has been  	 * enabled during the core configuration. */  	ret = priv->hw->mac->rx_ipc(priv->ioaddr); @@ -1943,7 +2532,7 @@ static int stmmac_hw_init(struct stmmac_priv *priv)  		device_set_wakeup_capable(priv->device, 1);  	} -	return ret; +	return 0;  }  /** @@ -1989,7 +2578,9 @@ struct stmmac_priv *stmmac_dvr_probe(struct device *device,  		priv->plat->phy_addr = phyaddr;  	/* Init MAC and get the capabilities */ -	stmmac_hw_init(priv); +	ret = stmmac_hw_init(priv); +	if (ret) +		goto error_free_netdev;  	ndev->netdev_ops = &stmmac_netdev_ops; @@ -2044,12 +2635,16 @@ struct stmmac_priv *stmmac_dvr_probe(struct device *device,  	else  		priv->clk_csr = priv->plat->clk_csr; -	/* MDIO bus Registration */ -	ret = stmmac_mdio_register(ndev); -	if (ret < 0) { -		pr_debug("%s: MDIO bus (id: %d) registration failed", -			 __func__, priv->plat->bus_id); -		goto error_mdio_register; +	stmmac_check_pcs_mode(priv); + +	if (!priv->pcs) { +		/* MDIO bus Registration */ +		ret = stmmac_mdio_register(ndev); +		if (ret < 0) { +			pr_debug("%s: MDIO bus (id: %d) registration failed", +				 __func__, priv->plat->bus_id); +			goto error_mdio_register; +		}  	}  	return priv; @@ -2060,6 +2655,7 @@ error_clk_get:  	unregister_netdev(ndev);  error_netdev_register:  	netif_napi_del(&priv->napi); +error_free_netdev:  	free_netdev(ndev);  	return NULL; @@ -2081,7 +2677,8 @@ int stmmac_dvr_remove(struct net_device *ndev)  	priv->hw->dma->stop_tx(priv->ioaddr);  	stmmac_set_mac(priv->ioaddr, false); -	stmmac_mdio_unregister(ndev); +	if (!priv->pcs) +		stmmac_mdio_unregister(ndev);  	netif_carrier_off(ndev);  	unregister_netdev(ndev);  	free_netdev(ndev); @@ -2093,7 +2690,6 @@ int stmmac_dvr_remove(struct net_device *ndev)  int stmmac_suspend(struct net_device *ndev)  {  	struct stmmac_priv *priv = netdev_priv(ndev); -	int dis_ic = 0;  	unsigned long flags;  	if (!ndev || !netif_running(ndev)) @@ -2107,18 +2703,13 @@ int stmmac_suspend(struct net_device *ndev)  	netif_device_detach(ndev);  	netif_stop_queue(ndev); -	if (priv->use_riwt) -		dis_ic = 1; -  	napi_disable(&priv->napi);  	/* Stop TX/RX DMA */  	priv->hw->dma->stop_tx(priv->ioaddr);  	priv->hw->dma->stop_rx(priv->ioaddr); -	/* Clear the Rx/Tx descriptors */ -	priv->hw->desc->init_rx_desc(priv->dma_rx, priv->dma_rx_size, -				     dis_ic); -	priv->hw->desc->init_tx_desc(priv->dma_tx, priv->dma_tx_size); + +	stmmac_clear_descriptors(priv);  	/* Enable Power down mode by programming the PMT regs */  	if (device_may_wakeup(priv->device)) @@ -2257,6 +2848,9 @@ static int __init stmmac_cmdline_opt(char *str)  		} else if (!strncmp(opt, "eee_timer:", 10)) {  			if (kstrtoint(opt + 10, 0, &eee_timer))  				goto err; +		} else if (!strncmp(opt, "chain_mode:", 11)) { +			if (kstrtoint(opt + 11, 0, &chain_mode)) +				goto err;  		}  	}  	return 0; diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_ptp.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_ptp.c new file mode 100644 index 00000000000..93d4beff92c --- /dev/null +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_ptp.c @@ -0,0 +1,215 @@ +/******************************************************************************* +  PTP 1588 clock using the STMMAC. + +  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> +*******************************************************************************/ +#include "stmmac.h" +#include "stmmac_ptp.h" + +/** + * stmmac_adjust_freq + * + * @ptp: pointer to ptp_clock_info structure + * @ppb: desired period change in parts ber billion + * + * Description: this function will adjust the frequency of hardware clock. + */ +static int stmmac_adjust_freq(struct ptp_clock_info *ptp, s32 ppb) +{ +	struct stmmac_priv *priv = +	    container_of(ptp, struct stmmac_priv, ptp_clock_ops); +	unsigned long flags; +	u32 diff, addend; +	int neg_adj = 0; +	u64 adj; + +	if (ppb < 0) { +		neg_adj = 1; +		ppb = -ppb; +	} + +	addend = priv->default_addend; +	adj = addend; +	adj *= ppb; +	diff = div_u64(adj, 1000000000ULL); +	addend = neg_adj ? (addend - diff) : (addend + diff); + +	spin_lock_irqsave(&priv->ptp_lock, flags); + +	priv->hw->ptp->config_addend(priv->ioaddr, addend); + +	spin_unlock_irqrestore(&priv->lock, flags); + +	return 0; +} + +/** + * stmmac_adjust_time + * + * @ptp: pointer to ptp_clock_info structure + * @delta: desired change in nanoseconds + * + * Description: this function will shift/adjust the hardware clock time. + */ +static int stmmac_adjust_time(struct ptp_clock_info *ptp, s64 delta) +{ +	struct stmmac_priv *priv = +	    container_of(ptp, struct stmmac_priv, ptp_clock_ops); +	unsigned long flags; +	u32 sec, nsec; +	u32 quotient, reminder; +	int neg_adj = 0; + +	if (delta < 0) { +		neg_adj = 1; +		delta = -delta; +	} + +	quotient = div_u64_rem(delta, 1000000000ULL, &reminder); +	sec = quotient; +	nsec = reminder; + +	spin_lock_irqsave(&priv->ptp_lock, flags); + +	priv->hw->ptp->adjust_systime(priv->ioaddr, sec, nsec, neg_adj); + +	spin_unlock_irqrestore(&priv->lock, flags); + +	return 0; +} + +/** + * stmmac_get_time + * + * @ptp: pointer to ptp_clock_info structure + * @ts: pointer to hold time/result + * + * Description: this function will read the current time from the + * hardware clock and store it in @ts. + */ +static int stmmac_get_time(struct ptp_clock_info *ptp, struct timespec *ts) +{ +	struct stmmac_priv *priv = +	    container_of(ptp, struct stmmac_priv, ptp_clock_ops); +	unsigned long flags; +	u64 ns; +	u32 reminder; + +	spin_lock_irqsave(&priv->ptp_lock, flags); + +	ns = priv->hw->ptp->get_systime(priv->ioaddr); + +	spin_unlock_irqrestore(&priv->ptp_lock, flags); + +	ts->tv_sec = div_u64_rem(ns, 1000000000ULL, &reminder); +	ts->tv_nsec = reminder; + +	return 0; +} + +/** + * stmmac_set_time + * + * @ptp: pointer to ptp_clock_info structure + * @ts: time value to set + * + * Description: this function will set the current time on the + * hardware clock. + */ +static int stmmac_set_time(struct ptp_clock_info *ptp, +			   const struct timespec *ts) +{ +	struct stmmac_priv *priv = +	    container_of(ptp, struct stmmac_priv, ptp_clock_ops); +	unsigned long flags; + +	spin_lock_irqsave(&priv->ptp_lock, flags); + +	priv->hw->ptp->init_systime(priv->ioaddr, ts->tv_sec, ts->tv_nsec); + +	spin_unlock_irqrestore(&priv->ptp_lock, flags); + +	return 0; +} + +static int stmmac_enable(struct ptp_clock_info *ptp, +			 struct ptp_clock_request *rq, int on) +{ +	return -EOPNOTSUPP; +} + +/* structure describing a PTP hardware clock */ +static struct ptp_clock_info stmmac_ptp_clock_ops = { +	.owner = THIS_MODULE, +	.name = "stmmac_ptp_clock", +	.max_adj = 62500000, +	.n_alarm = 0, +	.n_ext_ts = 0, +	.n_per_out = 0, +	.pps = 0, +	.adjfreq = stmmac_adjust_freq, +	.adjtime = stmmac_adjust_time, +	.gettime = stmmac_get_time, +	.settime = stmmac_set_time, +	.enable = stmmac_enable, +}; + +/** + * stmmac_ptp_register + * + * @ndev: net device pointer + * + * Description: this function will register the ptp clock driver + * to kernel. It also does some house keeping work. + */ +int stmmac_ptp_register(struct stmmac_priv *priv) +{ +	spin_lock_init(&priv->ptp_lock); +	priv->ptp_clock_ops = stmmac_ptp_clock_ops; + +	priv->ptp_clock = ptp_clock_register(&priv->ptp_clock_ops, +					     priv->device); +	if (IS_ERR(priv->ptp_clock)) { +		priv->ptp_clock = NULL; +		pr_err("ptp_clock_register() failed on %s\n", priv->dev->name); +	} else +		pr_debug("Added PTP HW clock successfully on %s\n", +			 priv->dev->name); + +	return 0; +} + +/** + * stmmac_ptp_unregister + * + * @ndev: net device pointer + * + * Description: this function will remove/unregister the ptp clock driver + * from the kernel. + */ +void stmmac_ptp_unregister(struct stmmac_priv *priv) +{ +	if (priv->ptp_clock) { +		ptp_clock_unregister(priv->ptp_clock); +		pr_debug("Removed PTP HW clock successfully on %s\n", +			 priv->dev->name); +	} +} 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 00000000000..3dbc047622f --- /dev/null +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_ptp.h @@ -0,0 +1,74 @@ +/****************************************************************************** +  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 */ + +#define PTP_STNSUR_ADDSUB_SHIFT 31 + +/* 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__ */  |