ice: avoid executing commands on other ports when driving sync
authorJacob Keller <jacob.e.keller@intel.com>
Wed, 23 Aug 2023 15:18:14 +0000 (08:18 -0700)
committerDavid S. Miller <davem@davemloft.net>
Fri, 25 Aug 2023 09:06:27 +0000 (10:06 +0100)
The ice hardware has a synchronization mechanism used to drive the
simultaneous application of commands on both PHY ports and the source timer
in the MAC.

When issuing a sync via ice_ptp_exec_tmr_cmd(), the hardware will
simultaneously apply the commands programmed for the main timer and each
PHY port. Neither the main timer command register, nor the PHY port command
registers auto clear on command execution.

During the execution of a timer command intended for a single port on E822
devices, such as those used to configure a PHY during link up, the driver
is not correctly clearing the previous commands.

This results in unintentionally executing the last programmed command on
the main timer and other PHY ports whenever performing reconfiguration on
E822 ports after link up. This results in unintended side effects on other
timers, depending on what command was previously programmed.

To fix this, the driver must ensure that the main timer and all other PHY
ports are properly initialized to perform no action.

The enumeration for timer commands does not include an enumeration value
for doing nothing. Introduce ICE_PTP_NOP for this purpose. When writing a
timer command to hardware, leave the command bits set to zero which
indicates that no operation should be performed on that port.

Modify ice_ptp_one_port_cmd() to always initialize all ports. For all ports
other than the one being configured, write their timer command register to
ICE_PTP_NOP. This ensures that no side effect happens on the timer command.

To fix this for the PHY ports, modify ice_ptp_one_port_cmd() to always
initialize all other ports to ICE_PTP_NOP. This ensures that no side
effects happen on the other ports.

Call ice_ptp_src_cmd() with a command value if ICE_PTP_NOP in
ice_sync_phy_timer_e822() and ice_start_phy_timer_e822().

With both of these changes, the driver should no longer execute a stale
command on the main timer or another PHY port when reconfiguring one of the
PHY ports after link up.

Fixes: 3a7496234d17 ("ice: implement basic E822 PTP support")
Signed-off-by: Siddaraju DH <siddaraju.dh@intel.com>
Signed-off-by: Jacob Keller <jacob.e.keller@intel.com>
Tested-by: Sunitha Mekala <sunithax.d.mekala@intel.com> (A Contingent worker at Intel)
Signed-off-by: Tony Nguyen <anthony.l.nguyen@intel.com>
Reviewed-by: Simon Horman <horms@kernel.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/intel/ice/ice_ptp_hw.c
drivers/net/ethernet/intel/ice/ice_ptp_hw.h

index a38614d21ea8fba0db0c72539d2d9432db493a9f..de1d83300481d8179c79b664e23322f201853659 100644 (file)
@@ -131,6 +131,8 @@ static void ice_ptp_src_cmd(struct ice_hw *hw, enum ice_ptp_tmr_cmd cmd)
        case READ_TIME:
                cmd_val |= GLTSYN_CMD_READ_TIME;
                break;
+       case ICE_PTP_NOP:
+               break;
        }
 
        wr32(hw, GLTSYN_CMD, cmd_val);
@@ -1226,18 +1228,18 @@ ice_ptp_read_port_capture(struct ice_hw *hw, u8 port, u64 *tx_ts, u64 *rx_ts)
 }
 
 /**
- * ice_ptp_one_port_cmd - Prepare a single PHY port for a timer command
+ * ice_ptp_write_port_cmd_e822 - Prepare a single PHY port for a timer command
  * @hw: pointer to HW struct
  * @port: Port to which cmd has to be sent
  * @cmd: Command to be sent to the port
  *
  * Prepare the requested port for an upcoming timer sync command.
  *
- * Note there is no equivalent of this operation on E810, as that device
- * always handles all external PHYs internally.
+ * Do not use this function directly. If you want to configure exactly one
+ * port, use ice_ptp_one_port_cmd() instead.
  */
 static int
