net/ice/base: support starting PHY in bypass mode
authorQi Zhang <qi.z.zhang@intel.com>
Tue, 10 Aug 2021 02:51:23 +0000 (10:51 +0800)
committerQi Zhang <qi.z.zhang@intel.com>
Wed, 11 Aug 2021 02:24:18 +0000 (04:24 +0200)
After starting the timestamping block, hardware begins calculating
precise offsets through a process of vernier calibration. This process
measures the effective phase offset of the various internal clocks used
in the PHY.

Once hardware completes these measurements, the P_REG_TX_OV_STATUS and
P_REG_RX_OV_STATUS registers are updated to indicate that the hardware
offset measurements are done.

This process does not happen immediately, but requires that at least one
packet be sent or received in order for the offset in that direction to
be calculated.

This poses a problem in some setups, because software expects the first
packet sent to be timestamped. This most often occurs if the clock time
is set by an application during startup. This set time command triggers
a PHY restart. Because of this, the timestamping block is reset, and
timestamps are not enabled until vernier calibration is complete. Since
this process won't complete until at least one packet is sent through
the PHY, timestamps of the very first packet sent will not be obtained.

This can result in the application failing due to missing timestamps.

To avoid this, allow starting the PHY in bypass mode. This mode enables
timestamps immediately, and skips adding the precise offset measurement.
This reduces the accuracy of the timestamp slightly, but ensures that we
get a reasonable value for the first packet.

The driver can continue monitoring the P_REG_TX_OV_STATUS and
P_REG_RX_OV_STATUS registers and exit bypass mode once the total
calibration is completed. In this way, once calibration is complete, the
timestamps will have the precise offset, but we do not break
applications which expect to be able to timestamp immediately.

Signed-off-by: Jacob Keller <jacob.e.keller@intel.com>
Signed-off-by: Qi Zhang <qi.z.zhang@intel.com>
Acked-by: Junfeng Guo <junfeng.guo@intel.com>
drivers/net/ice/base/ice_ptp_hw.c
drivers/net/ice/base/ice_ptp_hw.h

index bf68890..8ea7553 100644 (file)
@@ -2572,6 +2572,90 @@ ice_start_phy_timer_e822(struct ice_hw *hw, u8 port, bool bypass)
        return ICE_SUCCESS;
 }
 
+/**
+ * ice_phy_exit_bypass_e822 - Exit bypass mode, after vernier calculations
+ * @hw: pointer to the HW struct
+ * @port: the PHY port to configure
+ *
+ * After hardware finishes vernier calculations for the Tx and Rx offset, this
+ * function can be used to exit bypass mode by updating the total Tx and Rx
+ * offsets, and then disabling bypass. This will enable hardware to include
+ * the more precise offset calibrations, increasing precision of the generated
+ * timestamps.
+ *
+ * This cannot be done until hardware has measured the offsets, which requires
+ * waiting until at least one packet has been sent and received by the device.
+ */
+enum ice_status ice_phy_exit_bypass_e822(struct ice_hw *hw, u8 port)
+{
+       enum ice_status status;
+       u32 val;
+
+       status = ice_read_phy_reg_e822(hw, port, P_REG_TX_OV_STATUS, &val);
+       if (status) {
+               ice_debug(hw, ICE_DBG_PTP, "Failed to read TX_OV_STATUS for port %u, status %d\n",
+                         port, status);
+               return status;
+       }
+
+       if (!(val & P_REG_TX_OV_STATUS_OV_M)) {
+               ice_debug(hw, ICE_DBG_PTP, "Tx offset is not yet valid for port %u\n",
+                         port);
+               return ICE_ERR_NOT_READY;
+       }
+
+       status = ice_read_phy_reg_e822(hw, port, P_REG_RX_OV_STATUS, &val);
+       if (status) {
+               ice_debug(hw, ICE_DBG_PTP, "Failed to read RX_OV_STATUS for port %u, status %d\n",
+                         port, status);
+               return status;
+       }
+
+       if (!(val & P_REG_TX_OV_STATUS_OV_M)) {
+               ice_debug(hw, ICE_DBG_PTP, "Rx offset is not yet valid for port %u\n",
+                         port);
+               return ICE_ERR_NOT_READY;
+       }
+
+       status = ice_phy_cfg_tx_offset_e822(hw, port);
+       if (status) {
+               ice_debug(hw, ICE_DBG_PTP, "Failed to program total Tx offset for port %u, status %d\n",
+                         port, status);
+               return status;
+       }
+
+       status = ice_phy_cfg_rx_offset_e822(hw, port);
+       if (status) {
+               ice_debug(hw, ICE_DBG_PTP, "Failed to program total Rx offset for port %u, status %d\n",
+                         port, status);
+               return status;
+       }
+
+       /* Exit bypass mode now that the offset has been updated */
+       status = ice_read_phy_reg_e822(hw, port, P_REG_PS, &val);
+       if (status) {
+               ice_debug(hw, ICE_DBG_PTP, "Failed to read P_REG_PS for port %u, status %d\n",
+                         port, status);
+               return status;
+       }
+
+       if (!(val & P_REG_PS_BYPASS_MODE_M))
+               ice_debug(hw, ICE_DBG_PTP, "Port %u not in bypass mode\n",
+                         port);
+
+       val &= ~P_REG_PS_BYPASS_MODE_M;
+       status = ice_write_phy_reg_e822(hw, port, P_REG_PS, val);
+       if (status) {
+               ice_debug(hw, ICE_DBG_PTP, "Failed to disable bypass for port %u, status %d\n",
+                         port, status);
+               return status;
+       }
+
+       ice_info(hw, "Exiting bypass mode on PHY port %u\n", port);
+
+       return ICE_SUCCESS;
+}
+
 /* E810 functions
  *
  * The following functions operate on the E810 series devices which use
index f2d87ca..c804085 100644 (file)
@@ -218,6 +218,7 @@ enum ice_status
 ice_start_phy_timer_e822(struct ice_hw *hw, u8 port, bool bypass);
 enum ice_status ice_phy_cfg_tx_offset_e822(struct ice_hw *hw, u8 port);
 enum ice_status ice_phy_cfg_rx_offset_e822(struct ice_hw *hw, u8 port);
+enum ice_status ice_phy_exit_bypass_e822(struct ice_hw *hw, u8 port);
 
 /* E810 family functions */
 enum ice_status ice_ptp_init_phy_e810(struct ice_hw *hw);