Commit 0aacec49 authored by Jacob Keller's avatar Jacob Keller Committed by David S. Miller

ice: avoid executing commands on other ports when driving sync

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: 3a749623 ("ice: implement basic E822 PTP support")
Signed-off-by: default avatarSiddaraju DH <siddaraju.dh@intel.com>
Signed-off-by: default avatarJacob Keller <jacob.e.keller@intel.com>
Tested-by: Sunitha Mekala <sunithax.d.mekala@intel.com> (A Contingent worker at Intel)
Signed-off-by: default avatarTony Nguyen <anthony.l.nguyen@intel.com>
Reviewed-by: default avatarSimon Horman <horms@kernel.org>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent a4f39c9f
...@@ -131,6 +131,8 @@ static void ice_ptp_src_cmd(struct ice_hw *hw, enum ice_ptp_tmr_cmd cmd) ...@@ -131,6 +131,8 @@ static void ice_ptp_src_cmd(struct ice_hw *hw, enum ice_ptp_tmr_cmd cmd)
case READ_TIME: case READ_TIME:
cmd_val |= GLTSYN_CMD_READ_TIME; cmd_val |= GLTSYN_CMD_READ_TIME;
break; break;
case ICE_PTP_NOP:
break;
} }
wr32(hw, GLTSYN_CMD, cmd_val); 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) ...@@ -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 * @hw: pointer to HW struct
* @port: Port to which cmd has to be sent * @port: Port to which cmd has to be sent
* @cmd: Command to be sent to the port * @cmd: Command to be sent to the port
* *
* Prepare the requested port for an upcoming timer sync command. * Prepare the requested port for an upcoming timer sync command.
* *
* Note there is no equivalent of this operation on E810, as that device * Do not use this function directly. If you want to configure exactly one
* always handles all external PHYs internally. * port, use ice_ptp_one_port_cmd() instead.
*/ */
static int 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; u32 cmd_val, val;
u8 tmr_idx; u8 tmr_idx;
...@@ -1261,6 +1263,8 @@ ice_ptp_one_port_cmd(struct ice_hw *hw, u8 port, enum ice_ptp_tmr_cmd cmd) ...@@ -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: case ADJ_TIME_AT_TIME:
cmd_val |= PHY_CMD_ADJ_TIME_AT_TIME; cmd_val |= PHY_CMD_ADJ_TIME_AT_TIME;
break; break;
case ICE_PTP_NOP:
break;
} }
/* Tx case */ /* Tx case */
...@@ -1306,6 +1310,39 @@ ice_ptp_one_port_cmd(struct ice_hw *hw, u8 port, enum ice_ptp_tmr_cmd cmd) ...@@ -1306,6 +1310,39 @@ ice_ptp_one_port_cmd(struct ice_hw *hw, u8 port, enum ice_ptp_tmr_cmd cmd)
return 0; 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 * ice_ptp_port_cmd_e822 - Prepare all ports for a timer command
* @hw: pointer to the HW struct * @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) ...@@ -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++) { for (port = 0; port < ICE_NUM_EXTERNAL_PORTS; port++) {
int err; int err;
err = ice_ptp_one_port_cmd(hw, port, cmd); err = ice_ptp_write_port_cmd_e822(hw, port, cmd);
if (err) if (err)
return err; return err;
} }
...@@ -2252,6 +2289,9 @@ static int ice_sync_phy_timer_e822(struct ice_hw *hw, u8 port) ...@@ -2252,6 +2289,9 @@ static int ice_sync_phy_timer_e822(struct ice_hw *hw, u8 port)
if (err) if (err)
goto err_unlock; 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 */ /* Issue the sync to activate the time adjustment */
ice_ptp_exec_tmr_cmd(hw); ice_ptp_exec_tmr_cmd(hw);
...@@ -2372,6 +2412,9 @@ int ice_start_phy_timer_e822(struct ice_hw *hw, u8 port) ...@@ -2372,6 +2412,9 @@ int ice_start_phy_timer_e822(struct ice_hw *hw, u8 port)
if (err) if (err)
return 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); ice_ptp_exec_tmr_cmd(hw);
err = ice_read_phy_reg_e822(hw, port, P_REG_PS, &val); 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) ...@@ -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: case ADJ_TIME_AT_TIME:
cmd_val = GLTSYN_CMD_ADJ_INIT_TIME; cmd_val = GLTSYN_CMD_ADJ_INIT_TIME;
break; break;
case ICE_PTP_NOP:
return 0;
} }
/* Read, modify, write */ /* Read, modify, write */
......
...@@ -9,7 +9,8 @@ enum ice_ptp_tmr_cmd { ...@@ -9,7 +9,8 @@ enum ice_ptp_tmr_cmd {
INIT_INCVAL, INIT_INCVAL,
ADJ_TIME, ADJ_TIME,
ADJ_TIME_AT_TIME, ADJ_TIME_AT_TIME,
READ_TIME READ_TIME,
ICE_PTP_NOP,
}; };
enum ice_ptp_serdes { enum ice_ptp_serdes {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment