Add support for timestamping transmit packets.  We allocate SYNC
messages to queue 1, every other message to queue 0.

Signed-off-by: Russell King <rmk+ker...@armlinux.org.uk>
---
 drivers/net/ethernet/marvell/mvpp2/mvpp2.h    |  48 +++++-
 .../net/ethernet/marvell/mvpp2/mvpp2_main.c   | 155 ++++++++++++++++--
 2 files changed, 192 insertions(+), 11 deletions(-)

diff --git a/drivers/net/ethernet/marvell/mvpp2/mvpp2.h 
b/drivers/net/ethernet/marvell/mvpp2/mvpp2.h
index 50956551b336..834775843067 100644
--- a/drivers/net/ethernet/marvell/mvpp2/mvpp2.h
+++ b/drivers/net/ethernet/marvell/mvpp2/mvpp2.h
@@ -12,6 +12,7 @@
 #include <linux/interrupt.h>
 #include <linux/kernel.h>
 #include <linux/netdevice.h>
+#include <linux/net_tstamp.h>
 #include <linux/phy.h>
 #include <linux/phylink.h>
 #include <net/flow_offload.h>
@@ -804,6 +805,43 @@ enum mvpp2_prs_l3_cast {
        MVPP2_PRS_L3_BROAD_CAST
 };
 
+/* PTP descriptor constants. The low bits of the descriptor are stored
+ * separately from the high bits.
+ */
+#define MVPP22_PTP_DESC_MASK_LOW       0xfff
+
+/* PTPAction */
+enum mvpp22_ptp_action {
+       MVPP22_PTP_ACTION_NONE = 0,
+       MVPP22_PTP_ACTION_FORWARD = 1,
+       MVPP22_PTP_ACTION_CAPTURE = 3,
+       /* The following have not been verified */
+       MVPP22_PTP_ACTION_ADDTIME = 4,
+       MVPP22_PTP_ACTION_ADDCORRECTEDTIME = 5,
+       MVPP22_PTP_ACTION_CAPTUREADDTIME = 6,
+       MVPP22_PTP_ACTION_CAPTUREADDCORRECTEDTIME = 7,
+       MVPP22_PTP_ACTION_ADDINGRESSTIME = 8,
+       MVPP22_PTP_ACTION_CAPTUREADDINGRESSTIME = 9,
+       MVPP22_PTP_ACTION_CAPTUREINGRESSTIME = 10,
+};
+
+/* PTPPacketFormat */
+enum mvpp22_ptp_packet_format {
+       MVPP22_PTP_PKT_FMT_PTPV2 = 0,
+       MVPP22_PTP_PKT_FMT_PTPV1 = 1,
+       MVPP22_PTP_PKT_FMT_Y1731 = 2,
+       MVPP22_PTP_PKT_FMT_NTPTS = 3,
+       MVPP22_PTP_PKT_FMT_NTPRX = 4,
+       MVPP22_PTP_PKT_FMT_NTPTX = 5,
+       MVPP22_PTP_PKT_FMT_TWAMP = 6,
+};
+
+#define MVPP22_PTP_ACTION(x)           (((x) & 15) << 0)
+#define MVPP22_PTP_PACKETFORMAT(x)     (((x) & 7) << 4)
+#define MVPP22_PTP_MACTIMESTAMPINGEN   BIT(11)
+#define MVPP22_PTP_TIMESTAMPENTRYID(x) (((x) & 31) << 12)
+#define MVPP22_PTP_TIMESTAMPQUEUESELECT        BIT(18)
+
 /* BM constants */
 #define MVPP2_BM_JUMBO_BUF_NUM         512
 #define MVPP2_BM_LONG_BUF_NUM          1024
@@ -1022,6 +1060,11 @@ struct mvpp2_ethtool_fs {
        struct ethtool_rxnfc rxnfc;
 };
 