-ice_ptp_one_port_cmd(struct ice_hw *hw, u8 port, enum ice_ptp_tmr_cmd cmd)
+ice_ptp_write_port_cmd_e822(struct ice_hw *hw, u8 port, enum ice_ptp_tmr_cmd cmd)
 {
        u32 cmd_val, val;
        u8 tmr_idx;
@@ -1261,6 +1263,8 @@ ice_ptp_one_port_cmd(struct ice_hw *hw, u8 port, enum ice_ptp_tmr_cmd cmd)
        case ADJ_TIME_AT_TIME:
                cmd_val |= PHY_CMD_ADJ_TIME_AT_TIME;
                break;
+       case ICE_PTP_NOP:
+               break;
        }
 
        /* Tx case */
@@ -1306,6 +1310,39 @@ ice_ptp_one_port_cmd(struct ice_hw *hw, u8 port, enum ice_ptp_tmr_cmd cmd)
        return 0;
 }
 
+/**
+ * ice_ptp_one_port_cmd - Prepare one port for a timer command
+ * @hw: pointer to the HW struct
+ * @configured_port: the port to configure with configured_cmd
+ * @configured_cmd: timer command to prepare on the configured_port
+ *
+ * Prepare the configured_port for the configured_cmd, and prepare all other
+ * ports for ICE_PTP_NOP. This causes the configured_port to execute the
+ * desired command while all other ports perform no operation.
+ */
+static int
+ice_ptp_one_port_cmd(struct ice_hw *hw, u8 configured_port,
+                    enum ice_ptp_tmr_cmd configured_cmd)
+{
+       u8 port;
+
+       for (port = 0; port < ICE_NUM_EXTERNAL_PORTS; port++) {
+               enum ice_ptp_tmr_cmd cmd;
+               int err;
+
+               if (port == configured_port)
+                       cmd = configured_cmd;
+               else
+                       cmd = ICE_PTP_NOP;
+
+               err = ice_ptp_write_port_cmd_e822(hw, port, cmd);
+               if (err)
+                       return err;
+       }
+
+       return 0;
+}
+
 /**
  * ice_ptp_port_cmd_e822 - Prepare all ports for a timer command
  * @hw: pointer to the HW struct
@@ -1322,7 +1359,7 @@ ice_ptp_port_cmd_e822(struct ice_hw *hw, enum ice_ptp_tmr_cmd cmd)
        for (port = 0; port < ICE_NUM_EXTERNAL_PORTS; port++) {
                int err;
 
-               err = ice_ptp_one_port_cmd(hw, port, cmd);
+               err = ice_ptp_write_port_cmd_e822(hw, port, cmd);
                if (err)
                        return err;
        }
@@ -2252,6 +2289,9 @@ static int ice_sync_phy_timer_e822(struct ice_hw *hw, u8 port)
        if (err)
                goto err_unlock;
 
+       /* Do not perform any action on the main timer */
+       ice_ptp_src_cmd(hw, ICE_PTP_NOP);
+
        /* Issue the sync to activate the time adjustment */
        ice_ptp_exec_tmr_cmd(hw);
 
@@ -2372,6 +2412,9 @@ int ice_start_phy_timer_e822(struct ice_hw *hw, u8 port)
        if (err)
                return err;
 
+       /* Do not perform any action on the main timer */
+       ice_ptp_src_cmd(hw, ICE_PTP_NOP);
+
        ice_ptp_exec_tmr_cmd(hw);
 
        err = ice_read_phy_reg_e822(hw, port, P_REG_PS, &val);
@@ -2847,6 +2890,8 @@ static int ice_ptp_port_cmd_e810(struct ice_hw *hw, enum ice_ptp_tmr_cmd cmd)
        case ADJ_TIME_AT_TIME:
                cmd_val = GLTSYN_CMD_ADJ_INIT_TIME;
                break;
+       case ICE_PTP_NOP:
+               return 0;
        }
 
        /* Read, modify, write */
index 3b68cb91bd81944d0a8543a0fe69e3e200920708..096685237ca61bea6764a0545285c87f55bf5f71 100644 (file)
@@ -9,7 +9,8 @@ enum ice_ptp_tmr_cmd {
        INIT_INCVAL,
        ADJ_TIME,
        ADJ_TIME_AT_TIME,
-       READ_TIME
+       READ_TIME,
+       ICE_PTP_NOP,
 };
 
 enum ice_ptp_serdes {