aboutsummaryrefslogtreecommitdiffstats
path: root/recipes-kernel/linux/files/0013-Quark-stmmac-Ethernet-quark.patch
diff options
context:
space:
mode:
Diffstat (limited to 'recipes-kernel/linux/files/0013-Quark-stmmac-Ethernet-quark.patch')
-rw-r--r--recipes-kernel/linux/files/0013-Quark-stmmac-Ethernet-quark.patch2219
1 files changed, 2219 insertions, 0 deletions
diff --git a/recipes-kernel/linux/files/0013-Quark-stmmac-Ethernet-quark.patch b/recipes-kernel/linux/files/0013-Quark-stmmac-Ethernet-quark.patch
new file mode 100644
index 0000000..99ce791
--- /dev/null
+++ b/recipes-kernel/linux/files/0013-Quark-stmmac-Ethernet-quark.patch
@@ -0,0 +1,2219 @@
+From xxxx Mon Sep 17 00:00:00 2001
+From: Krzysztof Sywula <krzysztof.m.sywula@intel.com>
+Date: Thu, 20 Feb 2014 15:16:30 +0000
+Subject: [PATCH 13/21] Quark stmmac Ethernet
+
+---
+ drivers/net/ethernet/stmicro/stmmac/Kconfig | 25 +-
+ drivers/net/ethernet/stmicro/stmmac/Makefile | 1 +
+ drivers/net/ethernet/stmicro/stmmac/common.h | 14 +-
+ drivers/net/ethernet/stmicro/stmmac/descs.h | 45 ++-
+ drivers/net/ethernet/stmicro/stmmac/dwmac1000.h | 64 ++-
+ .../net/ethernet/stmicro/stmmac/dwmac1000_core.c | 139 ++++-
+ .../net/ethernet/stmicro/stmmac/dwmac1000_dma.c | 9 +-
+ .../net/ethernet/stmicro/stmmac/dwmac100_core.c | 6 +-
+ drivers/net/ethernet/stmicro/stmmac/mmc_core.c | 1 +
+ drivers/net/ethernet/stmicro/stmmac/stmmac.h | 76 ++
+ .../net/ethernet/stmicro/stmmac/stmmac_ethtool.c | 21 +-
+ drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 163 ++++-
+ drivers/net/ethernet/stmicro/stmmac/stmmac_pci.c | 263 +++++++-
+ drivers/net/ethernet/stmicro/stmmac/stmmac_ptp.c | 723 ++++++++++++++++++++
+ 14 files changed, 1476 insertions(+), 74 deletions(-)
+ create mode 100644 drivers/net/ethernet/stmicro/stmmac/stmmac_ptp.c
+
+diff --git a/drivers/net/ethernet/stmicro/stmmac/Kconfig b/drivers/net/ethernet/stmicro/stmmac/Kconfig
+index 1164930..c959209 100644
+--- a/drivers/net/ethernet/stmicro/stmmac/Kconfig
++++ b/drivers/net/ethernet/stmicro/stmmac/Kconfig
+@@ -26,8 +26,8 @@ config STMMAC_PLATFORM
+ If unsure, say N.
+
+ config STMMAC_PCI
+- bool "STMMAC PCI bus support (EXPERIMENTAL)"
+- depends on STMMAC_ETH && PCI && EXPERIMENTAL
++ bool "STMMAC PCI bus support"
++ depends on STMMAC_ETH && PCI
+ ---help---
+ This is to select the Synopsys DWMAC available on PCI devices,
+ if you have a controller with this interface, say Y or M here.
+@@ -54,6 +54,27 @@ config STMMAC_DA
+ By default, the DMA arbitration scheme is based on Round-robin
+ (rx:tx priority is 1:1).
+
++config STMMAC_PTP
++ bool "STMMAC PTP (1588-2005) Clock Support"
++ default n
++ depends on EXPERIMENTAL
++ select PPS
++ select PTP_1588_CLOCK
++ ---help---
++ Say Y here if you want support for 1588 Timestamping with a
++ Quark device, using the PTP 1588 Clock support. This is
++ required to enable timestamping support for the device.
++
++ If unsure, say N.
++
++config STMMAC_PTP_CLK_MHZ
++ depends on STMMAC_PTP
++ int "Reference clock in Mhz"
++ default 50
++ ---help---
++ Frequency if MHz of the reference clock used to derive PTP time
++ locally. Permissable values are 1 - 255 inclusive
++
+ choice
+ prompt "Select the DMA TX/RX descriptor operating modes"
+ depends on STMMAC_ETH
+diff --git a/drivers/net/ethernet/stmicro/stmmac/Makefile b/drivers/net/ethernet/stmicro/stmmac/Makefile
+index c8e8ea6..0995db5 100644
+--- a/drivers/net/ethernet/stmicro/stmmac/Makefile
++++ b/drivers/net/ethernet/stmicro/stmmac/Makefile
+@@ -3,6 +3,7 @@ stmmac-$(CONFIG_STMMAC_RING) += ring_mode.o
+ stmmac-$(CONFIG_STMMAC_CHAINED) += chain_mode.o
+ stmmac-$(CONFIG_STMMAC_PLATFORM) += stmmac_platform.o
+ stmmac-$(CONFIG_STMMAC_PCI) += stmmac_pci.o
++stmmac-$(CONFIG_STMMAC_PTP) += stmmac_ptp.o
+ stmmac-objs:= stmmac_main.o stmmac_ethtool.o stmmac_mdio.o \
+ dwmac_lib.o dwmac1000_core.o dwmac1000_dma.o \
+ dwmac100_core.o dwmac100_dma.o enh_desc.o norm_desc.o \
+diff --git a/drivers/net/ethernet/stmicro/stmmac/common.h b/drivers/net/ethernet/stmicro/stmmac/common.h
+index 186d148..ad16e73 100644
+--- a/drivers/net/ethernet/stmicro/stmmac/common.h
++++ b/drivers/net/ethernet/stmicro/stmmac/common.h
+@@ -32,9 +32,15 @@
+ #include <linux/init.h>
+ #if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE)
+ #define STMMAC_VLAN_TAG_USED
++#if defined(CONFIG_INTEL_QUARK_X1000_SOC)
++#define STMMAC_VLAN_HASH
++#endif
+ #include <linux/if_vlan.h>
+ #endif
+
++#if defined(STMMAC_VLAN_HASH) || defined(CONFIG_STMMAC_PTP)
++#define STMMAC_ATDS_USED
++#endif
+ #include "descs.h"
+ #include "mmc.h"
+
+@@ -319,15 +325,16 @@ struct stmmac_dma_ops {
+ void (*rx_watchdog) (void __iomem *ioaddr, u32 riwt);
+ };
+
++struct stmmac_priv;
+ struct stmmac_ops {
+ /* MAC core initialization */
+ void (*core_init) (void __iomem *ioaddr) ____cacheline_aligned;
+ /* Enable and verify that the IPC module is supported */
+- int (*rx_ipc) (void __iomem *ioaddr);
++ int (*set_rx_ipc) (void __iomem *ioaddr, bool on);
+ /* 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) (struct stmmac_priv * priv);
+ /* Multicast filter setting */
+ void (*set_filter) (struct net_device *dev, int id);
+ /* Flow control setting */
+@@ -340,6 +347,9 @@ struct stmmac_ops {
+ unsigned int reg_n);
+ void (*get_umac_addr) (void __iomem *ioaddr, unsigned char *addr,
+ unsigned int reg_n);
++ /* Enable/Disable VLAN Hash filters */
++ int (*vlan_rx_add_vid)(struct stmmac_priv *priv, unsigned short vid);
++ int (*vlan_rx_kill_vid)(struct stmmac_priv *priv, unsigned short vid);
+ void (*set_eee_mode) (void __iomem *ioaddr);
+ void (*reset_eee_mode) (void __iomem *ioaddr);
+ void (*set_eee_timer) (void __iomem *ioaddr, int ls, int tw);
+diff --git a/drivers/net/ethernet/stmicro/stmmac/descs.h b/drivers/net/ethernet/stmicro/stmmac/descs.h
+index 223adf9..ce08163 100644
+--- a/drivers/net/ethernet/stmicro/stmmac/descs.h
++++ b/drivers/net/ethernet/stmicro/stmmac/descs.h
+@@ -25,8 +25,8 @@
+ #define __DESCS_H__
+
+ struct dma_desc {
+- /* Receive descriptor */
+ union {
++ /* Receive descriptor */
+ struct {
+ /* RDES0 */
+ u32 payload_csum_error:1;
+@@ -160,6 +160,49 @@ struct dma_desc {
+ } des01;
+ unsigned int des2;
+ unsigned int des3;
++
++ /* Enhanced mode - with VLAN/1588-2005/IPC CHKSUM offload */
++ #if defined(STMMAC_ATDS_USED)
++ union {
++ /* Receive descriptor */
++ struct {
++ /* RDES4 */
++ u32 ip_payload_type:3;
++ u32 ip_header_error:1;
++ u32 ip_payload_error:1;
++ u32 ip_checksum_bypassed:1;
++ u32 ipv4_packet_received:1;
++ u32 ipv6_packet_received:1;
++ u32 message_type:4;
++ u32 ptp_frame_type:1;
++ u32 ptp_version:1;
++ u32 timestamp_dropped:1;
++ u32 reserved1:1;
++ u32 av_packet_received:1;
++ u32 av_tagged_packet_received:1;
++ u32 vlan_tag_priority_value:3;
++ u32 reserved2:3;
++ u32 layer3_filter_match:1;
++ u32 layer4_filter_match:1;
++ u32 layer3_layer4_filter_num_matched:2;
++ u32 reserved3:4;
++
++ /* RDES5 */
++ u32 reserved4;
++ }erx;
++
++ /* Transmit descriptor */
++ struct {
++ /* TDES4 */
++ u32 reserved1;
++
++ /* TDES5 */
++ u32 reserved2;
++ } etx;
++ } des05;
++ unsigned int ts_lo; /* des6 Tx/Rx timestmp lo */
++ unsigned int ts_hi; /* des7 Tx/Rx timestamp hi */
++ #endif
+ };
+
+ /* Transmit checksum insertion control */
+diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac1000.h b/drivers/net/ethernet/stmicro/stmmac/dwmac1000.h
+index 7ad56af..3042098 100644
+--- a/drivers/net/ethernet/stmicro/stmmac/dwmac1000.h
++++ b/drivers/net/ethernet/stmicro/stmmac/dwmac1000.h
+@@ -50,7 +50,14 @@ enum dwmac1000_irq_status {
+ rgmii_irq = 0x0001,
+ };
+ #define GMAC_INT_MASK 0x0000003c /* interrupt mask register */
+-
++#define GMAC_INT_MASK_LPIIM 0x00000200 /* LPI Interrupt Mask */
++#define GMAC_INT_MASK_TSIM 0x00000100 /* Timestamp Interrupt Mask */
++#define GMAC_INT_MASK_PMTIM 0x00000004 /* PMT Interrupt Mask */
++#define GMAC_INT_MASK_PCSANCIM 0x00000002 /* PCS AN Completion */
++#define GMAC_INT_MASK_PCSLCHGIM 0x00000001 /* PCS Link Status */
++#define GMAC_INT_MASK_DEFAULT GMAC_INT_MASK_PCSLCHGIM | GMAC_INT_MASK_PCSANCIM\
++ | GMAC_INT_MASK_PMTIM | GMAC_INT_MASK_TSIM\
++ | GMAC_INT_MASK_LPIIM
+ /* PMT Control and Status */
+ #define GMAC_PMT 0x0000002c
+ enum power_event {
+@@ -135,6 +142,7 @@ enum inter_frame_gap {
+ #define GMAC_FRAME_FILTER_SAIF 0x00000100 /* Inverse Filtering */
+ #define GMAC_FRAME_FILTER_SAF 0x00000200 /* Source Address Filter */
+ #define GMAC_FRAME_FILTER_HPF 0x00000400 /* Hash or perfect Filter */
++#define GMAC_FRAME_FILTER_VTFE 0x00010000 /* VLAN Tag Filter Enable */
+ #define GMAC_FRAME_FILTER_RA 0x80000000 /* Receive all mode */
+ /* GMII ADDR defines */
+ #define GMAC_MII_ADDR_WRITE 0x00000002 /* MII Write */
+@@ -145,11 +153,17 @@ enum inter_frame_gap {
+ #define GMAC_FLOW_CTRL_RFE 0x00000004 /* Rx Flow Control Enable */
+ #define GMAC_FLOW_CTRL_TFE 0x00000002 /* Tx Flow Control Enable */
+ #define GMAC_FLOW_CTRL_FCB_BPA 0x00000001 /* Flow Control Busy ... */
+-
++/* GMAC VLAN TAG defines */
++#define GMAC_VLAN_TAG_VTHM 0x00080000 /* Hash Table Match Enable */
++#define GMAC_VLAN_TAG_ESVL 0x00040000 /* Enable S-VLAN */
++#define GMAC_VLAN_TAG_VTIM 0x00020000 /* VLAN Tag inverse match */
++#define GMAC_VLAN_TAG_ETV 0x00010000 /* Enable 12-bit tag comp */
++#define GMAC_VLAN_TAG_VLMASK 0x0000FFFF /* VLAN tag ID for Rx frames */
+ /*--- DMA BLOCK defines ---*/
+ /* DMA Bus Mode register defines */
+ #define DMA_BUS_MODE_SFT_RESET 0x00000001 /* Software Reset */
+ #define DMA_BUS_MODE_DA 0x00000002 /* Arbitration scheme */
++#define DMA_BUS_MODE_ATDS 0X00000080 /* Alternate Descriptor Size */
+ #define DMA_BUS_MODE_DSL_MASK 0x0000007c /* Descriptor Skip Length */
+ #define DMA_BUS_MODE_DSL_SHIFT 2 /* (in DWORDS) */
+ /* Programmable burst length (passed thorugh platform)*/
+@@ -169,6 +183,7 @@ enum rx_tx_priority_ratio {
+ #define DMA_BUS_MODE_USP 0x00800000
+ #define DMA_BUS_MODE_PBL 0x01000000
+ #define DMA_BUS_MODE_AAL 0x02000000
++#define DMA_BUS_MODE_RIX 0x80000000
+
+ /* DMA CRS Control and Status Register Mapping */
+ #define DMA_HOST_TX_DESC 0x00001048 /* Current Host Tx descriptor */
+@@ -230,5 +245,50 @@ enum rtc_control {
+ #define GMAC_MMC_TX_INTR 0x108
+ #define GMAC_MMC_RX_CSUM_OFFLOAD 0x208
+
++/* VLAN Hash register offset */
++#define GMAC_VLAN_TAG_REP 0x584
++#define GMAC_VLAN_HASH 0x588
++#define GMAC_VLAN_HASH_MAXID 0x0F
++
++/***************** 1588 regs *****************/
++#define GMAC_TS_CTRL 0x700 /* Timestamp control reg */
++#define GMAC_TS_CTRL_TSENA 0x00000001 /* Timestamp enable */
++#define GMAC_TS_CTRL_TSCFUPDT 0x00000002 /* Timestamp fine/coarse */
++#define GMAC_TS_CTRL_TSINT 0x00000004 /* Timestamp initialise */
++#define GMAC_TS_CTRL_TSUPDT 0x00000008 /* Timestamp update */
++#define GMAC_TS_CTRL_TSTRIG 0x00000010 /* Timestamp trigger en */
++#define GMAC_TS_CTRL_TSADDREG 0x00000020 /* Timestamp addreg update */
++#define GMAC_TS_CTRL_TSENALL 0x00000100 /* Timestamp RX enable all */
++#define GMAC_TS_CTRL_TSCTRLSSR 0x00000200 /* Timestamp rollover ctr */
++#define GMAC_TS_CTRL_TSVER2ENA 0x00000400 /* Timestamp PTP v2 en */
++#define GMAC_TS_CTRL_TSIPENA 0x00000800 /* Timestamp PTP over eth */
++#define GMAC_TS_CTRL_TSIPV6ENA 0x00001000 /* Timestamp over IPV6 */
++#define GMAC_TS_CTRL_TSIPV4ENA 0x00002000 /* Timestamp over IPV4 */
++#define GMAC_TS_CTRL_TSEVNTENA 0x00004000 /* Timestamp event only */
++#define GMAC_TS_CTRL_TSMSTRENA 0x00008000 /* Timestamp master enable */
++#define GMAC_TS_CTRL_SNTYPSEL0 0x00000000 /* Timestamp type 0 snapshot */
++#define GMAC_TS_CTRL_SNTYPSEL1 0x00010000 /* Timestamp type 1 snapshot */
++#define GMAC_TS_CTRL_SNTYPSEL2 0x00020000 /* Timestamp type 2 snapshot */
++#define GMAC_TS_CTRL_SNTYPSEL3 0x00030000 /* Timestamp type 3 snapshot */
++#define GMAC_TS_CTRL_TSENMACADR 0x00040000 /* Timestamp mac filter en */
++#define GMAC_TS_CTRL_ATSFC 0x01000000 /* Timestamp aux fifo clear */
++#define GMAC_TS_CTRL_ATSEN0 0x02000000 /* Timestamp aux0 snap en */
++#define GMAC_TS_CTRL_ATSEN1 0x04000000 /* Timestamp aux1 snap en */
++#define GMAC_TS_CTRL_ATSEN2 0x08000000 /* Timestamp aux2 snap en */
++#define GMAC_TS_CTRL_ATSEN3 0x10000000 /* Timestamp aux3 enable */
++#define GMAC_SS_INC 0x704 /* Sub-second increment reg */
++#define GMAC_ST_SEC 0x708 /* System time seconds */
++#define GMAC_ST_NSEC 0x70C /* System time nseconds */
++#define GMAC_ST_SECUP 0x710 /* System time sec-update */
++#define GMAC_ST_NSECUP 0x714 /* System time nsec-update */
++#define GMAC_TS_APPEND 0x718 /* Timestamp append */
++#define GMAC_TT_SEC 0x71C /* Target time seconds */
++#define GMAC_TT_NSEC 0x720 /* Target time nseconds */
++#define GMAC_ST_HWSEC 0x724 /* System time high word sec */
++#define GMAC_ST_TS_STAT 0x728 /* Timestamp status */
++#define GMAC_PPS_CTRL 0x72C /* PPS signal output control */
++#define GMAC_AUXTS_NSEC 0x730 /* Aux timestamp counter nsec */
++#define GMAC_AUXTS_SEC 0x734 /* Aux timestamp counter sec */
++
+ 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 bfe0226..b6d04ca 100644
+--- a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c
++++ b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c
+@@ -30,6 +30,7 @@
+ #include <linux/slab.h>
+ #include <asm/io.h>
+ #include "dwmac1000.h"
++#include "stmmac.h"
+
+ static void dwmac1000_core_init(void __iomem *ioaddr)
+ {
+@@ -38,7 +39,7 @@ static void dwmac1000_core_init(void __iomem *ioaddr)
+ writel(value, ioaddr + GMAC_CONTROL);
+
+ /* Mask GMAC interrupts */
+- writel(0x207, ioaddr + GMAC_INT_MASK);
++ writel(GMAC_INT_MASK_DEFAULT, ioaddr + GMAC_INT_MASK);
+
+ #ifdef STMMAC_VLAN_TAG_USED
+ /* Tag detection without filtering */
+@@ -46,11 +47,15 @@ static void dwmac1000_core_init(void __iomem *ioaddr)
+ #endif
+ }
+
+-static int dwmac1000_rx_ipc_enable(void __iomem *ioaddr)
++static int dwmac1000_set_rx_ipc(void __iomem *ioaddr, bool on)
+ {
+ u32 value = readl(ioaddr + GMAC_CONTROL);
+
+- value |= GMAC_CONTROL_IPC;
++ if(on == true){
++ value |= GMAC_CONTROL_IPC;
++ }else{
++ value &= ~GMAC_CONTROL_IPC;
++ }
+ writel(value, ioaddr + GMAC_CONTROL);
+
+ value = readl(ioaddr + GMAC_CONTROL);
+@@ -87,6 +92,7 @@ static void dwmac1000_get_umac_addr(void __iomem *ioaddr, unsigned char *addr,
+ static void dwmac1000_set_filter(struct net_device *dev, int id)
+ {
+ void __iomem *ioaddr = (void __iomem *) dev->base_addr;
++ struct stmmac_priv *priv = netdev_priv(dev);
+ unsigned int value = 0;
+ unsigned int perfect_addr_number;
+
+@@ -147,6 +153,10 @@ static void dwmac1000_set_filter(struct net_device *dev, int id)
+ /* Enable Receive all mode (to debug filtering_fail errors) */
+ value |= GMAC_FRAME_FILTER_RA;
+ #endif
++ if (priv->active_vlans != 0){
++ /* VLAN hash filtering is active on this interface */
++ value |= GMAC_FRAME_FILTER_VTFE;
++ }
+ writel(value, ioaddr + GMAC_FRAME_FILTER);
+
+ CHIP_DBG(KERN_INFO "\tFrame Filter reg: 0x%08x\n\tHash regs: "
+@@ -194,38 +204,42 @@ static void dwmac1000_pmt(void __iomem *ioaddr, unsigned long mode)
+ }
+
+
+-static int dwmac1000_irq_status(void __iomem *ioaddr)
++#ifndef CONFIG_STMMAC_PTP
++#define stmmac_ptp_check_pps_event(x){}
++#endif
++
++static int dwmac1000_irq_status(struct stmmac_priv *priv)
+ {
+- u32 intr_status = readl(ioaddr + GMAC_INT_STATUS);
++ u32 intr_status = readl(priv->ioaddr + GMAC_INT_STATUS);
+ int status = 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));
++ readl(priv->ioaddr + GMAC_MMC_TX_INTR));
+ status |= core_mmc_tx_irq;
+ }
+ if (unlikely(intr_status & mmc_rx_irq)) {
+ CHIP_DBG(KERN_INFO "GMAC: MMC rx interrupt: 0x%08x\n",
+- readl(ioaddr + GMAC_MMC_RX_INTR));
++ readl(priv->ioaddr + GMAC_MMC_RX_INTR));
+ status |= core_mmc_rx_irq;
+ }
+ 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));
++ readl(priv->ioaddr + GMAC_MMC_RX_CSUM_OFFLOAD));
+ status |= core_mmc_rx_csum_offload_irq;
+ }
+ 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);
++ readl(priv->ioaddr + GMAC_PMT);
+ status |= core_irq_receive_pmt_irq;
+ }
+ /* 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);
++ u32 lpi_status = readl(priv->ioaddr + LPI_CTRL_STATUS);
+
+ if (lpi_status & LPI_CTRL_STATUS_TLPIEN) {
+ CHIP_DBG(KERN_INFO "GMAC TX entered in LPI\n");
+@@ -244,10 +258,110 @@ static int dwmac1000_irq_status(void __iomem *ioaddr)
+ status |= core_irq_rx_path_exit_lpi_mode;
+ }
+ }
++ if (unlikely(intr_status & time_stamp_irq)){
++ stmmac_ptp_check_pps_event(priv);
++ }
+
+ return status;
+ }
+
++static unsigned int dwmac1000_calc_vlan_4bit_crc ( const char * vlan )
++{
++ int i = 0, j = 0, len = 0, bit = 0;
++ unsigned int crc = 0xFFFFFFFF;
++ unsigned int poly = 0x04C11DB7;
++ unsigned char data;
++
++ if(unlikely(vlan == NULL)){
++ return 0;
++ }
++
++ for( i = 0; i < 2; i++ ) {
++ data = vlan[i];
++
++ if (i==0){
++ len = 8;
++ }else{
++ len = 4;
++ }
++
++ for( bit = 0; bit < len; bit++ ) {
++
++ j = ((crc >> 31) ^ data) & 0x1;
++ crc <<= 1;
++
++ if( j != 0 ){
++ crc ^= poly;
++ }
++
++ data >>= 1;
++ }
++ }
++ return crc;
++
++}
++
++static int dwmac1000_vlan_rx_add_vid(struct stmmac_priv *priv, unsigned short vid)
++{
++ u32 reg = 0;
++ u32 bit_nr = 0;
++
++ if(unlikely(priv == NULL || vid > GMAC_VLAN_HASH_MAXID)){
++ return -EINVAL;
++ }
++
++ if(priv->active_vlans == 0){
++
++ /* Flip the VTFE bit in GMAC_FRAME_FILTER */
++ reg = readl(priv->ioaddr + GMAC_FRAME_FILTER);
++ reg |= GMAC_FRAME_FILTER_VTFE;
++ writel(reg, priv->ioaddr + GMAC_FRAME_FILTER);
++
++ /* Enable hash filtering - based on 12 bit vid */
++ reg = readl(priv->ioaddr + GMAC_VLAN_TAG);
++ reg |= GMAC_VLAN_TAG_VTHM | GMAC_VLAN_TAG_ETV | 0x0000FFFF;
++ writel(reg, priv->ioaddr + GMAC_VLAN_TAG);
++ }
++
++ bit_nr = (~dwmac1000_calc_vlan_4bit_crc((const char*)&vid)) >> 28;
++ priv->active_vlans |= 1 << bit_nr;
++
++ writel(priv->active_vlans, priv->ioaddr + GMAC_VLAN_HASH);
++
++ return 0;
++}
++
++static int dwmac1000_vlan_rx_kill_vid(struct stmmac_priv *priv, unsigned short vid)
++{
++ u32 reg = 0;
++ u32 bit_nr = 0;
++
++ if(unlikely(priv == NULL || vid > GMAC_VLAN_HASH_MAXID)){
++ return -EINVAL;
++ }
++
++ bit_nr = (~dwmac1000_calc_vlan_4bit_crc((const char*)&vid)) >> 28;
++
++ priv->active_vlans &= ~(1 << bit_nr);
++ writel(priv->active_vlans, priv->ioaddr + GMAC_VLAN_HASH);
++
++ if(priv->active_vlans == 0){
++
++ /* Disable hash filtering */
++ reg = readl(priv->ioaddr + GMAC_VLAN_TAG);
++ reg &= ~(GMAC_VLAN_TAG_VTHM | GMAC_VLAN_TAG_ETV | 0x00000001);
++ writel(reg, priv->ioaddr + GMAC_VLAN_TAG);
++
++ /* Flip the VTFE bit in GMAC_FRAME_FILTER */
++ reg = readl(priv->ioaddr + GMAC_FRAME_FILTER);
++ reg &= ~GMAC_FRAME_FILTER_VTFE;
++ writel(reg, priv->ioaddr + GMAC_FRAME_FILTER);
++
++ }
++
++ return 0;
++}
++
+ static void dwmac1000_set_eee_mode(void __iomem *ioaddr)
+ {
+ u32 value;
+@@ -297,9 +411,10 @@ static void dwmac1000_set_eee_timer(void __iomem *ioaddr, int ls, int tw)
+ writel(value, ioaddr + LPI_TIMER_CTRL);
+ }
+
++
+ static const struct stmmac_ops dwmac1000_ops = {
+ .core_init = dwmac1000_core_init,
+- .rx_ipc = dwmac1000_rx_ipc_enable,
++ .set_rx_ipc = dwmac1000_set_rx_ipc,
+ .dump_regs = dwmac1000_dump_regs,
+ .host_irq_status = dwmac1000_irq_status,
+ .set_filter = dwmac1000_set_filter,
+@@ -307,6 +422,8 @@ static const struct stmmac_ops dwmac1000_ops = {
+ .pmt = dwmac1000_pmt,
+ .set_umac_addr = dwmac1000_set_umac_addr,
+ .get_umac_addr = dwmac1000_get_umac_addr,
++ .vlan_rx_add_vid = dwmac1000_vlan_rx_add_vid,
++ .vlan_rx_kill_vid = dwmac1000_vlan_rx_kill_vid,
+ .set_eee_mode = dwmac1000_set_eee_mode,
+ .reset_eee_mode = dwmac1000_reset_eee_mode,
+ .set_eee_timer = dwmac1000_set_eee_timer,
+diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c
+index bf83c03..a0c08e1 100644
+--- a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c
++++ b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c
+@@ -59,9 +59,12 @@ static int dwmac1000_dma_init(void __iomem *ioaddr, int pbl, int fb,
+ * DMA transfers the data in 8, 16, 32, 64, 128 & 256 beats
+ * depending on pbl value.
+ */
++#ifdef CONFIG_INTEL_QUARK_X1000_SOC
++ value = DMA_BUS_MODE_RIX | (pbl << DMA_BUS_MODE_PBL_SHIFT);
++#else
+ value = DMA_BUS_MODE_PBL | ((pbl << DMA_BUS_MODE_PBL_SHIFT) |
+ (pbl << DMA_BUS_MODE_RPBL_SHIFT));
+-
++#endif
+ /* Set the Fixed burst mode */
+ if (fb)
+ value |= DMA_BUS_MODE_FB;
+@@ -70,6 +73,10 @@ static int dwmac1000_dma_init(void __iomem *ioaddr, int pbl, int fb,
+ if (mb)
+ value |= DMA_BUS_MODE_MB;
+
++#if defined(STMMAC_ATDS_USED)
++ value |= DMA_BUS_MODE_ATDS;
++#endif
++
+ #ifdef CONFIG_STMMAC_DA
+ value |= DMA_BUS_MODE_DA; /* Rx has priority over tx */
+ #endif
+diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac100_core.c b/drivers/net/ethernet/stmicro/stmmac/dwmac100_core.c
+index f83210e..43472c0 100644
+--- a/drivers/net/ethernet/stmicro/stmmac/dwmac100_core.c
++++ b/drivers/net/ethernet/stmicro/stmmac/dwmac100_core.c
+@@ -67,12 +67,12 @@ static void dwmac100_dump_mac_regs(void __iomem *ioaddr)
+ readl(ioaddr + MAC_VLAN2));
+ }
+
+-static int dwmac100_rx_ipc_enable(void __iomem *ioaddr)
++static int dwmac100_set_rx_ipc(void __iomem *ioaddr, bool on)
+ {
+ return 0;
+ }
+
+-static int dwmac100_irq_status(void __iomem *ioaddr)
++static int dwmac100_irq_status(struct stmmac_priv *priv)
+ {
+ return 0;
+ }
+@@ -160,7 +160,7 @@ static void dwmac100_pmt(void __iomem *ioaddr, unsigned long mode)
+
+ static const struct stmmac_ops dwmac100_ops = {
+ .core_init = dwmac100_core_init,
+- .rx_ipc = dwmac100_rx_ipc_enable,
++ .set_rx_ipc = dwmac100_set_rx_ipc,
+ .dump_regs = dwmac100_dump_mac_regs,
+ .host_irq_status = dwmac100_irq_status,
+ .set_filter = dwmac100_set_filter,
+diff --git a/drivers/net/ethernet/stmicro/stmmac/mmc_core.c b/drivers/net/ethernet/stmicro/stmmac/mmc_core.c
+index 0c74a70..50617c5 100644
+--- a/drivers/net/ethernet/stmicro/stmmac/mmc_core.c
++++ b/drivers/net/ethernet/stmicro/stmmac/mmc_core.c
+@@ -149,6 +149,7 @@ void dwmac_mmc_intr_all_mask(void __iomem *ioaddr)
+ {
+ writel(MMC_DEFAULT_MASK, ioaddr + MMC_RX_INTR_MASK);
+ writel(MMC_DEFAULT_MASK, ioaddr + MMC_TX_INTR_MASK);
++ writel(MMC_DEFAULT_MASK, ioaddr + MMC_RX_IPC_INTR_MASK);
+ }
+
+ /* This reads the MAC core counters (if actaully supported).
+diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac.h b/drivers/net/ethernet/stmicro/stmmac/stmmac.h
+index b05df89..611f70e 100644
+--- a/drivers/net/ethernet/stmicro/stmmac/stmmac.h
++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac.h
+@@ -27,9 +27,11 @@
+ #define DRV_MODULE_VERSION "Nov_2012"
+
+ #include <linux/clk.h>
++#include <linux/clocksource.h>
+ #include <linux/stmmac.h>
+ #include <linux/phy.h>
+ #include <linux/pci.h>
++#include <linux/ptp_clock_kernel.h>
+ #include "common.h"
+
+ struct stmmac_priv {
+@@ -72,8 +74,22 @@ struct stmmac_priv {
+ u32 msg_enable;
+ spinlock_t lock;
+ spinlock_t tx_lock;
++
++ /* PTP */
++ struct ptp_clock *ptp_clock;
++ struct ptp_clock_info ptp_caps;
++ struct delayed_work overflow_work;
++ spinlock_t tmreg_lock;
++ struct cyclecounter ccnt;
++ struct timecounter tcnt;
++// struct timecompare tcmp;
++ int hwts;
++ struct stmmac_timer *tm;
++
+ int wolopts;
+ int wol_irq;
++
++ int active_vlans;
+ struct plat_stmmacenet_data *plat;
+ struct stmmac_counters mmc;
+ struct dma_features dma_cap;
+@@ -81,6 +97,8 @@ struct stmmac_priv {
+ struct clk *stmmac_clk;
+ int clk_csr;
+ int synopsys_id;
++ int irqmode_msi;
++ struct pci_dev * pdev;
+ struct timer_list eee_ctrl_timer;
+ bool tx_path_in_lpi_mode;
+ int lpi_irq;
+@@ -110,6 +128,63 @@ int stmmac_dvr_remove(struct net_device *ndev);
+ struct stmmac_priv *stmmac_dvr_probe(struct device *device,
+ struct plat_stmmacenet_data *plat_dat,
+ void __iomem *addr);
++#ifdef CONFIG_STMMAC_PTP
++
++#define STMMAC_PTP_OVERFLOW_CHECK_ENABLED (u32)(1)
++#define STMMAC_PTP_PPS_ENABLED (u32)(1 << 1)
++#define STMMAC_PTP_HWTS_TX_EN (u32)(1 << 2)
++#define STMMAC_PTP_HWTS_RX_EN (u32)(1 << 3)
++
++extern void stmmac_ptp_init(struct net_device *ndev, struct device * pdev);
++extern void stmmac_ptp_remove(struct stmmac_priv *priv);
++extern int stmmac_ptp_hwtstamp_ioctl(struct stmmac_priv *priv,
++ struct ifreq *ifr, int cmd);
++extern void stmmac_ptp_rx_hwtstamp(struct stmmac_priv *priv, struct dma_desc *pdma,
++ struct sk_buff *skb);
++extern void stmmac_ptp_tx_hwtstamp(struct stmmac_priv *priv, struct dma_desc *pdma,
++ struct sk_buff *skb);
++extern void stmmac_ptp_check_pps_event(struct stmmac_priv *priv);
++#endif
++
++#ifdef CONFIG_HAVE_CLK
++static inline int stmmac_clk_enable(struct stmmac_priv *priv)
++{
++ if (!IS_ERR(priv->stmmac_clk))
++ return clk_prepare_enable(priv->stmmac_clk);
++
++ return 0;
++}
++
++static inline void stmmac_clk_disable(struct stmmac_priv *priv)
++{
++ if (IS_ERR(priv->stmmac_clk))
++ return;
++
++ clk_disable_unprepare(priv->stmmac_clk);
++}
++static inline int stmmac_clk_get(struct stmmac_priv *priv)
++{
++ priv->stmmac_clk = clk_get(priv->device, NULL);
++
++ if (IS_ERR(priv->stmmac_clk))
++ return PTR_ERR(priv->stmmac_clk);
++
++ return 0;
++}
++#else
++static inline int stmmac_clk_enable(struct stmmac_priv *priv)
++{
++ return 0;
++}
++static inline void stmmac_clk_disable(struct stmmac_priv *priv)
++{
++}
++static inline int stmmac_clk_get(struct stmmac_priv *priv)
++{
++ return 0;
++}
++#endif /* CONFIG_HAVE_CLK */
++
+ void stmmac_disable_eee_mode(struct stmmac_priv *priv);
+ bool stmmac_eee_init(struct stmmac_priv *priv);
+
+@@ -167,6 +242,7 @@ static inline int stmmac_register_pci(void)
+ static inline void stmmac_unregister_pci(void)
+ {
+ }
++
+ #endif /* CONFIG_STMMAC_PCI */
+
+ #endif /* __STMMAC_H__ */
+diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c
+index 1372ce2..0644dcd 100644
+--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c
++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c
+@@ -31,6 +31,7 @@
+
+ #include "stmmac.h"
+ #include "dwmac_dma.h"
++#include "dwmac1000.h"
+
+ #define REG_SPACE_SIZE 0x1054
+ #define MAC100_ETHTOOL_NAME "st_mac100"
+@@ -231,9 +232,7 @@ static int stmmac_ethtool_getsettings(struct net_device *dev,
+ return -EBUSY;
+ }
+ cmd->transceiver = XCVR_INTERNAL;
+- spin_lock_irq(&priv->lock);
+ rc = phy_ethtool_gset(phy, cmd);
+- spin_unlock_irq(&priv->lock);
+ return rc;
+ }
+
+@@ -244,10 +243,7 @@ static int stmmac_ethtool_setsettings(struct net_device *dev,
+ struct phy_device *phy = priv->phydev;
+ int rc;
+
+- spin_lock(&priv->lock);
+ rc = phy_ethtool_sset(phy, cmd);
+- spin_unlock(&priv->lock);
+-
+ return rc;
+ }
+
+@@ -279,7 +275,7 @@ static int stmmac_ethtool_get_regs_len(struct net_device *dev)
+ static void stmmac_ethtool_gregs(struct net_device *dev,
+ struct ethtool_regs *regs, void *space)
+ {
+- int i;
++ int i, offset = 0;
+ u32 *reg_space = (u32 *) space;
+
+ struct stmmac_priv *priv = netdev_priv(dev);
+@@ -300,9 +296,20 @@ static void stmmac_ethtool_gregs(struct net_device *dev,
+ /* MAC registers */
+ for (i = 0; i < 55; i++)
+ reg_space[i] = readl(priv->ioaddr + (i * 4));
++
++ /* VLAN registers */
++ offset = i;
++ reg_space[offset++] = readl(priv->ioaddr + GMAC_VLAN_TAG_REP);
++ reg_space[offset++] = readl(priv->ioaddr + GMAC_VLAN_HASH);
++
++ /* 1588 registers */
++ for(i = 0; i < 13; i++);
++ reg_space[i + offset] = readl(priv->ioaddr + (GMAC_TS_CTRL + (i * 4)));
++
+ /* DMA registers */
++ offset += i;
+ for (i = 0; i < 22; i++)
+- reg_space[i + 55] =
++ reg_space[i + offset] =
+ readl(priv->ioaddr + (DMA_BUS_MODE + (i * 4)));
+ }
+ }
+diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
+index b75f4b2..aaccd3a 100644
+--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
+@@ -28,6 +28,9 @@
+ https://bugzilla.stlinux.com/
+ *******************************************************************************/
+
++#if defined(CONFIG_INTEL_QUARK_X1000_SOC)
++#include <asm/qrk.h>
++#endif
+ #include <linux/clk.h>
+ #include <linux/kernel.h>
+ #include <linux/interrupt.h>
+@@ -135,6 +138,8 @@ static irqreturn_t stmmac_interrupt(int irq, void *dev_id);
+ #ifdef CONFIG_STMMAC_DEBUG_FS
+ static int stmmac_init_fs(struct net_device *dev);
+ static void stmmac_exit_fs(void);
++static int debugfs_registered = 0;
++
+ #endif
+
+ #define STMMAC_COAL_TIMER(x) (jiffies + usecs_to_jiffies(x))
+@@ -502,13 +507,29 @@ static int stmmac_set_bfsize(int mtu, int bufsize)
+ }
+
+ /**
++ * init_dma_err_cleanup
++ *
++ * @dev: net device to clean
++ * Description: Does a cleanup if kmalloc fails during init
++ */
++static void init_dma_err_cleanup(struct stmmac_priv * priv)
++{
++ if (priv->tx_skbuff != NULL)
++ kfree(priv->tx_skbuff);
++ if (priv->rx_skbuff_dma != NULL)
++ kfree (priv->rx_skbuff_dma);
++ if (priv->rx_skbuff != NULL)
++ kfree (priv->rx_skbuff);
++}
++
++/**
+ * init_dma_desc_rings - init the RX/TX descriptor rings
+ * @dev: net device structure
+ * Description: this function initializes the DMA RX/TX descriptors
+ * and allocates the socket buffers. It suppors the chained and ring
+ * modes.
+ */
+-static void init_dma_desc_rings(struct net_device *dev)
++static int init_dma_desc_rings(struct net_device *dev)
+ {
+ int i;
+ struct stmmac_priv *priv = netdev_priv(dev);
+@@ -532,8 +553,16 @@ static void init_dma_desc_rings(struct net_device *dev)
+ txsize, rxsize, bfsize);
+
+ priv->rx_skbuff_dma = kmalloc(rxsize * sizeof(dma_addr_t), GFP_KERNEL);
++ if (priv->rx_skbuff_dma == NULL)
++ return -ENOMEM;
++
+ priv->rx_skbuff =
+ kmalloc(sizeof(struct sk_buff *) * rxsize, GFP_KERNEL);
++ if (priv->rx_skbuff == NULL){
++ init_dma_err_cleanup(priv);
++ return -ENOMEM;
++ }
++
+ priv->dma_rx =
+ (struct dma_desc *)dma_alloc_coherent(priv->device,
+ rxsize *
+@@ -542,6 +571,11 @@ static void init_dma_desc_rings(struct net_device *dev)
+ GFP_KERNEL);
+ priv->tx_skbuff = kmalloc(sizeof(struct sk_buff *) * txsize,
+ GFP_KERNEL);
++ if (priv->tx_skbuff == NULL){
++ init_dma_err_cleanup(priv);
++ return -ENOMEM;
++ }
++
+ priv->dma_tx =
+ (struct dma_desc *)dma_alloc_coherent(priv->device,
+ txsize *
+@@ -550,8 +584,9 @@ static void init_dma_desc_rings(struct net_device *dev)
+ GFP_KERNEL);
+
+ if ((priv->dma_rx == NULL) || (priv->dma_tx == NULL)) {
++ init_dma_err_cleanup(priv);
+ pr_err("%s:ERROR allocating the DMA Tx/Rx desc\n", __func__);
+- return;
++ return -ENOMEM;
+ }
+
+ DBG(probe, INFO, "stmmac (%s) DMA desc: virt addr (Rx %p, "
+@@ -569,8 +604,9 @@ static void init_dma_desc_rings(struct net_device *dev)
+ skb = __netdev_alloc_skb(dev, bfsize + NET_IP_ALIGN,
+ GFP_KERNEL);
+ if (unlikely(skb == NULL)) {
++ init_dma_err_cleanup(priv);
+ pr_err("%s: Rx init fails; skb is NULL\n", __func__);
+- break;
++ return -ENOMEM;
+ }
+ skb_reserve(skb, NET_IP_ALIGN);
+ priv->rx_skbuff[i] = skb;
+@@ -615,6 +651,8 @@ static void init_dma_desc_rings(struct net_device *dev)
+ pr_info("TX descriptor ring:\n");
+ display_ring(priv->dma_tx, txsize);
+ }
++
++ return 0;
+ }
+
+ static void dma_free_rx_skbufs(struct stmmac_priv *priv)
+@@ -736,6 +774,10 @@ static void stmmac_tx_clean(struct stmmac_priv *priv)
+ DMA_TO_DEVICE);
+ priv->hw->ring->clean_desc3(p);
+
++#ifdef CONFIG_STMMAC_PTP
++ stmmac_ptp_tx_hwtstamp(priv, p, skb);
++#endif
++
+ if (likely(skb != NULL)) {
+ dev_kfree_skb(skb);
+ priv->tx_skbuff[entry] = NULL;
+@@ -963,6 +1005,25 @@ static int stmmac_init_dma_engine(struct stmmac_priv *priv)
+ }
+
+ /**
++ * stmmac_hw_set_rx_ipc
++ * @priv : pointer to the private device structure.
++ * Enables RX IPC offload if the feature is supported in hardware
++ */
++static int stmmac_hw_set_rx_ipc(struct stmmac_priv *priv, bool on)
++{
++ int ret = 0;
++
++ /* Enable the IPC (Checksum Offload) and check if the feature has been
++ * enabled during the core configuration. */
++ ret = priv->hw->mac->set_rx_ipc(priv->ioaddr, on);
++ if (on == true && !ret) {
++ pr_warning(" RX IPC Checksum Offload not configured.\n");
++ priv->plat->rx_coe = STMMAC_RX_COE_NONE;
++ }
++ return ret;
++}
++
++/**
+ * stmmac_tx_timer:
+ * @data: data pointer
+ * Description:
+@@ -1022,7 +1083,12 @@ static int stmmac_open(struct net_device *dev)
+ priv->dma_tx_size = STMMAC_ALIGN(dma_txsize);
+ priv->dma_rx_size = STMMAC_ALIGN(dma_rxsize);
+ priv->dma_buf_sz = STMMAC_ALIGN(buf_sz);
+- init_dma_desc_rings(dev);
++ ret = init_dma_desc_rings(dev);
++ if (ret < 0){
++ pr_err("%s: DMA initialization failed\n", __func__);
++ goto open_error;
++ }
++
+
+ /* DMA initialization and SW reset */
+ ret = stmmac_init_dma_engine(priv);
+@@ -1078,6 +1144,9 @@ static int stmmac_open(struct net_device *dev)
+ /* Set the HW DMA mode and the COE */
+ stmmac_dma_operation_mode(priv);
+
++ /* Enable RX IPC if supported by silicon */
++ ret = stmmac_hw_set_rx_ipc(priv, true);
++
+ /* Extra statistics */
+ memset(&priv->xstats, 0, sizeof(struct stmmac_extra_stats));
+ priv->xstats.threshold = tc;
+@@ -1085,9 +1154,12 @@ static int stmmac_open(struct net_device *dev)
+ stmmac_mmc_setup(priv);
+
+ #ifdef CONFIG_STMMAC_DEBUG_FS
+- ret = stmmac_init_fs(dev);
+- if (ret < 0)
+- pr_warning("%s: failed debugFS registration\n", __func__);
++ if (debugfs_registered == 0){
++ debugfs_registered = 1;
++ ret = stmmac_init_fs(dev);
++ if (ret < 0)
++ pr_warning("%s: failed debugFS registration\n", __func__);
++ }
+ #endif
+ /* Start the ball rolling... */
+ DBG(probe, DEBUG, "%s: DMA RX/TX processes started...\n", dev->name);
+@@ -1430,6 +1502,9 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit)
+ #endif
+ skb->protocol = eth_type_trans(skb, priv->dev);
+
++#ifdef CONFIG_STMMAC_PTP
++ stmmac_ptp_tx_hwtstamp(priv, priv->dma_rx + entry, skb);
++#endif
+ if (unlikely(!priv->plat->rx_coe))
+ skb_checksum_none_assert(skb);
+ else
+@@ -1588,9 +1663,19 @@ static netdev_features_t stmmac_fix_features(struct net_device *dev,
+ if (priv->plat->bugged_jumbo && (dev->mtu > ETH_DATA_LEN))
+ features &= ~NETIF_F_ALL_CSUM;
+
++ stmmac_hw_set_rx_ipc(priv, features & NETIF_F_RXCSUM);
++
+ return features;
+ }
+
++#if defined(CONFIG_INTEL_QUARK_X1000_SOC)
++ #define mask_pvm(x) qrk_pci_pvm_mask(x)
++ #define unmask_pvm(x) qrk_pci_pvm_unmask(x)
++#else
++ #define mask_pvm(x)
++ #define unmask_pvm(x)
++#endif
++
+ static irqreturn_t stmmac_interrupt(int irq, void *dev_id)
+ {
+ struct net_device *dev = (struct net_device *)dev_id;
+@@ -1601,10 +1686,12 @@ static irqreturn_t stmmac_interrupt(int irq, void *dev_id)
+ return IRQ_NONE;
+ }
+
++ mask_pvm(priv->pdev);
++
+ /* To handle GMAC own interrupts */
+ if (priv->plat->has_gmac) {
+- int status = priv->hw->mac->host_irq_status((void __iomem *)
+- dev->base_addr);
++ int status = priv->hw->mac->host_irq_status(priv);
++
+ if (unlikely(status)) {
+ if (status & core_mmc_tx_irq)
+ priv->xstats.mmc_tx_irq_n++;
+@@ -1634,6 +1721,8 @@ static irqreturn_t stmmac_interrupt(int irq, void *dev_id)
+ /* To handle DMA interrupts */
+ stmmac_dma_interrupt(priv);
+
++ unmask_pvm(priv->pdev);
++
+ return IRQ_HANDLED;
+ }
+
+@@ -1669,7 +1758,15 @@ static int stmmac_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+ if (!priv->phydev)
+ return -EINVAL;
+
+- ret = phy_mii_ioctl(priv->phydev, rq, cmd);
++ switch (cmd) {
++#ifdef CONFIG_STMMAC_PTP
++ case SIOCSHWTSTAMP:
++ ret = stmmac_ptp_hwtstamp_ioctl(priv, rq, cmd);
++ break;
++#endif
++ default:
++ ret = phy_mii_ioctl(priv->phydev, rq, cmd);
++ }
+
+ return ret;
+ }
+@@ -1850,6 +1947,21 @@ static void stmmac_exit_fs(void)
+ }
+ #endif /* CONFIG_STMMAC_DEBUG_FS */
+
++#ifdef STMMAC_VLAN_HASH
++static int stmmac_vlan_rx_add_vid(struct net_device *dev, unsigned short vid)
++{
++ struct stmmac_priv *priv = netdev_priv(dev);
++ return priv->hw->mac->vlan_rx_add_vid(priv, vid);
++}
++
++static int stmmac_vlan_rx_kill_vid(struct net_device *dev, unsigned short vid)
++{
++ struct stmmac_priv *priv = netdev_priv(dev);
++ return priv->hw->mac->vlan_rx_kill_vid(priv, vid);
++}
++
++#endif
++
+ static const struct net_device_ops stmmac_netdev_ops = {
+ .ndo_open = stmmac_open,
+ .ndo_start_xmit = stmmac_xmit,
+@@ -1860,6 +1972,10 @@ static const struct net_device_ops stmmac_netdev_ops = {
+ .ndo_tx_timeout = stmmac_tx_timeout,
+ .ndo_do_ioctl = stmmac_ioctl,
+ .ndo_set_config = stmmac_config,
++#ifdef STMMAC_VLAN_HASH
++ .ndo_vlan_rx_add_vid = stmmac_vlan_rx_add_vid,
++ .ndo_vlan_rx_kill_vid = stmmac_vlan_rx_kill_vid,
++#endif
+ #ifdef CONFIG_NET_POLL_CONTROLLER
+ .ndo_poll_controller = stmmac_poll_controller,
+ #endif
+@@ -1924,13 +2040,7 @@ static int stmmac_hw_init(struct stmmac_priv *priv)
+ /* 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);
+- if (!ret) {
+- pr_warning(" RX IPC Checksum Offload not configured.\n");
+- priv->plat->rx_coe = STMMAC_RX_COE_NONE;
+- }
++ ret = stmmac_hw_set_rx_ipc(priv, true);
+
+ if (priv->plat->rx_coe)
+ pr_info(" RX Checksum Offload Engine supported (type %d)\n",
+@@ -2001,6 +2111,12 @@ struct stmmac_priv *stmmac_dvr_probe(struct device *device,
+ /* Both mac100 and gmac support receive VLAN tag detection */
+ ndev->features |= NETIF_F_HW_VLAN_RX;
+ #endif
++#ifdef STMMAC_VLAN_HASH
++ ndev->features |= NETIF_F_HW_VLAN_FILTER;
++ ndev->vlan_features = NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM |
++ NETIF_F_RXCSUM;
++#endif
++
+ priv->msg_enable = netif_msg_init(debug, default_msg_level);
+
+ if (flow_ctrl)
+@@ -2044,6 +2160,10 @@ struct stmmac_priv *stmmac_dvr_probe(struct device *device,
+ else
+ priv->clk_csr = priv->plat->clk_csr;
+
++#ifdef CONFIG_STMMAC_PTP
++ stmmac_ptp_init(ndev, device);
++#endif
++
+ /* MDIO bus Registration */
+ ret = stmmac_mdio_register(ndev);
+ if (ret < 0) {
+@@ -2060,6 +2180,9 @@ error_clk_get:
+ unregister_netdev(ndev);
+ error_netdev_register:
+ netif_napi_del(&priv->napi);
++#ifdef CONFIG_STMMAC_PTP
++ stmmac_ptp_remove(priv);
++#endif
+ free_netdev(ndev);
+
+ return NULL;
+@@ -2075,7 +2198,7 @@ int stmmac_dvr_remove(struct net_device *ndev)
+ {
+ struct stmmac_priv *priv = netdev_priv(ndev);
+
+- pr_info("%s:\n\tremoving driver", __func__);
++ pr_info("%s:\n\tremoving driver\n", __func__);
+
+ priv->hw->dma->stop_rx(priv->ioaddr);
+ priv->hw->dma->stop_tx(priv->ioaddr);
+@@ -2083,6 +2206,10 @@ int stmmac_dvr_remove(struct net_device *ndev)
+ stmmac_set_mac(priv->ioaddr, false);
+ stmmac_mdio_unregister(ndev);
+ netif_carrier_off(ndev);
++
++#ifdef CONFIG_STMMAC_PTP
++ stmmac_ptp_remove(priv);
++#endif
+ unregister_netdev(ndev);
+ free_netdev(ndev);
+
+diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_pci.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_pci.c
+index 064eaac..f7afcad 100644
+--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_pci.c
++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_pci.c
+@@ -23,32 +23,194 @@
+ Author: Giuseppe Cavallaro <peppe.cavallaro@st.com>
+ *******************************************************************************/
+
++#include <linux/dmi.h>
+ #include <linux/pci.h>
++#include <linux/platform_data/quark.h>
+ #include "stmmac.h"
+
+-struct plat_stmmacenet_data plat_dat;
+-struct stmmac_mdio_bus_data mdio_data;
+-struct stmmac_dma_cfg dma_cfg;
++/* List of supported PCI device IDs */
++#define STMMAC_VENDOR_ID 0x700
++#define STMMAC_DEVICE_ID 0x1108
++#define STMMAC_QUARK_ID 0x0937
++#define MAX_INTERFACES 0x02
++
++#if defined (CONFIG_INTEL_QUARK_X1000_SOC)
++static int enable_msi = 1;
++#else
++static int enable_msi = 0;
++#endif
++module_param(enable_msi, int, S_IRUGO | S_IWUSR);
++MODULE_PARM_DESC(enable_msi, "Enable PCI MSI mode");
++
++static int bus_id = 1;
++static char stmmac_mac_data[MAX_INTERFACES][ETH_ALEN];
++
++struct stmmac_qrk_mac_data {
++ int phy_addr;
++ int bus_id;
++ const char * name;
++};
++
++static struct stmmac_qrk_mac_data phy_data [] = {
++ {
++ .phy_addr = -1, /* not connected */
++ .bus_id = 1,
++ .name = "QuarkEmulation",
++ },
++ {
++ .phy_addr = 1,
++ .bus_id = 2,
++ .name = "QuarkEmulation",
++ },
++ {
++ .phy_addr = 3,
++ .bus_id = 1,
++ .name = "ClantonPeakSVP",
++ },
++ {
++ .phy_addr = 1,
++ .bus_id = 2,
++ .name = "ClantonPeakSVP",
++ },
++ {
++ .phy_addr = 1,
++ .bus_id = 1,
++ .name = "KipsBay",
++ },
++ {
++ .phy_addr = -1, /* not connected */
++ .bus_id = 2,
++ .name = "KipsBay",
++ },
++ {
++ .phy_addr = 1,
++ .bus_id = 1,
++ .name = "CrossHill",
++ },
++ {
++ .phy_addr = 1,
++ .bus_id = 2,
++ .name = "CrossHill",
++ },
++ {
++ .phy_addr = 1,
++ .bus_id = 1,
++ .name = "ClantonHill",
++ },
++ {
++ .phy_addr = 1,
++ .bus_id = 2,
++ .name = "ClantonHill",
++ },
++ {
++ .phy_addr = 1,
++ .bus_id = 1,
++ .name = "Galileo",
++ },
++ {
++ .phy_addr = -1, /* not connected */
++ .bus_id = 2,
++ .name = "Galileo",
++ },
++};
++
++
++static int stmmac_find_phy_addr(int mdio_bus_id)
++{
++ int i = 0;
++ const char * board_name = dmi_get_system_info(DMI_BOARD_NAME);
++ if (board_name == NULL)
++ return -1;
++
++ for (; i < sizeof(phy_data)/sizeof(struct stmmac_qrk_mac_data); i++){
++ if ((!strcmp(phy_data[i].name, board_name)) &&
++ phy_data[i].bus_id == mdio_bus_id)
++ return phy_data[i].phy_addr;
++ }
++
++ return -1;
++}
+
+-static void stmmac_default_data(void)
++static int stmmac_default_data(struct plat_stmmacenet_data *plat_dat,
++ int mdio_bus_id, const struct pci_device_id *id)
+ {
+- memset(&plat_dat, 0, sizeof(struct plat_stmmacenet_data));
+- plat_dat.bus_id = 1;
+- plat_dat.phy_addr = 0;
+- plat_dat.interface = PHY_INTERFACE_MODE_GMII;
+- plat_dat.clk_csr = 2; /* clk_csr_i = 20-35MHz & MDC = clk_csr_i/16 */
+- plat_dat.has_gmac = 1;
+- plat_dat.force_sf_dma_mode = 1;
+-
+- mdio_data.phy_reset = NULL;
+- mdio_data.phy_mask = 0;
+- plat_dat.mdio_bus_data = &mdio_data;
+-
+- dma_cfg.pbl = 32;
+- dma_cfg.burst_len = DMA_AXI_BLEN_256;
+- plat_dat.dma_cfg = &dma_cfg;
++ int phy_addr = 0;
++ memset(plat_dat, 0, sizeof(struct plat_stmmacenet_data));
++
++ plat_dat->mdio_bus_data = kzalloc(sizeof(struct stmmac_mdio_bus_data),
++ GFP_KERNEL);
++ if (plat_dat->mdio_bus_data == NULL)
++ return -ENOMEM;
++
++ plat_dat->dma_cfg = kzalloc(sizeof(struct stmmac_dma_cfg),GFP_KERNEL);
++ if (plat_dat->dma_cfg == NULL)
++ return -ENOMEM;
++
++ if (id->device == STMMAC_QUARK_ID) {
++
++ phy_addr = stmmac_find_phy_addr(mdio_bus_id);
++ if (phy_addr == -1)
++ return -ENODEV;
++
++ plat_dat->bus_id = mdio_bus_id;
++ plat_dat->phy_addr = phy_addr;
++ plat_dat->interface = PHY_INTERFACE_MODE_RMII;
++ /* clk_csr_i = 20-35MHz & MDC = clk_csr_i/16 */
++ plat_dat->clk_csr = 2;
++ plat_dat->has_gmac = 1;
++ plat_dat->force_sf_dma_mode = 1;
++
++ plat_dat->mdio_bus_data->phy_reset = NULL;
++ plat_dat->mdio_bus_data->phy_mask = 0;
++
++ plat_dat->dma_cfg->pbl = 16;
++ plat_dat->dma_cfg->fixed_burst = 1;
++ plat_dat->dma_cfg->burst_len = DMA_AXI_BLEN_256;
++
++ } else {
++
++ plat_dat->bus_id = mdio_bus_id;
++ plat_dat->phy_addr = phy_addr;
++ plat_dat->interface = PHY_INTERFACE_MODE_GMII;
++ /* clk_csr_i = 20-35MHz & MDC = clk_csr_i/16 */
++ plat_dat->clk_csr = 2;
++ plat_dat->has_gmac = 1;
++ plat_dat->force_sf_dma_mode = 1;
++
++ plat_dat->mdio_bus_data->phy_reset = NULL;
++ plat_dat->mdio_bus_data->phy_mask = 0;
++
++ plat_dat->dma_cfg->pbl = 32;
++ plat_dat->dma_cfg->burst_len = DMA_AXI_BLEN_256;
++
++ }
++
++ return 0;
+ }
+
++#if 0
++/**
++ * stmmac_pci_find_mac
++ *
++ * @prive: pointer to private stmmac structure
++ * @mdio_bus_id : MDIO bus identifier used to find the platform MAC id
++ *
++ * Attempt to find MAC in platform data. If not found then driver will generate
++ * a random one for itself in any case
++ */
++void stmmac_pci_find_mac (struct stmmac_priv * priv, unsigned int mdio_bus_id)
++{
++ unsigned int id = mdio_bus_id - 1;
++ if (priv == NULL || id >= MAX_INTERFACES)
++ return;
++
++ if (intel_qrk_plat_get_mac(PLAT_DATA_MAC0+id,
++ (char*)&stmmac_mac_data[id]) == 0){
++ memcpy(priv->dev->dev_addr, &stmmac_mac_data[id], ETH_ALEN);
++ }
++}
++#endif
++
+ /**
+ * stmmac_pci_probe
+ *
+@@ -67,8 +229,21 @@ static int stmmac_pci_probe(struct pci_dev *pdev,
+ int ret = 0;
+ void __iomem *addr = NULL;
+ struct stmmac_priv *priv = NULL;
++ struct plat_stmmacenet_data *plat_dat = NULL;
+ int i;
+
++ plat_dat = kmalloc(sizeof(struct plat_stmmacenet_data), GFP_KERNEL);
++ if (plat_dat == NULL){
++ ret = -ENOMEM;
++ goto err_out_map_failed;
++ }
++
++ /* return -ENODEV for non existing PHY, stop probing here */
++ ret = stmmac_default_data(plat_dat, bus_id, id);
++ if (ret != 0)
++ goto err_platdata;
++
++
+ /* Enable pci device */
+ ret = pci_enable_device(pdev);
+ if (ret) {
+@@ -96,30 +271,51 @@ static int stmmac_pci_probe(struct pci_dev *pdev,
+ break;
+ }
+ pci_set_master(pdev);
++ if(enable_msi == 1){
++ pci_enable_msi(pdev);
++ pr_info("stmmac MSI mode enabled");
++ }
+
+- stmmac_default_data();
++ pr_info("Vendor 0x%04x Device 0x%04x\n",
++ id->vendor, id->device);
+
+- priv = stmmac_dvr_probe(&(pdev->dev), &plat_dat, addr);
++ priv = stmmac_dvr_probe(&(pdev->dev), plat_dat, addr);
+ if (!priv) {
+ pr_err("%s: main driver probe failed", __func__);
+ goto err_out;
+ }
++#if 0
++ stmmac_pci_find_mac(priv, bus_id);
++#endif
+ priv->dev->irq = pdev->irq;
+ priv->wol_irq = pdev->irq;
+-
++ priv->irqmode_msi = enable_msi;
++ priv->pdev = pdev;
++ #ifdef CONFIG_INTEL_QUARK_X1000_SOC
++ priv->lpi_irq = -ENXIO;
++ #endif
+ pci_set_drvdata(pdev, priv->dev);
+
+ pr_debug("STMMAC platform driver registration completed");
++ bus_id++;
+
+ return 0;
+
+ err_out:
+ pci_clear_master(pdev);
+-err_out_map_failed:
+ pci_release_regions(pdev);
+ err_out_req_reg_failed:
+ pci_disable_device(pdev);
+-
++err_platdata:
++ if (plat_dat != NULL){
++ if (plat_dat->dma_cfg != NULL)
++ kfree (plat_dat->dma_cfg);
++ if (plat_dat->mdio_bus_data != NULL)
++ kfree (plat_dat->mdio_bus_data);
++ kfree(plat_dat);
++ }
++err_out_map_failed:
++ bus_id++;
+ return ret;
+ }
+
+@@ -138,6 +334,21 @@ static void stmmac_pci_remove(struct pci_dev *pdev)
+ stmmac_dvr_remove(ndev);
+
+ pci_set_drvdata(pdev, NULL);
++
++ if(enable_msi == 1) {
++ if(pci_dev_msi_enabled(pdev)) {
++ pci_disable_msi(pdev);
++ }
++ }
++
++ if (priv->plat != NULL) {
++ if (priv->plat->dma_cfg != NULL)
++ kfree (priv->plat->dma_cfg);
++ if (priv->plat->mdio_bus_data != NULL)
++ kfree (priv->plat->mdio_bus_data);
++ kfree(priv->plat);
++ }
++
+ pci_iounmap(pdev, priv->ioaddr);
+ pci_release_regions(pdev);
+ pci_disable_device(pdev);
+@@ -167,12 +378,10 @@ static int stmmac_pci_resume(struct pci_dev *pdev)
+ }
+ #endif
+
+-#define STMMAC_VENDOR_ID 0x700
+-#define STMMAC_DEVICE_ID 0x1108
+-
+ static DEFINE_PCI_DEVICE_TABLE(stmmac_id_table) = {
+ {PCI_DEVICE(STMMAC_VENDOR_ID, STMMAC_DEVICE_ID)},
+ {PCI_DEVICE(PCI_VENDOR_ID_STMICRO, PCI_DEVICE_ID_STMICRO_MAC)},
++ {PCI_DEVICE(PCI_VENDOR_ID_INTEL, STMMAC_QUARK_ID)},
+ {}
+ };
+
+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 0000000..8552d0c
+--- /dev/null
++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_ptp.c
+@@ -0,0 +1,723 @@
++/*******************************************************************************
++
++ STMMAC 1588-2005 hardware accel
++ Copyright(c) 2012 Intel Corporation. This code is based on IXGBE_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".
++
++ Contact Information::w
++ Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
++
++*******************************************************************************/
++#include "stmmac.h"
++#include "dwmac1000.h"
++#include <linux/export.h>
++#include <linux/net_tstamp.h>
++
++/* PPSCMD0 */
++#define STMMAC_PPSCMD_NONE 0x00 /* No command */
++#define STMMAC_PPSCMD_START_SINGLE 0x01 /* Start single pulse */
++#define STMMAC_PPSCMD_START_PULSE 0x02 /* Start pulse train */
++#define STMMAC_PPSCMD_START_CANCEL 0x03 /* Cancel start */
++#define STMMAC_PPSCMD_STOP_PULSE_ATTIME 0x04 /* Stop pulse train at time */
++#define STMMAC_PPSCMD_STOP_PULSE_IMMED 0x05 /* Stop pulse train immediate */
++#define STMMAC_PPSCMD_STOP_CANCEL 0x06 /* Stop cancel pulse train */
++
++#define STMMAC_PTP_OVERFLOW_CHECK_ENABLED (u32)(1)
++#define STMMAC_PTP_PPS_ENABLED (u32)(1 << 1)
++#define STMMAC_PTP_HWTS_TX_EN (u32)(1 << 2)
++#define STMMAC_PTP_HWTS_RX_EN (u32)(1 << 3)
++
++#ifndef NSECS_PER_SEC
++#define NSECS_PER_SEC 1000000000ULL
++#endif
++
++/*
++ * Structure of STMMAC timer registers
++ *
++ * GMAC_TS_HWSEC GMAC_ST_SEC GMAC_ST_NSEC
++ * +--------------+ +--------------+ +---+---+------+
++ * STMMAC | 16 | | 32 | | 32 |
++ * +--------------+ +--------------+ +---+---+------+
++ *
++ * The counter for the STMMAC is 80 bits
++ * - HWSEC == overflow value for ST_SEC => 130 years to overflow (optional)
++ * - ST_SEC == seconds
++ * - ST_NSEC == nanoseconds
++ */
++
++/**
++ * stmmac_ptp_read - read raw cycle counter (to be used by time counter)
++ * @cc - the cyclecounter structure
++ *
++ * this function reads the cyclecounter registers and is called by the
++ * cyclecounter structure used to construct a ns counter from the
++ * arbitrary fixed point registers
++ */
++static cycle_t stmmac_ptp_read(const struct cyclecounter *ccnt)
++{
++ struct stmmac_priv *priv =
++ container_of(ccnt, struct stmmac_priv, ccnt);
++ cycle_t stamp = 0;
++
++ stamp = (u64)readl(priv->ioaddr + GMAC_ST_NSEC);
++ stamp |= (u64)readl(priv->ioaddr + GMAC_ST_SEC) << 32;
++
++ return stamp;
++}
++
++/**
++ * stmmac_ptp_adjfreq
++ * @ptp - the ptp clock structure
++ * @ppb - parts per billion adjustment from base
++ *
++ * adjust the frequency of the ptp cycle counter by the
++ * indicated ppb from the base frequency.
++ */
++static int stmmac_ptp_adjfreq(struct ptp_clock_info *ptp, s32 ppb)
++{
++ return 0;
++}
++
++/**
++ * stmmac_ptp_adjtime
++ * @ptp - the ptp clock structure
++ * @delta - offset to adjust the cycle counter by
++ *
++ * adjust the timer by resetting the timecounter structure.
++ */
++static int stmmac_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta)
++{
++ struct stmmac_priv *priv =
++ container_of(ptp, struct stmmac_priv, ptp_caps);
++ unsigned long flags;
++ u64 now;
++
++ spin_lock_irqsave(&priv->tmreg_lock, flags);
++
++ now = timecounter_read(&priv->tcnt);
++ now += delta;
++
++ /* reset the timecounter */
++ timecounter_init(&priv->tcnt,
++ &priv->ccnt,
++ now);
++
++ spin_unlock_irqrestore(&priv->tmreg_lock, flags);
++ return 0;
++}
++
++/**
++ * stmmac_ptp_gettime
++ * @ptp - the ptp clock structure
++ * @ts - timespec structure to hold the current time value
++ *
++ * read the timecounter and return the correct value on ns,
++ * after converting it into a struct timespec.
++ */
++static int stmmac_ptp_gettime(struct ptp_clock_info *ptp, struct timespec *ts)
++{
++ struct stmmac_priv *priv =
++ container_of(ptp, struct stmmac_priv, ptp_caps);
++ u64 ns;
++ u32 remainder;
++ unsigned long flags;
++
++ spin_lock_irqsave(&priv->tmreg_lock, flags);
++ ns = timecounter_read(&priv->tcnt);
++ spin_unlock_irqrestore(&priv->tmreg_lock, flags);
++
++ ts->tv_sec = div_u64_rem(ns, 1000000000ULL, &remainder);
++ ts->tv_nsec = remainder;
++
++ return 0;
++}
++
++/**
++ * stmmac_ptp_settime
++ * @ptp - the ptp clock structure
++ * @ts - the timespec containing the new time for the cycle counter
++ *
++ * reset the timecounter to use a new base value instead of the kernel
++ * wall timer value.
++ */
++static int stmmac_ptp_settime(struct ptp_clock_info *ptp,
++ const struct timespec *ts)
++{
++ struct stmmac_priv *priv =
++ container_of(ptp, struct stmmac_priv, ptp_caps);
++ u64 ns;
++ unsigned long flags;
++
++ ns = ts->tv_sec * 1000000000ULL;
++ ns += ts->tv_nsec;
++
++ /* reset the timecounter */
++ spin_lock_irqsave(&priv->tmreg_lock, flags);
++ timecounter_init(&priv->tcnt, &priv->ccnt, ns);
++ spin_unlock_irqrestore(&priv->tmreg_lock, flags);
++
++ return 0;
++}
++
++/**
++ * stmmac_ptp_enable
++ * @ptp - the ptp clock structure
++ * @rq - the requested feature to change
++ * @on - whether to enable or disable the feature
++ *
++ * enable (or disable) ancillary features of the phc subsystem.
++ * our driver only supports the PPS feature on the X540
++ */
++static int stmmac_ptp_enable(struct ptp_clock_info *ptp,
++ struct ptp_clock_request *rq, int on)
++{
++ struct stmmac_priv *priv =
++ container_of(ptp, struct stmmac_priv, ptp_caps);
++ uint32_t reg = 0;
++
++ /**
++ * When enabling PPS functionality in STMMAC we need to unmask the
++ * interrupt mask reg and enable the TSTRIG bit in the timestamp control
++ * reg
++ */
++ if (rq->type == PTP_CLK_REQ_PPS) {
++ if (on){
++ priv->hwts |= STMMAC_PTP_PPS_ENABLED;
++
++ /* Enable TSTRIG */
++ reg = readl(priv->ioaddr + GMAC_TS_CTRL);
++ reg |= GMAC_TS_CTRL_TSTRIG;
++ writel(reg, priv->ioaddr + GMAC_TS_CTRL);
++ wmb();
++
++ /* Unmask interrupt */
++ reg = readl(priv->ioaddr + GMAC_INT_MASK);
++ printk(KERN_INFO "%s[on] read interrupt mask 0x%08x\n", __func__, reg);
++ reg &= ~GMAC_INT_MASK_TSIM;
++ printk(KERN_INFO "%s[on] write interrupt mask 0x%08x\n", __func__, reg);
++ writel(reg, priv->ioaddr + GMAC_INT_MASK);
++ wmb();
++
++ } else {
++ /* Mask interrupt */
++ reg = readl(priv->ioaddr + GMAC_INT_MASK);
++ printk(KERN_INFO "%s[off] read interrupt mask 0x%08x\n", __func__, reg);
++ reg |= GMAC_INT_MASK_TSIM;
++ printk(KERN_INFO "%s[off] write interrupt mask 0x%08x\n", __func__, reg);
++ writel(reg, priv->ioaddr + GMAC_INT_MASK);
++ wmb();
++
++ /* Disable TSTRIG */
++ reg = readl(priv->ioaddr + GMAC_TS_CTRL);
++ reg &= ~GMAC_TS_CTRL_TSTRIG;
++ writel(reg, priv->ioaddr + GMAC_TS_CTRL);
++ wmb();
++
++ priv->hwts &=
++ ~STMMAC_PTP_PPS_ENABLED;
++ }
++ return 0;
++ }
++
++ return -ENOTSUPP;
++}
++
++/**
++ * stmmac_ptp_check_pps_event
++ * @priv - the private priv structure
++ *
++ * This function is called by the interrupt routine when checking for
++ * interrupts. It will check and handle a pps event.
++ */
++void stmmac_ptp_check_pps_event(struct stmmac_priv *priv)
++{
++ struct ptp_clock_event event;
++ event.type = PTP_CLOCK_PPS;
++
++ /* Make sure ptp clock is valid, and PPS event enabled */
++ if (!priv->ptp_clock ||
++ !(priv->hwts & STMMAC_PTP_PPS_ENABLED)){
++ return;
++ }
++
++ ptp_clock_event(priv->ptp_clock, &event);
++}
++
++#if 0
++/**
++ * stmmac_ptp_overflow_check - delayed work to detect SYSTIME overflow
++ * @work: structure containing information about this work task
++ *
++ * this work function is scheduled to continue reading the timecounter
++ * in order to prevent missing when the system time registers wrap
++ * around. This needs to be run approximately twice a minute when no
++ * PTP activity is occurring.
++ */
++void stmmac_ptp_overflow_check(struct stmmac_priv *priv)
++{
++ unsigned long elapsed_jiffies = priv->last_overflow_check - jiffies;
++ struct timespec ts;
++
++ if ((priv->hwts & STMMAC_PTP_OVERFLOW_CHECK_ENABLED) &&
++ (elapsed_jiffies >= STMMAC_OVERFLOW_PERIOD)) {
++ stmmac_ptp_gettime(&priv->ptp_caps, &ts);
++ priv->last_overflow_check = jiffies;
++ }
++}
++#endif
++
++/**
++ * stmmac_ptp_tx_hwtstamp - utility function which checks for TX time stamp
++ * @q_vector: structure containing interrupt and ring information
++ * @skb: particular skb to send timestamp with
++ *
++ * if the timestamp is valid, we convert it into the timecounter ns
++ * value, then store that result into the shhwtstamps structure which
++ * is passed up the network stack
++ */
++void stmmac_ptp_tx_hwtstamp(struct stmmac_priv *priv, struct dma_desc *pdma,
++ struct sk_buff *skb)
++{
++ /* Sanity check input */
++ if (unlikely(priv == NULL || pdma == NULL || skb == NULL)){
++ return;
++ }
++
++ if(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP){
++ struct skb_shared_hwtstamps shhwtstamps;
++ u64 ns;
++ u64 regval;
++ unsigned long flags;
++
++ regval = (u64)pdma->ts_lo;
++ regval |= (u64)pdma->ts_hi << 32;
++
++ spin_lock_irqsave(&priv->tmreg_lock, flags);
++ ns = timecounter_cyc2time(&priv->tcnt, regval);
++ spin_unlock_irqrestore(&priv->tmreg_lock, flags);
++
++ memset(&shhwtstamps, 0, sizeof(shhwtstamps));
++ shhwtstamps.hwtstamp = ns_to_ktime(ns);
++ skb_tstamp_tx(skb, &shhwtstamps);
++ }
++}
++
++/**
++ * stmmac_ptp_rx_hwtstamp - utility function which checks for RX time stamp
++ * @q_vector: structure containing interrupt and ring information
++ * @skb: particular skb to send timestamp with
++ *
++ * if the timestamp is valid, we convert it into the timecounter ns
++ * value, then store that result into the shhwtstamps structure which
++ * is passed up the network stack
++ */
++void stmmac_ptp_rx_hwtstamp(struct stmmac_priv *priv, struct dma_desc *pdma,
++ struct sk_buff *skb)
++{
++ /* Sanity check input */
++ if (unlikely(priv == NULL || pdma == NULL || skb == NULL)){
++ return;
++ }
++
++ if(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP){
++ struct skb_shared_hwtstamps shhwtstamps;
++ u64 ns;
++ u64 regval;
++ unsigned long flags;
++
++ regval = (u64)pdma->ts_lo;
++ regval |= (u64)pdma->ts_hi << 32;
++
++ spin_lock_irqsave(&priv->tmreg_lock, flags);
++ ns = timecounter_cyc2time(&priv->tcnt, regval);
++ spin_unlock_irqrestore(&priv->tmreg_lock, flags);
++
++ memset(&shhwtstamps, 0, sizeof(shhwtstamps));
++ shhwtstamps.hwtstamp = ns_to_ktime(ns);
++ }
++}
++
++/**
++ * ixgbe_ptp_hwtstamp_ioctl - control hardware time stamping
++ * @priv: pointer to priv struct
++ * @ifreq: ioctl data
++ * @cmd: particular ioctl requested
++ *
++ * Outgoing time stamping can be enabled and disabled. Play nice and
++ * disable it when requested, although it shouldn't case any overhead
++ * when no packet needs it. At most one packet in the queue may be
++ * marked for time stamping, otherwise it would be impossible to tell
++ * for sure to which packet the hardware time stamp belongs.
++ *
++ * Incoming time stamping has to be configured via the hardware
++ * filters. Not all combinations are supported, in particular event
++ * type has to be specified. Matching the kind of event packet is
++ * not supported, with the exception of "all V2 events regardless of
++ * level 2 or 4".
++ */
++int stmmac_ptp_hwtstamp_ioctl(struct stmmac_priv *priv,
++ struct ifreq *ifr, int cmd)
++{
++ struct hwtstamp_config config;
++// struct stmmac_priv *priv = netdev_priv(netdev);
++ u32 tsctl = 0;
++
++ if (copy_from_user(&config, ifr->ifr_data, sizeof(config)))
++ return -EFAULT;
++
++ /* reserved for future extensions */
++ if (config.flags)
++ return -EINVAL;
++
++ /* Snapshot the reg - preserve Timestamp Interrupt Trigger Enable */
++ tsctl = readl(priv->ioaddr + GMAC_TS_CTRL) & GMAC_TS_CTRL_TSTRIG;
++
++ /* TX */
++ switch (config.tx_type) {
++ case HWTSTAMP_TX_OFF:
++ priv->hwts &= ~STMMAC_PTP_HWTS_TX_EN;
++ printk(KERN_INFO "%s set TX PTP en false\n", __func__);
++ break;
++ case HWTSTAMP_TX_ON:
++ priv->hwts |= STMMAC_PTP_HWTS_TX_EN;
++ printk(KERN_INFO "%s set TX PTP en true\n", __func__);
++ break;
++ default:
++ return -ERANGE;
++ }
++
++ /* RX */
++ priv->hwts |= STMMAC_PTP_HWTS_RX_EN;
++
++ switch (config.rx_filter) {
++
++
++ case HWTSTAMP_FILTER_PTP_V1_L4_EVENT:
++ /*
++ * V1_L4 UDP any event
++ * SYNC, Follow_Up, Delay_Req, Delay_Resp,
++ * Pdelay_Req, Pdelay_Resp, Pdelay_Resp_Follow_Up
++ *
++ * SNAPTYPSEL=1, TSMSTRENA=x, TSEVNTENA=0, TSVER2ENA=0, IPV4=1
++ */
++ tsctl |= GMAC_TS_CTRL_SNTYPSEL1 | GMAC_TS_CTRL_TSIPV4ENA;
++ printk(KERN_INFO "%s HWTSTAMP_FILTER_PTP_V1_L4_EVENT \n", __func__);
++ break;
++
++ case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ:
++ /*
++ * V1_L4 Master
++ * Delay_Req
++ *
++ * SNAPTYPSEL=0, TSMSTRENA=1, TSEVNTENA=1, TSVER2ENA=0, IPV4=1
++ */
++ tsctl |= GMAC_TS_CTRL_SNTYPSEL0 | GMAC_TS_CTRL_TSMSTRENA;
++ tsctl |= GMAC_TS_CTRL_TSEVNTENA | GMAC_TS_CTRL_TSIPV4ENA;
++ printk(KERN_INFO "%s HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ \n", __func__);
++ break;
++
++ case HWTSTAMP_FILTER_PTP_V1_L4_SYNC:
++ /*
++ * V1_L4 Slave
++ * Sync
++ *
++ * SNAPTYPSEL=0, TSMSTRENA=0, TSEVNTENA=1, TSVER2ENA=0, IPV4=1
++ */
++ tsctl |= GMAC_TS_CTRL_SNTYPSEL0 | GMAC_TS_CTRL_TSEVNTENA;
++ tsctl |= GMAC_TS_CTRL_TSIPV4ENA;
++ printk(KERN_INFO "%s HWTSTAMP_FILTER_PTP_V1_L4_SYNC \n", __func__);
++ break;
++
++ case HWTSTAMP_FILTER_PTP_V2_L4_EVENT:
++ /*
++ * V2_L4 UDP any event
++ * SYNC, Follow_Up, Delay_Req, Delay_Resp,
++ * Pdelay_Req, Pdelay_Resp, Pdelay_Resp_Follow_Up
++ *
++ * SNAPTYPSEL=1, TSMSTRENA=x, TSEVNTENA=0, TSVER2ENA=1, IPV4=1
++ */
++ tsctl |= GMAC_TS_CTRL_SNTYPSEL1 | GMAC_TS_CTRL_TSVER2ENA | GMAC_TS_CTRL_TSIPV4ENA;
++ printk(KERN_INFO "%s HWTSTAMP_FILTER_PTP_V2_L4_EVENT \n", __func__);
++ break;
++
++ case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ:
++ /*
++ * V2_L4 Master
++ * Delay_Req
++ *
++ * SNAPTYPSEL=0, TSMSTRENA=1, TSEVNTENA=1, TSVER2ENA=1, IPV4=1
++ */
++ tsctl |= GMAC_TS_CTRL_SNTYPSEL0 | GMAC_TS_CTRL_TSMSTRENA;
++ tsctl |= GMAC_TS_CTRL_TSEVNTENA | GMAC_TS_CTRL_TSVER2ENA;
++ tsctl |= GMAC_TS_CTRL_TSIPV4ENA;
++
++ printk(KERN_INFO "%s HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ \n", __func__);
++ break;
++
++ case HWTSTAMP_FILTER_PTP_V2_L4_SYNC:
++ /*
++ * V2_L4 Slave
++ * Sync
++ *
++ * SNAPTYPSEL=0, TSMSTRENA=0, TSEVNTENA=1, TSVER2ENA=1, IPV4=1
++ */
++ tsctl |= GMAC_TS_CTRL_SNTYPSEL0 | GMAC_TS_CTRL_TSVER2ENA | GMAC_TS_CTRL_TSEVNTENA;
++ tsctl |= GMAC_TS_CTRL_TSIPV4ENA;
++ printk(KERN_INFO "%s HWTSTAMP_FILTER_PTP_V2_L4_SYNC \n", __func__);
++ break;
++
++ case HWTSTAMP_FILTER_PTP_V2_L2_EVENT:
++ /*
++ * V2_L2 Ethernet any event
++ * SYNC, Follow_Up, Delay_Req, Delay_Resp,
++ * Pdelay_Req, Pdelay_Resp, Pdelay_Resp_Follow_Up
++ *
++ * SNAPTYPSEL=1, TSMSTRENA=x, TSEVNTENA=0, TSVER2ENA=1,TSIPENA=1
++ * TSIPENA=1
++ */
++ tsctl |= GMAC_TS_CTRL_SNTYPSEL1 | GMAC_TS_CTRL_TSVER2ENA | GMAC_TS_CTRL_TSIPENA;
++ printk(KERN_INFO "%s HWTSTAMP_FILTER_PTP_V2_L2_EVENT \n", __func__);
++ break;
++
++ case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ:
++ /*
++ * V2_L2 Master
++ * Delay_Req
++ *
++ * SNAPTYPSEL=0, TSMSTRENA=1, TSEVNTENA=1, TSVER2ENA=1,TSIPENA=1
++ */
++ tsctl |= GMAC_TS_CTRL_SNTYPSEL0 | GMAC_TS_CTRL_TSMSTRENA;
++ tsctl |= GMAC_TS_CTRL_TSEVNTENA | GMAC_TS_CTRL_TSVER2ENA;
++ tsctl |= GMAC_TS_CTRL_TSIPENA;
++ printk(KERN_INFO "%s HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ \n", __func__);
++ break;
++
++ case HWTSTAMP_FILTER_PTP_V2_L2_SYNC:
++ /*
++ * V2_L2 Slave
++ * Sync
++ *
++ * SNAPTYPSEL=0, TSMSTRENA=0, TSEVNTENA=1, TSVER2ENA=1,
++ * TSIPENA=1
++ */
++ tsctl |= GMAC_TS_CTRL_SNTYPSEL0 | GMAC_TS_CTRL_TSVER2ENA;
++ tsctl |= GMAC_TS_CTRL_TSEVNTENA | GMAC_TS_CTRL_TSIPENA;
++ printk(KERN_INFO "%s HWTSTAMP_FILTER_PTP_V2_L2_SYNC \n", __func__);
++ break;
++
++ case HWTSTAMP_FILTER_PTP_V2_EVENT:
++ /*
++ * V2_L2 Ethernet any event
++ * SYNC, Follow_Up, Delay_Req, Delay_Resp,
++ * Pdelay_Req, Pdelay_Resp, Pdelay_Resp_Follow_Up
++ *
++ * SNAPTYPSEL=1, TSMSTRENA=x, TSEVNTENA=0, TSVER2ENA=1
++ * TSIPENA=1, TSIPV4ENA=1
++ */
++ tsctl |= GMAC_TS_CTRL_SNTYPSEL1 | GMAC_TS_CTRL_TSVER2ENA;
++ tsctl |= GMAC_TS_CTRL_TSIPENA | GMAC_TS_CTRL_TSIPV4ENA;
++ printk(KERN_INFO "%s HWTSTAMP_FILTER_PTP_V2_EVENT \n", __func__);
++ break;
++
++ case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ:
++ /*
++ * V2_L2_L4 Master
++ * Delay_Req
++ *
++ * SNAPTYPSEL=0, TSMSTRENA=1, TSEVNTENA=1,
++ * TSVER2ENA=1,TSIPENA=1, TSIPV4ENA=1
++ */
++ tsctl |= GMAC_TS_CTRL_SNTYPSEL0 | GMAC_TS_CTRL_TSMSTRENA;
++ tsctl |= GMAC_TS_CTRL_TSEVNTENA | GMAC_TS_CTRL_TSVER2ENA;
++ tsctl |= GMAC_TS_CTRL_TSIPENA | GMAC_TS_CTRL_TSIPV4ENA;
++ printk(KERN_INFO "%s HWTSTAMP_FILTER_PTP_V2_DELAY_REQ \n", __func__);
++ break;
++
++ case HWTSTAMP_FILTER_PTP_V2_SYNC:
++ /*
++ * V2_L2_L4 Slave
++ * Sync
++ *
++ * SNAPTYPSEL=0, TSMSTRENA=0, TSEVNTENA=1, TSVER2ENA=1,
++ * TSIPENA=1, TSIPV4ENA=1
++ */
++ tsctl |= GMAC_TS_CTRL_SNTYPSEL0 | GMAC_TS_CTRL_TSVER2ENA;
++ tsctl |= GMAC_TS_CTRL_TSEVNTENA | GMAC_TS_CTRL_TSIPENA;
++ tsctl |= GMAC_TS_CTRL_TSIPV4ENA;
++ printk(KERN_INFO "%s HWTSTAMP_FILTER_PTP_V2_SYNC \n", __func__);
++ break;
++
++ case HWTSTAMP_FILTER_ALL:
++ /*
++ * V2_L2_L4 Ethernet any event
++ *
++ * SYNC, Follow_Up, Delay_Req, Delay_Resp,
++ * Pdelay_Req, Pdelay_Resp, Pdelay_Resp_Follow_Up
++ *
++ * GMAC_TS_CTRL_TSENALL
++ */
++ tsctl |= GMAC_TS_CTRL_TSENALL;
++ printk(KERN_INFO "%s HWTSTAMP_FILTER_ALL \n", __func__);
++ break;
++
++ case HWTSTAMP_FILTER_NONE:
++ printk(KERN_INFO "%s HWTSTAMP_FILTER_NONE \n", __func__);
++ priv->hwts &= ~STMMAC_PTP_HWTS_RX_EN;
++ break;
++
++ default:
++ printk(KERN_INFO "%s error ioctl rx type %d \n", __func__, config.rx_filter);
++ /* bad/unknown parameter */
++ return -ERANGE;
++ }
++
++ /* Set the TS CTRL reg */
++ tsctl |= GMAC_TS_CTRL_TSENA;
++ writel(tsctl, priv->ioaddr + GMAC_TS_CTRL);
++ wmb();
++
++ printk(KERN_INFO "%s sync ts_ctl @ value 0x%08x\n", __func__, tsctl);
++
++ return copy_to_user(ifr->ifr_data, &config, sizeof(config)) ?
++ -EFAULT : 0;
++}
++
++
++#define DEFAULT_PTP_CLK 50
++
++/**
++ * stmmac_ptp_init_timestamp - initialise the PTP clock
++ * @priv - pointer to the priv structure
++ *
++ * This function initialises the PTP clock consistent with the method spelled
++ * out in the Snopsys documentation
++ */
++static void stmmac_ptp_init_timestamp(struct stmmac_priv *priv)
++{
++ unsigned long tsctl = GMAC_TS_CTRL_TSENA, flags = 0;
++ uint8_t ssinc = CONFIG_STMMAC_PTP_CLK_MHZ;
++
++ /* Enable TS */
++ writel(tsctl, priv->ioaddr + GMAC_TS_CTRL);
++ wmb();
++
++ /* Write SSINC - number of nano seconds to increment on each clock */
++ if(ssinc == 0){
++ ssinc = DEFAULT_PTP_CLK;
++ }
++ ssinc = 1000/ssinc;
++ writel(ssinc, priv->ioaddr + GMAC_SS_INC);
++ wmb();
++
++ printk(KERN_INFO "%s setting PTP_CLK to 0x%02x\n", __func__, ssinc);
++
++ /* Reset system time registers to zero */
++ writel(0x00000000, priv->ioaddr + GMAC_ST_SEC);
++ writel(0x00000000, priv->ioaddr + GMAC_ST_NSEC);
++ wmb();
++
++ /* Set TSINT to latch values in ST_SEC and ST_NSEC */
++ tsctl |= GMAC_TS_CTRL_TSINT;
++ writel(tsctl, priv->ioaddr + GMAC_TS_CTRL);
++ wmb();
++ printk(KERN_INFO "%s tsctl == 0x%08lx (TSINIT | TSENA)\n", __func__, tsctl);
++
++ spin_lock_irqsave(&priv->tmreg_lock, flags);
++
++ /* Init timecounter */
++ memset(&priv->ccnt, 0, sizeof(priv->ccnt));
++ priv->ccnt.read = stmmac_ptp_read;
++ priv->ccnt.mask = CLOCKSOURCE_MASK(64);
++ priv->ccnt.mult = 1;
++ priv->ccnt.shift = 0;
++
++ /* reset the ns time counter */
++ timecounter_init(&priv->tcnt, &priv->ccnt,
++ ktime_to_ns(ktime_get_real()));
++
++ spin_unlock_irqrestore(&priv->tmreg_lock, flags);
++}
++
++/**
++ * stmmac_ptp_init
++ * @priv - the stmmac private priv structure
++ *
++ * This function performs the required steps for enabling ptp
++ * support. If ptp support has already been loaded it simply calls the
++ * cyclecounter init routine and exits.
++ */
++void stmmac_ptp_init(struct net_device *ndev, struct device * pdev)
++{
++ struct stmmac_priv *priv = netdev_priv(ndev);
++
++ /* Ensure the timestamp interrupt is masked */
++ writel(GMAC_INT_MASK_TSIM, priv->ioaddr + GMAC_INT_MASK);
++
++ /* Fill out PTP callback contents */
++ snprintf(priv->ptp_caps.name, 16, "%pm", ndev->dev_addr);
++ priv->ptp_caps.owner = THIS_MODULE;
++ priv->ptp_caps.max_adj = 0; /* Cannot be adjusted */
++ priv->ptp_caps.n_alarm = 0;
++ priv->ptp_caps.n_ext_ts = 0;
++ priv->ptp_caps.n_per_out = 0;
++ priv->ptp_caps.pps = 0;
++ priv->ptp_caps.adjfreq = stmmac_ptp_adjfreq;
++ priv->ptp_caps.adjtime = stmmac_ptp_adjtime;
++ priv->ptp_caps.gettime = stmmac_ptp_gettime;
++ priv->ptp_caps.settime = stmmac_ptp_settime;
++ priv->ptp_caps.enable = stmmac_ptp_enable;
++
++ spin_lock_init(&priv->tmreg_lock);
++
++ stmmac_ptp_init_timestamp(priv);
++
++ /* Init to default state */
++// priv->hwts = STMMAC_PTP_OVERFLOW_CHECK_ENABLED;
++
++ priv->ptp_clock = ptp_clock_register(&priv->ptp_caps, pdev);
++ if (IS_ERR(priv->ptp_clock)) {
++ priv->ptp_clock = NULL;
++ printk(KERN_ERR "%s ptp_clock_reg failed!\n", __func__);
++ } else {
++ printk(KERN_INFO "%s ptp_clock_reg success!\n", __func__);
++ }
++
++ return;
++}
++
++/**
++ * stmmac_ptp_remove - disable ptp device and stop the overflow check
++ * @priv: pointer to priv struct
++ *
++ * this function stops the ptp support, and cancels the delayed work.
++ */
++void stmmac_ptp_remove(struct stmmac_priv *priv)
++{
++ /* Ensure the timestamp interrupt is masked */
++ writel(GMAC_INT_MASK_TSIM, priv->ioaddr + GMAC_INT_MASK);
++
++ /* stop the overflow check task */
++// priv->hwts &= ~STMMAC_PTP_OVERFLOW_CHECK_ENABLED;
++
++ if (priv->ptp_clock != NULL) {
++ ptp_clock_unregister(priv->ptp_clock);
++ priv->ptp_clock = NULL;
++ printk(KERN_INFO "%s removed ptp_clock\n", __func__);
++ }
++}
++
+--
+1.7.4.1
+