Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net
[firefly-linux-kernel-4.4.55.git] / drivers / net / ethernet / sfc / ptp.c
index 3f93624fc27313134fa0682bc67cf154d8af0a31..07f6baa15c0cd4cc075130d45130729eb77c7d1b 100644 (file)
@@ -99,6 +99,9 @@
 #define PTP_V2_VERSION_LENGTH  1
 #define PTP_V2_VERSION_OFFSET  29
 
+#define PTP_V2_UUID_LENGTH     8
+#define PTP_V2_UUID_OFFSET     48
+
 /* Although PTP V2 UUIDs are comprised a ClockIdentity (8) and PortNumber (2),
  * the MC only captures the last six bytes of the clock identity. These values
  * reflect those, not the ones used in the standard.  The standard permits
@@ -429,13 +432,10 @@ static int efx_ptp_process_times(struct efx_nic *efx, u8 *synch_buf,
        unsigned number_readings = (response_length /
                               MC_CMD_PTP_OUT_SYNCHRONIZE_TIMESET_LEN);
        unsigned i;
-       unsigned min;
-       unsigned min_set = 0;
        unsigned total;
        unsigned ngood = 0;
        unsigned last_good = 0;
        struct efx_ptp_data *ptp = efx->ptp_data;
-       bool min_valid = false;
        u32 last_sec;
        u32 start_sec;
        struct timespec delta;
@@ -443,35 +443,17 @@ static int efx_ptp_process_times(struct efx_nic *efx, u8 *synch_buf,
        if (number_readings == 0)
                return -EAGAIN;
 
-       /* Find minimum value in this set of results, discarding clearly
-        * erroneous results.
+       /* Read the set of results and increment stats for any results that
+        * appera to be erroneous.
         */
        for (i = 0; i < number_readings; i++) {
                efx_ptp_read_timeset(synch_buf, &ptp->timeset[i]);
                synch_buf += MC_CMD_PTP_OUT_SYNCHRONIZE_TIMESET_LEN;
-               if (ptp->timeset[i].window > SYNCHRONISATION_GRANULARITY_NS) {
-                       if (min_valid) {
-                               if (ptp->timeset[i].window < min_set)
-                                       min_set = ptp->timeset[i].window;
-                       } else {
-                               min_valid = true;
-                               min_set = ptp->timeset[i].window;
-                       }
-               }
-       }
-
-       if (min_valid) {
-               if (ptp->base_sync_valid && (min_set > ptp->base_sync_ns))
-                       min = ptp->base_sync_ns;
-               else
-                       min = min_set;
-       } else {
-               min = SYNCHRONISATION_GRANULARITY_NS;
        }
 
-       /* Discard excessively long synchronise durations.  The MC times
-        * when it finishes reading the host time so the corrected window
-        * time should be fairly constant for a given platform.
+       /* Find the last good host-MC synchronization result. The MC times
+        * when it finishes reading the host time so the corrected window time
+        * should be fairly constant for a given platform.
         */
        total = 0;
        for (i = 0; i < number_readings; i++)
@@ -489,8 +471,8 @@ static int efx_ptp_process_times(struct efx_nic *efx, u8 *synch_buf,
 
        if (ngood == 0) {
                netif_warn(efx, drv, efx->net_dev,
-                          "PTP no suitable synchronisations %dns %dns\n",
-                          ptp->base_sync_ns, min_set);
+                          "PTP no suitable synchronisations %dns\n",
+                          ptp->base_sync_ns);
                return -EAGAIN;
        }
 
@@ -1006,43 +988,53 @@ bool efx_ptp_is_ptp_tx(struct efx_nic *efx, struct sk_buff *skb)
  * the receive timestamp from the MC - this will probably occur after the
  * packet arrival because of the processing in the MC.
  */
