]> rtime.felk.cvut.cz Git - vajnamar/linux-xlnx.git/commitdiff
net: ethernet: xilinx: Add 1588 support for 10G/25G MAC
authorKedareswara rao Appana <appana.durga.rao@xilinx.com>
Tue, 22 Nov 2016 17:15:10 +0000 (22:45 +0530)
committerMichal Simek <michal.simek@xilinx.com>
Tue, 20 Dec 2016 06:58:06 +0000 (07:58 +0100)
This patch adds 1588 support for 10G/25G MAC.

Signed-off-by: Kedareswara rao Appana <appanad@xilinx.com>
Signed-off-by: Michal Simek <michal.simek@xilinx.com>
drivers/net/ethernet/xilinx/xilinx_axienet.h
drivers/net/ethernet/xilinx/xilinx_axienet_main.c

index 9a0ebcdfda732a820d88ae5f26a2e933301c1aa0..3a96f4b0a5795ff368fe653eceb042976bbc3465 100644 (file)
 
 /* AXI Tx Timestamp Stream FIFO Register Definitions */
 #define XAXIFIFO_TXTS_ISR      0x00000000 /* Interrupt Status Register */
+#define XAXIFIFO_TXTS_TXFD     0x00000010 /* Tx Data Write Port */
+#define XAXIFIFO_TXTS_TLR      0x00000014 /* Transmit Length Register */
+#define XAXIFIFO_TXTS_RFO      0x0000001C /* Rx Fifo Occupancy */
+#define XAXIFIFO_TXTS_RDFR     0x00000018 /* Rx Fifo reset */
 #define XAXIFIFO_TXTS_RXFD     0x00000020 /* Rx Data Read Port */
 #define XAXIFIFO_TXTS_RLR      0x00000024 /* Receive Length Register */
+#define XAXIFIFO_TXTS_SRR      0x00000028 /* AXI4-Stream Reset */
 
 #define XAXIFIFO_TXTS_INT_RC_MASK      0x04000000
 #define XAXIFIFO_TXTS_RXFD_MASK                0x7FFFFFFF
+#define XAXIFIFO_TXTS_RESET_MASK       0x000000A5
 #define XAXIFIFO_TXTS_TAG_MASK         0xFFFF0000
 #define XAXIFIFO_TXTS_TAG_SHIFT                16
 
 #define XXV_TICKREG_STATEN_MASK BIT(0)
 #define XXV_MAC_MIN_PKT_LEN    64
 