+struct mvpp2_hwtstamp_queue {
+       struct sk_buff *skb[32];
+       u8 next;
+};
+
 struct mvpp2_port {
        u8 id;
 
@@ -1108,6 +1151,8 @@ struct mvpp2_port {
 
        bool hwtstamp;
        bool rx_hwtstamp;
+       enum hwtstamp_tx_types tx_hwtstamp_type;
+       struct mvpp2_hwtstamp_queue tx_hwtstamp_queue[2];
 };
 
 /* The mvpp2_tx_desc and mvpp2_rx_desc structures describe the
@@ -1176,7 +1221,8 @@ struct mvpp22_tx_desc {
        u8  packet_offset;
        u8  phys_txq;
        __le16 data_size;
-       __le64 reserved1;
+       __le32 ptp_descriptor;
+       __le32 reserved2;
        __le64 buf_dma_addr_ptp;
        __le64 buf_cookie_misc;
 };
diff --git a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c 
b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c
index f3148e033bfe..7130e31c7431 100644
--- a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c
+++ b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c
@@ -28,6 +28,7 @@
 #include <linux/phy.h>
 #include <linux/phylink.h>
 #include <linux/phy/phy.h>
+#include <linux/ptp_classify.h>
 #include <linux/clk.h>
 #include <linux/hrtimer.h>
 #include <linux/ktime.h>
@@ -2984,13 +2985,19 @@ static irqreturn_t mvpp2_isr(int irq, void *dev_id)
 
 static void mvpp2_isr_handle_ptp_queue(struct mvpp2_port *port, int nq)
 {
+       struct skb_shared_hwtstamps shhwtstamps;
+       struct mvpp2_hwtstamp_queue *queue;
+       struct sk_buff *skb;
        void __iomem *ptp_q;
+       unsigned int id;
        u32 r0, r1, r2;
 
        ptp_q = port->priv->iface_base + MVPP22_PTP_BASE(port->gop_id);
        if (nq)
                ptp_q += MVPP22_PTP_TX_Q1_R0 - MVPP22_PTP_TX_Q0_R0;
 
+       queue = &port->tx_hwtstamp_queue[nq];
+
        while (1) {
                r0 = readl_relaxed(ptp_q + MVPP22_PTP_TX_Q0_R0) & 0xffff;
                if (!r0)
@@ -2998,6 +3005,18 @@ static void mvpp2_isr_handle_ptp_queue(struct mvpp2_port 
*port, int nq)
 
                r1 = readl_relaxed(ptp_q + MVPP22_PTP_TX_Q0_R1) & 0xffff;
                r2 = readl_relaxed(ptp_q + MVPP22_PTP_TX_Q0_R2) & 0xffff;
+
+               id = (r0 >> 1) & 31;
+
+               skb = queue->skb[id];
+               queue->skb[id] = NULL;
+               if (skb) {
+                       u32 ts = r2 << 19 | r1 << 3 | r0 >> 13;
+
+                       mvpp22_tai_tstamp(port->priv->tai, ts, &shhwtstamps);
+                       skb_tstamp_tx(skb, &shhwtstamps);
+                       dev_kfree_skb_any(skb);
+               }
        }
 }
 
@@ -3654,6 +3673,92 @@ tx_desc_unmap_put(struct mvpp2_port *port, struct 
mvpp2_tx_queue *txq,
        mvpp2_txq_desc_put(txq);
 }
 
+static void mvpp2_txdesc_clear_ptp(struct mvpp2_port *port,
+                                  struct mvpp2_tx_desc *desc)
+{
+       /* We only need to clear the low bits */
+       if (port->priv->hw_version != MVPP21)
+               desc->pp22.ptp_descriptor &=
+                       cpu_to_le32(~MVPP22_PTP_DESC_MASK_LOW);
+}
+
+static bool mvpp2_tx_hw_tstamp(struct mvpp2_port *port,
+                              struct mvpp2_tx_desc *tx_desc,
+                              struct sk_buff *skb)
+{
+       struct mvpp2_hwtstamp_queue *queue;
+       unsigned int mtype, type, i;
+       struct ptp_header *hdr;
+       u64 ptpdesc;
+
+       if (port->priv->hw_version == MVPP21 ||
+           port->tx_hwtstamp_type == HWTSTAMP_TX_OFF)
+               return false;
+
+       type = ptp_classify_raw(skb);
+       if (!type)
+               return false;
+
+       hdr = ptp_parse_header(skb, type);
+       if (!hdr)
+               return false;
+
+       ptpdesc = MVPP22_PTP_MACTIMESTAMPINGEN |
+                 MVPP22_PTP_ACTION_CAPTURE;
+       queue = &port->tx_hwtstamp_queue[0];
+
+       switch (type & PTP_CLASS_VMASK) {
+       case PTP_CLASS_V1:
+               ptpdesc |= MVPP22_PTP_PACKETFORMAT(MVPP22_PTP_PKT_FMT_PTPV1);
+               break;
+
+       case PTP_CLASS_V2:
+               ptpdesc |= MVPP22_PTP_PACKETFORMAT(MVPP22_PTP_PKT_FMT_PTPV2);
+               mtype = hdr->tsmt & 15;
+               /* Direct PTP Sync messages to queue 1 */
+               if (mtype == 0) {
+                       ptpdesc |= MVPP22_PTP_TIMESTAMPQUEUESELECT;
+                       queue = &port->tx_hwtstamp_queue[1];
+               }
+               break;
+       }
+
+       /* Take a reference on the skb and insert into our queue */
+       i = queue->next;
+       queue->next = (i + 1) & 31;
+       if (queue->skb[i])
+               dev_kfree_skb_any(queue->skb[i]);
+       queue->skb[i] = skb_get(skb);
+
+       ptpdesc |= MVPP22_PTP_TIMESTAMPENTRYID(i);
+
+       /*
+        * 3:0          - PTPAction
+        * 6:4          - PTPPacketFormat
+        * 7            - PTP_CF_WraparoundCheckEn
+        * 9:8          - IngressTimestampSeconds[1:0]
+        * 10           - Reserved
+        * 11           - MACTimestampingEn
+        * 17:12        - PTP_TimestampQueueEntryID[5:0]
+        * 18           - PTPTimestampQueueSelect
+        * 19           - UDPChecksumUpdateEn
+        * 27:20        - TimestampOffset
+        *                      PTP, NTPTransmit, OWAMP/TWAMP - L3 to PTP header
+        *                      NTPTs, Y.1731 - L3 to timestamp entry
+        * 35:28        - UDP Checksum Offset
+        *
+        * stored in tx descriptor bits 75:64 (11:0) and 191:168 (35:12)
+        */
+       tx_desc->pp22.ptp_descriptor &=
+               cpu_to_le32(~MVPP22_PTP_DESC_MASK_LOW);
+       tx_desc->pp22.ptp_descriptor |=
+               cpu_to_le32(ptpdesc & MVPP22_PTP_DESC_MASK_LOW);
+       tx_desc->pp22.buf_dma_addr_ptp &= cpu_to_le64(~0xffffff0000000000ULL);
+       tx_desc->pp22.buf_dma_addr_ptp |= cpu_to_le64((ptpdesc >> 12) << 40);
+
+       return true;
+}
+
 /* Handle tx fragmentation processing */
 static int mvpp2_tx_frag_process(struct mvpp2_port *port, struct sk_buff *skb,
                                 struct mvpp2_tx_queue *aggr_txq,
@@ -3670,6 +3775,7 @@ static int mvpp2_tx_frag_process(struct mvpp2_port *port, 
struct sk_buff *skb,
                void *addr = skb_frag_address(frag);
 
                tx_desc = mvpp2_txq_next_desc_get(aggr_txq);
+               mvpp2_txdesc_clear_ptp(port, tx_desc);
                mvpp2_txdesc_txq_set(port, tx_desc, txq->id);
                mvpp2_txdesc_size_set(port, tx_desc, skb_frag_size(frag));
 
@@ -3719,6 +3825,7 @@ static inline void mvpp2_tso_put_hdr(struct sk_buff *skb,
        struct mvpp2_tx_desc *tx_desc = mvpp2_txq_next_desc_get(aggr_txq);
        dma_addr_t addr;
 
+       mvpp2_txdesc_clear_ptp(port, tx_desc);
        mvpp2_txdesc_txq_set(port, tx_desc, txq->id);
        mvpp2_txdesc_size_set(port, tx_desc, hdr_sz);
 
@@ -3743,6 +3850,7 @@ static inline int mvpp2_tso_put_data(struct sk_buff *skb,
        struct mvpp2_tx_desc *tx_desc = mvpp2_txq_next_desc_get(aggr_txq);
        dma_addr_t buf_dma_addr;
 
+       mvpp2_txdesc_clear_ptp(port, tx_desc);
        mvpp2_txdesc_txq_set(port, tx_desc, txq->id);
        mvpp2_txdesc_size_set(port, tx_desc, sz);
 
@@ -3859,6 +3967,9 @@ static netdev_tx_t mvpp2_tx(struct sk_buff *skb, struct 
net_device *dev)
 
        /* Get a descriptor for the first part of the packet */
        tx_desc = mvpp2_txq_next_desc_get(aggr_txq);
+       if (!(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) ||
+           !mvpp2_tx_hw_tstamp(port, tx_desc, skb))
+               mvpp2_txdesc_clear_ptp(port, tx_desc);
        mvpp2_txdesc_txq_set(port, tx_desc, txq->id);
        mvpp2_txdesc_size_set(port, tx_desc, skb_headlen(skb));
 
@@ -4618,6 +4729,7 @@ static int mvpp2_set_ts_config(struct mvpp2_port *port, 
struct ifreq *ifr)
 {
        struct hwtstamp_config config;
        void __iomem *ptp;
+       u32 gcr, int_mask;
 
        if (copy_from_user(&config, ifr->ifr_data, sizeof(config)))
                return -EFAULT;
@@ -4625,30 +4737,51 @@ static int mvpp2_set_ts_config(struct mvpp2_port *port, 
struct ifreq *ifr)
        if (config.flags)
                return -EINVAL;
 
-       if (config.tx_type != HWTSTAMP_TX_OFF)
+       if (config.tx_type != HWTSTAMP_TX_OFF &&
+           config.tx_type != HWTSTAMP_TX_ON)
                return -ERANGE;
 
        ptp = port->priv->iface_base + MVPP22_PTP_BASE(port->gop_id);
+
+       int_mask = gcr = 0;
+       if (config.tx_type != HWTSTAMP_TX_OFF) {
+               gcr |= MVPP22_PTP_GCR_TSU_ENABLE | MVPP22_PTP_GCR_TX_RESET;
+               int_mask |= MVPP22_PTP_INT_MASK_QUEUE1 |
+                           MVPP22_PTP_INT_MASK_QUEUE0;
+       }
+
+       /* It seems we must also release the TX reset when enabling the TSU */
+       if (config.rx_filter != HWTSTAMP_FILTER_NONE)
+               gcr |= MVPP22_PTP_GCR_TSU_ENABLE | MVPP22_PTP_GCR_RX_RESET |
+                      MVPP22_PTP_GCR_TX_RESET;
+
+       if (gcr & MVPP22_PTP_GCR_TSU_ENABLE)
+               mvpp22_tai_start(port->priv->tai);
+
        if (config.rx_filter != HWTSTAMP_FILTER_NONE) {
                config.rx_filter = HWTSTAMP_FILTER_ALL;
-               mvpp22_tai_start(port->priv->tai);
                mvpp2_modify(ptp + MVPP22_PTP_GCR,
                             MVPP22_PTP_GCR_RX_RESET |
                             MVPP22_PTP_GCR_TX_RESET |
-                            MVPP22_PTP_GCR_TSU_ENABLE,
-                            MVPP22_PTP_GCR_RX_RESET |
-                            MVPP22_PTP_GCR_TX_RESET |
-                            MVPP22_PTP_GCR_TSU_ENABLE);
+                            MVPP22_PTP_GCR_TSU_ENABLE, gcr);
                port->rx_hwtstamp = true;
        } else {
                port->rx_hwtstamp = false;
                mvpp2_modify(ptp + MVPP22_PTP_GCR,
                             MVPP22_PTP_GCR_RX_RESET |
                             MVPP22_PTP_GCR_TX_RESET |
-                            MVPP22_PTP_GCR_TSU_ENABLE, 0);
-               mvpp22_tai_stop(port->priv->tai);
+                            MVPP22_PTP_GCR_TSU_ENABLE, gcr);
        }
 
+       mvpp2_modify(ptp + MVPP22_PTP_INT_MASK,
+                    MVPP22_PTP_INT_MASK_QUEUE1 |
+                    MVPP22_PTP_INT_MASK_QUEUE0, int_mask);
+
+       if (!(gcr & MVPP22_PTP_GCR_TSU_ENABLE))
+               mvpp22_tai_stop(port->priv->tai);
+
+       port->tx_hwtstamp_type = config.tx_type;
+
        if (copy_to_user(ifr->ifr_data, &config, sizeof(config)))
                return -EFAULT;
 
@@ -4661,7 +4794,7 @@ static int mvpp2_get_ts_config(struct mvpp2_port *port, 
struct ifreq *ifr)
 
        memset(&config, 0, sizeof(config));
 
-       config.tx_type = HWTSTAMP_TX_OFF;
+       config.tx_type = port->tx_hwtstamp_type;
        config.rx_filter = port->rx_hwtstamp ?
                HWTSTAMP_FILTER_ALL : HWTSTAMP_FILTER_NONE;
 
@@ -4683,9 +4816,11 @@ static int mvpp2_ethtool_get_ts_info(struct net_device 
*dev,
        info->so_timestamping = SOF_TIMESTAMPING_TX_SOFTWARE |
                                SOF_TIMESTAMPING_RX_SOFTWARE |
                                SOF_TIMESTAMPING_SOFTWARE |
+                               SOF_TIMESTAMPING_TX_HARDWARE |
                                SOF_TIMESTAMPING_RX_HARDWARE |
                                SOF_TIMESTAMPING_RAW_HARDWARE;
-       info->tx_types = BIT(HWTSTAMP_TX_OFF);
+       info->tx_types = BIT(HWTSTAMP_TX_OFF) |
+                        BIT(HWTSTAMP_TX_ON);
        info->rx_filters = BIT(HWTSTAMP_FILTER_NONE) |
                           BIT(HWTSTAMP_FILTER_ALL);
 
-- 
2.20.1

Reply via email to