-static void efx_ptp_rx(struct efx_channel *channel, struct sk_buff *skb)
+static bool efx_ptp_rx(struct efx_channel *channel, struct sk_buff *skb)
 {
        struct efx_nic *efx = channel->efx;
        struct efx_ptp_data *ptp = efx->ptp_data;
        struct efx_ptp_match *match = (struct efx_ptp_match *)skb->cb;
-       u8 *data;
+       u8 *match_data_012, *match_data_345;
        unsigned int version;
 
        match->expiry = jiffies + msecs_to_jiffies(PKT_EVENT_LIFETIME_MS);
 
        /* Correct version? */
        if (ptp->mode == MC_CMD_PTP_MODE_V1) {
-               if (skb->len < PTP_V1_MIN_LENGTH) {
-                       netif_receive_skb(skb);
-                       return;
+               if (!pskb_may_pull(skb, PTP_V1_MIN_LENGTH)) {
+                       return false;
                }
                version = ntohs(*(__be16 *)&skb->data[PTP_V1_VERSION_OFFSET]);
                if (version != PTP_VERSION_V1) {
-                       netif_receive_skb(skb);
-                       return;
+                       return false;
                }
+
+               /* PTP V1 uses all six bytes of the UUID to match the packet
+                * to the timestamp
+                */
+               match_data_012 = skb->data + PTP_V1_UUID_OFFSET;
+               match_data_345 = skb->data + PTP_V1_UUID_OFFSET + 3;
        } else {
-               if (skb->len < PTP_V2_MIN_LENGTH) {
-                       netif_receive_skb(skb);
-                       return;
+               if (!pskb_may_pull(skb, PTP_V2_MIN_LENGTH)) {
+                       return false;
                }
                version = skb->data[PTP_V2_VERSION_OFFSET];
-
-               BUG_ON(ptp->mode != MC_CMD_PTP_MODE_V2);
-               BUILD_BUG_ON(PTP_V1_UUID_OFFSET != PTP_V2_MC_UUID_OFFSET);
-               BUILD_BUG_ON(PTP_V1_UUID_LENGTH != PTP_V2_MC_UUID_LENGTH);
-               BUILD_BUG_ON(PTP_V1_SEQUENCE_OFFSET != PTP_V2_SEQUENCE_OFFSET);
-               BUILD_BUG_ON(PTP_V1_SEQUENCE_LENGTH != PTP_V2_SEQUENCE_LENGTH);
-
                if ((version & PTP_VERSION_V2_MASK) != PTP_VERSION_V2) {
-                       netif_receive_skb(skb);
-                       return;
+                       return false;
+               }
+
+               /* The original V2 implementation uses bytes 2-7 of
+                * the UUID to match the packet to the timestamp. This
+                * discards two of the bytes of the MAC address used
+                * to create the UUID (SF bug 33070).  The PTP V2
+                * enhanced mode fixes this issue and uses bytes 0-2
+                * and byte 5-7 of the UUID.
+                */
+               match_data_345 = skb->data + PTP_V2_UUID_OFFSET + 5;
+               if (ptp->mode == MC_CMD_PTP_MODE_V2) {
+                       match_data_012 = skb->data + PTP_V2_UUID_OFFSET + 2;
+               } else {
+                       match_data_012 = skb->data + PTP_V2_UUID_OFFSET + 0;
+                       BUG_ON(ptp->mode != MC_CMD_PTP_MODE_V2_ENHANCED);
                }
        }
 
@@ -1056,14 +1048,19 @@ static void efx_ptp_rx(struct efx_channel *channel, struct sk_buff *skb)
                timestamps = skb_hwtstamps(skb);
                memset(timestamps, 0, sizeof(*timestamps));
 
+               /* We expect the sequence number to be in the same position in
+                * the packet for PTP V1 and V2
+                */
+               BUILD_BUG_ON(PTP_V1_SEQUENCE_OFFSET != PTP_V2_SEQUENCE_OFFSET);
+               BUILD_BUG_ON(PTP_V1_SEQUENCE_LENGTH != PTP_V2_SEQUENCE_LENGTH);
+
                /* Extract UUID/Sequence information */
-               data = skb->data + PTP_V1_UUID_OFFSET;
-               match->words[0] = (data[0]         |
-                                  (data[1] << 8)  |
-                                  (data[2] << 16) |
-                                  (data[3] << 24));
-               match->words[1] = (data[4]         |
-                                  (data[5] << 8)  |
+               match->words[0] = (match_data_012[0]         |
+                                  (match_data_012[1] << 8)  |
+                                  (match_data_012[2] << 16) |
+                                  (match_data_345[0] << 24));
+               match->words[1] = (match_data_345[1]         |
+                                  (match_data_345[2] << 8)  |
                                   (skb->data[PTP_V1_SEQUENCE_OFFSET +
                                              PTP_V1_SEQUENCE_LENGTH - 1] <<
                                    16));
@@ -1073,6 +1070,8 @@ static void efx_ptp_rx(struct efx_channel *channel, struct sk_buff *skb)
 
        skb_queue_tail(&ptp->rxq, skb);
        queue_work(ptp->workwq, &ptp->work);
+
+       return true;
 }
 
 /* Transmit a PTP packet.  This has to be transmitted by the MC
@@ -1167,7 +1166,7 @@ static int efx_ptp_ts_init(struct efx_nic *efx, struct hwtstamp_config *init)
         * timestamped
         */
                init->rx_filter = HWTSTAMP_FILTER_PTP_V2_L4_EVENT;
-               new_mode = MC_CMD_PTP_MODE_V2;
+               new_mode = MC_CMD_PTP_MODE_V2_ENHANCED;
                enable_wanted = true;
                break;
        case HWTSTAMP_FILTER_PTP_V2_EVENT:
@@ -1186,7 +1185,14 @@ static int efx_ptp_ts_init(struct efx_nic *efx, struct hwtstamp_config *init)
        if (init->tx_type != HWTSTAMP_TX_OFF)
                enable_wanted = true;
 
+       /* Old versions of the firmware do not support the improved
+        * UUID filtering option (SF bug 33070).  If the firmware does
+        * not accept the enhanced mode, fall back to the standard PTP
+        * v2 UUID filtering.
+        */
        rc = efx_ptp_change_mode(efx, enable_wanted, new_mode);
+       if ((rc != 0) && (new_mode == MC_CMD_PTP_MODE_V2_ENHANCED))
+               rc = efx_ptp_change_mode(efx, enable_wanted, MC_CMD_PTP_MODE_V2);
        if (rc != 0)
                return rc;