+/* PTP Packet length */
+#define XAE_TX_PTP_LEN         16
+#define XXV_TX_PTP_LEN         12
+
 /**
  * struct axidma_bd - Axi Dma buffer descriptor layout
  * @next:         MM2S/S2MM Next Descriptor Pointer
@@ -470,7 +480,9 @@ struct axidma_bd {
  * @eth_hasnobuf: Ethernet is configured in Non buf mode.
  * @axienet_config: Ethernet config structure
  * @tx_ts_regs:          Base address for the axififo device address space.
+ * @rx_ts_regs:          Base address for the rx axififo device address space.
  * @tstamp_config: Hardware timestamp config structure.
+ * @tx_ptpheader: Stores the tx ptp header.
  */
 struct axienet_local {
        struct net_device *ndev;
@@ -525,7 +537,9 @@ struct axienet_local {
 
 #ifdef CONFIG_XILINX_AXI_EMAC_HWTSTAMP
        void __iomem *tx_ts_regs;
+       void __iomem *rx_ts_regs;
        struct hwtstamp_config tstamp_config;
+       u8 *tx_ptpheader;
 #endif
 };
 
@@ -546,6 +560,7 @@ enum axienet_ip_type {
 struct axienet_config {
        enum axienet_ip_type mactype;
        void (*setoptions)(struct net_device *ndev, u32 options);
+       u32 tx_ptplen;
 };
 
 /**
@@ -622,6 +637,33 @@ static inline void axienet_txts_iow(struct  axienet_local *lp, off_t reg,
 {
        out_be32((lp->tx_ts_regs + reg), value);
 }
+
+/**
+ * axienet_rxts_ior - Memory mapped AXI FIFO MM S register read
+ * @lp:         Pointer to axienet_local structure
+ * @reg:     Address offset from the base address of AXI FIFO MM S
+ *              core
+ *
+ * Return: the contents of the AXI FIFO MM S register
+ */
+
+static inline u32 axienet_rxts_ior(struct axienet_local *lp, off_t reg)
+{
+       return in_be32(lp->rx_ts_regs + reg);
+}
+
+/**
+ * axienet_rxts_iow - Memory mapper AXI FIFO MM S register write
+ * @lp:         Pointer to axienet_local structure
+ * @reg:     Address offset from the base address of AXI FIFO MM S
+ *              core.
+ * @value:      Value to be written into the AXI FIFO MM S register
+ */
+static inline void axienet_rxts_iow(struct  axienet_local *lp, off_t reg,
+                                   u32 value)
+{
+       out_be32((lp->rx_ts_regs + reg), value);
+}
 #endif
 
 /* Function prototypes visible in xilinx_axienet_mdio.c for other files */
index 44ffa73c9701eaaec5baf0093555968ec08198f5..932386961e0ac712840e99465e10550f0d41fd6b 100644 (file)
@@ -37,6 +37,7 @@
 #include <linux/iopoll.h>
 #include <linux/ptp_classify.h>
 #include <linux/net_tstamp.h>
+#include <linux/random.h>
 #include <net/sock.h>
 #include <linux/xilinx_phy.h>
 
@@ -53,6 +54,7 @@
 
 #define AXIENET_REGS_N         32
 #define AXIENET_TS_HEADER_LEN  8
+#define XXVENET_TS_HEADER_LEN  4
 #define NS_PER_SEC              1000000000ULL /* Nanoseconds per second */
 
 /* Option table for setting up Axi Ethernet hardware options */
@@ -579,6 +581,12 @@ static void axienet_device_reset(struct net_device *ndev)
                        netdev_err(ndev, "Got Set cross check the ref clock");
                        netdev_err(ndev, "Configuration for the mac");
                }
+#ifdef CONFIG_XILINX_AXI_EMAC_HWTSTAMP
+               axienet_rxts_iow(lp, XAXIFIFO_TXTS_RDFR,
+                                XAXIFIFO_TXTS_RESET_MASK);
+               axienet_rxts_iow(lp, XAXIFIFO_TXTS_SRR,
+                                XAXIFIFO_TXTS_RESET_MASK);
+#endif
        }
 
        if ((lp->axienet_config->mactype == XAXIENET_1G) &&
@@ -679,6 +687,7 @@ static void axienet_tx_hwtstamp(struct axienet_local *lp,
        u32 sec = 0, nsec = 0, val;
        u64 time64;
        int err = 0;
+       u32 count, len = lp->axienet_config->tx_ptplen;
        struct skb_shared_hwtstamps *shhwtstamps =
                skb_hwtstamps((struct sk_buff *)cur_p->ptp_tx_skb);
 
@@ -689,10 +698,9 @@ static void axienet_tx_hwtstamp(struct axienet_local *lp,
        /* If FIFO is configured in cut through Mode we will get Rx complete
         * interrupt even one byte is there in the fifo wait for the full packet
         */
-       err = readl_poll_timeout(lp->tx_ts_regs + XAXIFIFO_TXTS_RLR,
-                                val, ((val & XAXIFIFO_TXTS_RXFD_MASK) >= 16),
-                                10, 1000000);
-
+       err = readl_poll_timeout_atomic(lp->tx_ts_regs + XAXIFIFO_TXTS_RLR, val,
+                                       ((val & XAXIFIFO_TXTS_RXFD_MASK) >=
+                                       len), 0, 1000000);
        if (err)
                netdev_err(lp->ndev, "%s: Didn't get the full timestamp packet",
                            __func__);
@@ -701,21 +709,87 @@ static void axienet_tx_hwtstamp(struct axienet_local *lp,
        sec  = axienet_txts_ior(lp, XAXIFIFO_TXTS_RXFD);
        val = axienet_txts_ior(lp, XAXIFIFO_TXTS_RXFD);
        val = ((val & XAXIFIFO_TXTS_TAG_MASK) >> XAXIFIFO_TXTS_TAG_SHIFT);
-       if (val != cur_p->ptp_tx_ts_tag)
-               dev_info(lp->dev, "Mismatching 2-step tag. Got %x,"
-                        "expected %x\n", val, cur_p->ptp_tx_ts_tag);
+       if (val != cur_p->ptp_tx_ts_tag) {
+               count = axienet_txts_ior(lp, XAXIFIFO_TXTS_RFO);
+               while (count) {
+                       nsec = axienet_txts_ior(lp, XAXIFIFO_TXTS_RXFD);
+                       sec  = axienet_txts_ior(lp, XAXIFIFO_TXTS_RXFD);
+                       val = axienet_txts_ior(lp, XAXIFIFO_TXTS_RXFD);
+                       val = ((val & XAXIFIFO_TXTS_TAG_MASK) >>
+                               XAXIFIFO_TXTS_TAG_SHIFT);
+                       if (val == cur_p->ptp_tx_ts_tag)
+                               break;
+                       count = axienet_txts_ior(lp, XAXIFIFO_TXTS_RFO);
+               }
+               if (val != cur_p->ptp_tx_ts_tag) {
+                       dev_info(lp->dev, "Mismatching 2-step tag. Got %x",
+                                val);
+                       dev_info(lp->dev, "Expected %x\n",
+                                cur_p->ptp_tx_ts_tag);
+               }
+       }
 
-       val = axienet_txts_ior(lp, XAXIFIFO_TXTS_RXFD);
+       if (lp->axienet_config->mactype != XAXIENET_10G_25G)
+               val = axienet_txts_ior(lp, XAXIFIFO_TXTS_RXFD);
 
        time64 = sec * NS_PER_SEC + nsec;
        memset(shhwtstamps, 0, sizeof(struct skb_shared_hwtstamps));
        shhwtstamps->hwtstamp = ns_to_ktime(time64);
-       skb_pull((struct sk_buff *)cur_p->ptp_tx_skb, AXIENET_TS_HEADER_LEN);
+       if (lp->axienet_config->mactype != XAXIENET_10G_25G)
+               skb_pull((struct sk_buff *)cur_p->ptp_tx_skb,
+                        AXIENET_TS_HEADER_LEN);
 
        skb_tstamp_tx((struct sk_buff *)cur_p->ptp_tx_skb, shhwtstamps);
        dev_kfree_skb_any((struct sk_buff *)cur_p->ptp_tx_skb);
        cur_p->ptp_tx_skb = 0;
 }
+
+/**
+ * axienet_rx_hwtstamp - Read rx timestamp from hw and update it to the skbuff
+ * @lp:                Pointer to axienet local structure
+ * @skb:       Pointer to the sk_buff structure
+ *
+ * Return:     None.
+ */
+static void axienet_rx_hwtstamp(struct axienet_local *lp,
+                               struct sk_buff *skb)
+{
+       u32 sec = 0, nsec = 0, val;
+       u64 time64;
+       int err = 0;
+       struct skb_shared_hwtstamps *shhwtstamps = skb_hwtstamps(skb);
+
+       val = axienet_rxts_ior(lp, XAXIFIFO_TXTS_ISR);
+       if (unlikely(!(val & XAXIFIFO_TXTS_INT_RC_MASK))) {
+               dev_info(lp->dev, "Did't get FIFO rx interrupt %d\n", val);
+               return;
+       }
+
+       val = axienet_rxts_ior(lp, XAXIFIFO_TXTS_RFO);
+       if (!val)
+               return;
+
+       /* If FIFO is configured in cut through Mode we will get Rx complete
+        * interrupt even one byte is there in the fifo wait for the full packet
+        */
+       err = readl_poll_timeout_atomic(lp->rx_ts_regs + XAXIFIFO_TXTS_RLR, val,
+                                       ((val & XAXIFIFO_TXTS_RXFD_MASK) >= 12),
+                                       0, 1000000);
+       if (err) {
+               netdev_err(lp->ndev, "%s: Didn't get the full timestamp packet",
+                          __func__);
+               return;
+       }
+
+       nsec = axienet_rxts_ior(lp, XAXIFIFO_TXTS_RXFD);
+       sec  = axienet_rxts_ior(lp, XAXIFIFO_TXTS_RXFD);
+       val = axienet_rxts_ior(lp, XAXIFIFO_TXTS_RXFD);
+
+       if (lp->tstamp_config.rx_filter == HWTSTAMP_FILTER_ALL) {
+               time64 = sec * NS_PER_SEC + nsec;
+               shhwtstamps->hwtstamp = ns_to_ktime(time64);
+       }
+}
 #endif
 
 /**
@@ -816,6 +890,7 @@ static void axienet_create_tsheader(struct axienet_local *lp, u8 *buf,
 {
        struct axidma_bd *cur_p;
        u64 val;
+       u32 tmp;
 
        cur_p = &lp->tx_bd_v[lp->tx_bd_tail];
 
@@ -834,6 +909,10 @@ static void axienet_create_tsheader(struct axienet_local *lp, u8 *buf,
                memcpy(&val, buf, AXIENET_TS_HEADER_LEN);
                swab64s(&val);
                memcpy(buf, &val, AXIENET_TS_HEADER_LEN);
+       } else if (lp->axienet_config->mactype == XAXIENET_10G_25G) {
+               memcpy(&tmp, buf, XXVENET_TS_HEADER_LEN);
+               axienet_txts_iow(lp, XAXIFIFO_TXTS_TXFD, tmp);
+               axienet_txts_iow(lp, XAXIFIFO_TXTS_TLR, XXVENET_TS_HEADER_LEN);
        }
 }
 #endif
@@ -875,8 +954,9 @@ static int axienet_start_xmit(struct sk_buff *skb, struct net_device *ndev)
        }
 
 #ifdef CONFIG_XILINX_AXI_EMAC_HWTSTAMP
-       if ((lp->tstamp_config.tx_type == HWTSTAMP_TX_ONESTEP_SYNC) ||
-           (lp->tstamp_config.tx_type == HWTSTAMP_TX_ON)) {
+       if (((lp->tstamp_config.tx_type == HWTSTAMP_TX_ONESTEP_SYNC) ||
+            (lp->tstamp_config.tx_type == HWTSTAMP_TX_ON)) &&
+           (lp->axienet_config->mactype != XAXIENET_10G_25G)) {
                u8 *tmp;
                struct sk_buff *new_skb;
 
@@ -916,6 +996,19 @@ static int axienet_start_xmit(struct sk_buff *skb, struct net_device *ndev)
                                cur_p->ptp_tx_skb = (unsigned long)skb_get(skb);
                        }
                }
+       } else if ((skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) &&
+                  (lp->axienet_config->mactype == XAXIENET_10G_25G)) {
+               cur_p->ptp_tx_ts_tag = (prandom_u32() &
+                                               ~XAXIFIFO_TXTS_TAG_MASK) + 1;
+               if (lp->tstamp_config.tx_type == HWTSTAMP_TX_ONESTEP_SYNC) {
+                       axienet_create_tsheader(lp, lp->tx_ptpheader,
+                                               TX_TS_OP_ONESTEP);
+               } else {
+                       axienet_create_tsheader(lp, lp->tx_ptpheader,
+                                               TX_TS_OP_TWOSTEP);
+                       skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS;
+                       cur_p->ptp_tx_skb = (phys_addr_t)skb_get(skb);
+               }
        }
 #endif
        /* Work around for XXV MAC as MAC will drop the packets
@@ -1025,7 +1118,8 @@ static int axienet_recv(struct net_device *ndev, int budget)
 
                skb_put(skb, length);
 #ifdef CONFIG_XILINX_AXI_EMAC_HWTSTAMP
-               if (lp->tstamp_config.rx_filter == HWTSTAMP_FILTER_ALL) {
+               if (lp->tstamp_config.rx_filter == HWTSTAMP_FILTER_ALL &&
+                   (lp->axienet_config->mactype != XAXIENET_10G_25G)) {
                        u32 sec, nsec;
                        u64 time64;
                        struct skb_shared_hwtstamps *shhwtstamps;
@@ -1048,6 +1142,8 @@ static int axienet_recv(struct net_device *ndev, int budget)
                        time64 = sec * NS_PER_SEC + nsec;
                        shhwtstamps = skb_hwtstamps(skb);
                        shhwtstamps->hwtstamp = ns_to_ktime(time64);
+               } else {
+                       axienet_rx_hwtstamp(lp, skb);
                }
 #endif
                skb->protocol = eth_type_trans(skb, ndev);
@@ -1525,7 +1621,8 @@ static int axienet_set_timestamp_mode(struct axienet_local *lp,
                return -ERANGE;
        }
 
-       axienet_iow(lp, XAE_TC_OFFSET, regval);
+       if (lp->axienet_config->mactype != XAXIENET_10G_25G)
+               axienet_iow(lp, XAE_TC_OFFSET, regval);
 
        /* Read the current value in the MAC RX RCW1 register */
        regval = axienet_ior(lp, XAE_RCW1_OFFSET);
@@ -1540,7 +1637,9 @@ static int axienet_set_timestamp_mode(struct axienet_local *lp,
                regval |= XAE_RCW1_INBAND1588_MASK;
        }
 
-       axienet_iow(lp, XAE_RCW1_OFFSET, regval);
+       if (lp->axienet_config->mactype != XAXIENET_10G_25G)
+               axienet_iow(lp, XAE_RCW1_OFFSET, regval);
+
        return 0;
 }
 
@@ -2082,16 +2181,19 @@ static void axienet_dma_err_handler(unsigned long data)
 static const struct axienet_config axienet_1g_config = {
        .mactype = XAXIENET_1G,
        .setoptions = axienet_setoptions,
+       .tx_ptplen = XAE_TX_PTP_LEN,
 };
 
 static const struct axienet_config axienet_10g_config = {
        .mactype = XAXIENET_LEGACY_10G,
        .setoptions = axienet_setoptions,
+       .tx_ptplen = XAE_TX_PTP_LEN,
 };
 
 static const struct axienet_config axienet_10g25g_config = {
        .mactype = XAXIENET_10G_25G,
        .setoptions = xxvenet_setoptions,
+       .tx_ptplen = XXV_TX_PTP_LEN,
 };
 
 /* Match table for of_platform binding */
@@ -2228,7 +2330,7 @@ static int axienet_probe(struct platform_device *pdev)
                lp->eth_irq = platform_get_irq(pdev, 0);
 
 #ifdef CONFIG_XILINX_AXI_EMAC_HWTSTAMP
-       struct resource txtsres;
+       struct resource txtsres, rxtsres;
 
        /* Find AXI Stream FIFO */
        np = of_parse_phandle(pdev->dev.of_node, "axififo-connected", 0);
@@ -2250,6 +2352,35 @@ static int axienet_probe(struct platform_device *pdev)
                ret = PTR_ERR(lp->tx_ts_regs);
                goto free_netdev;
        }
+
+       if (lp->axienet_config->mactype == XAXIENET_10G_25G) {
+               np = of_parse_phandle(pdev->dev.of_node, "xlnx,rxtsfifo",
+                                     0);
+               if (IS_ERR(np)) {
+                       dev_err(&pdev->dev,
+                               "couldn't find rx-timestamp FIFO\n");
+                       ret = PTR_ERR(np);
+                       goto free_netdev;
+               }
+
+               ret = of_address_to_resource(np, 0, &rxtsres);
+               if (ret) {
+                       dev_err(&pdev->dev,
+                               "unable to get rx-timestamp resource\n");
+                       goto free_netdev;
+               }
+
+               lp->rx_ts_regs = devm_ioremap_resource(&pdev->dev, &rxtsres);
+               if (IS_ERR(lp->rx_ts_regs)) {
+                       dev_err(&pdev->dev, "couldn't map rx-timestamp regs\n");
+                       ret = PTR_ERR(lp->rx_ts_regs);
+                       goto free_netdev;
+               }
+               lp->tx_ptpheader = devm_kzalloc(&pdev->dev,
+                                               XXVENET_TS_HEADER_LEN,
+                                               GFP_KERNEL);
+       }
+
        of_node_put(np);
 #endif