Import of kernel-6.12.0-211.44.1.el10_2

This commit is contained in:
almalinux-bot-kernel 2026-08-13 04:40:39 +00:00
parent 00ebf9a778
commit afc717d8bf
45 changed files with 2220 additions and 283 deletions

View File

@ -12,7 +12,7 @@ RHEL_MINOR = 2
#
# Use this spot to avoid future merge conflicts.
# Do not trim this comment.
RHEL_RELEASE = 211.43.1
RHEL_RELEASE = 211.44.1
#
# RHEL_REBASE_NUM

View File

@ -54,7 +54,7 @@ ice-$(CONFIG_PCI_IOV) += \
ice_vf_mbx.o \
ice_vf_vsi_vlan_ops.o \
ice_vf_lib.o
ice-$(CONFIG_PTP_1588_CLOCK) += ice_ptp.o ice_ptp_hw.o ice_dpll.o ice_tspll.o
ice-$(CONFIG_PTP_1588_CLOCK) += ice_ptp.o ice_ptp_hw.o ice_dpll.o ice_tspll.o ice_cpi.o ice_txclk.o
ice-$(CONFIG_DCB) += ice_dcb.o ice_dcb_nl.o ice_dcb_lib.o
ice-$(CONFIG_RFS_ACCEL) += ice_arfs.o
ice-$(CONFIG_XDP_SOCKETS) += ice_xsk.o

View File

@ -1243,6 +1243,8 @@ static int ice_devlink_reinit_up(struct ice_pf *pf)
return err;
}
ice_init_dev_hw(pf);
/* load MSI-X values */
ice_set_min_max_msix(pf);

View File

@ -753,7 +753,7 @@ static inline bool ice_is_xdp_ena_vsi(struct ice_vsi *vsi)
static inline void ice_set_ring_xdp(struct ice_tx_ring *ring)
{
ring->flags |= ICE_TX_FLAGS_RING_XDP;
set_bit(ICE_TX_RING_FLAGS_XDP, ring->flags);
}
/**
@ -778,7 +778,7 @@ static inline bool ice_is_txtime_ena(const struct ice_tx_ring *ring)
*/
static inline bool ice_is_txtime_cfg(const struct ice_tx_ring *ring)
{
return !!(ring->flags & ICE_TX_FLAGS_TXTIME);
return test_bit(ICE_TX_RING_FLAGS_TXTIME, ring->flags);
}
/**
@ -1155,4 +1155,16 @@ static inline struct ice_hw *ice_get_primary_hw(struct ice_pf *pf)
else
return &pf->adapter->ctrl_pf->hw;
}
/**
* ice_get_ctrl_pf - Get pointer to Control PF of the adapter
* @pf: pointer to the current PF structure
*
* Return: A pointer to ice_pf structure which is Control PF,
* NULL if it's not initialized yet.
*/
static inline struct ice_pf *ice_get_ctrl_pf(struct ice_pf *pf)
{
return !pf->adapter ? NULL : pf->adapter->ctrl_pf;
}
#endif /* _ICE_H_ */

View File

@ -62,6 +62,8 @@ static struct ice_adapter *ice_adapter_new(struct pci_dev *pdev)
adapter->index = ice_adapter_index(pdev);
spin_lock_init(&adapter->ptp_gltsyn_time_lock);
spin_lock_init(&adapter->txq_ctx_lock);
for (int i = 0; i < ARRAY_SIZE(adapter->cpi_phy_lock); i++)
mutex_init(&adapter->cpi_phy_lock[i]);
refcount_set(&adapter->refcount, 1);
mutex_init(&adapter->ports.lock);
@ -73,6 +75,8 @@ static struct ice_adapter *ice_adapter_new(struct pci_dev *pdev)
static void ice_adapter_free(struct ice_adapter *adapter)
{
WARN_ON(!list_empty(&adapter->ports.ports));
for (int i = 0; i < ARRAY_SIZE(adapter->cpi_phy_lock); i++)
mutex_destroy(&adapter->cpi_phy_lock[i]);
mutex_destroy(&adapter->ports.lock);
kfree(adapter);

View File

@ -5,9 +5,12 @@
#define _ICE_ADAPTER_H_
#include <linux/types.h>
#include <linux/mutex.h>
#include <linux/spinlock_types.h>
#include <linux/refcount_types.h>
#include "ice_type.h"
struct pci_dev;
struct ice_pf;
@ -31,6 +34,8 @@ struct ice_port_list {
* @ptp_gltsyn_time_lock: Spinlock protecting access to the GLTSYN_TIME
* register of the PTP clock.
* @txq_ctx_lock: Spinlock protecting access to the GLCOMM_QTX_CNTX_CTL register
* @cpi_phy_lock: Per-PHY mutex serializing CPI REQ/ACK transactions.
* Index 0 = PHY0, index 1 = PHY1. Used on E825C devices.
* @ctrl_pf: Control PF of the adapter
* @ports: Ports list
* @index: 64-bit index cached for collision detection on 32bit systems
@ -41,6 +46,8 @@ struct ice_adapter {
spinlock_t ptp_gltsyn_time_lock;
/* For access to GLCOMM_QTX_CNTX_CTL register */
spinlock_t txq_ctx_lock;
/* Serialize CPI REQ/ACK transactions per PHY (E825C only) */
struct mutex cpi_phy_lock[ICE_E825_MAX_PHYS];
struct ice_pf *ctrl_pf;
struct ice_port_list ports;

View File

@ -1169,6 +1169,8 @@ struct ice_aqc_restart_an {
u8 cmd_flags;
#define ICE_AQC_RESTART_AN_LINK_RESTART BIT(1)
#define ICE_AQC_RESTART_AN_LINK_ENABLE BIT(2)
#define ICE_AQC_RESTART_AN_REFCLK_M GENMASK(4, 3)
#define ICE_AQC_RESTART_AN_REFCLK_NOCHANGE 0
u8 reserved2[13];
};
@ -1252,7 +1254,7 @@ struct ice_aqc_get_link_status_data {
#define ICE_AQ_LINK_PWR_QSFP_CLASS_3 2
#define ICE_AQ_LINK_PWR_QSFP_CLASS_4 3
__le16 link_speed;
#define ICE_AQ_LINK_SPEED_M 0x7FF
#define ICE_AQ_LINK_SPEED_M GENMASK(11, 0)
#define ICE_AQ_LINK_SPEED_10MB BIT(0)
#define ICE_AQ_LINK_SPEED_100MB BIT(1)
#define ICE_AQ_LINK_SPEED_1000MB BIT(2)

View File

@ -1127,8 +1127,6 @@ int ice_init_hw(struct ice_hw *hw)
if (status)
goto err_unroll_fltr_mgmt_struct;
ice_init_dev_hw(hw->back);
mutex_init(&hw->tnl_lock);
ice_init_chk_recipe_reuse_support(hw);
@ -4127,12 +4125,13 @@ int ice_get_link_status(struct ice_port_info *pi, bool *link_up)
* @pi: pointer to the port information structure
* @ena_link: if true: enable link, if false: disable link
* @cd: pointer to command details structure or NULL
* @refclk: the new TX reference clock, 0 if no change
*
* Sets up the link and restarts the Auto-Negotiation over the link.
*/
int
ice_aq_set_link_restart_an(struct ice_port_info *pi, bool ena_link,
struct ice_sq_cd *cd)
struct ice_sq_cd *cd, u8 refclk)
{
struct ice_aqc_restart_an *cmd;
struct libie_aq_desc desc;
@ -4148,6 +4147,8 @@ ice_aq_set_link_restart_an(struct ice_port_info *pi, bool ena_link,
else
cmd->cmd_flags &= ~ICE_AQC_RESTART_AN_LINK_ENABLE;
cmd->cmd_flags |= FIELD_PREP(ICE_AQC_RESTART_AN_REFCLK_M, refclk);
return ice_aq_send_cmd(pi->hw, &desc, NULL, 0, cd);
}

View File

@ -215,7 +215,7 @@ ice_cfg_phy_fec(struct ice_port_info *pi, struct ice_aqc_set_phy_cfg_data *cfg,
enum ice_fec_mode fec);
int
ice_aq_set_link_restart_an(struct ice_port_info *pi, bool ena_link,
struct ice_sq_cd *cd);
struct ice_sq_cd *cd, u8 refclk);
int
ice_aq_set_mac_cfg(struct ice_hw *hw, u16 max_frame_size, struct ice_sq_cd *cd);
int

View File

@ -0,0 +1,361 @@
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (C) 2026 Intel Corporation */
#include "ice_type.h"
#include "ice_common.h"
#include "ice_ptp_hw.h"
#include "ice.h"
#include "ice_cpi.h"
/**
* ice_cpi_get_dest_dev - get destination PHY for given phy index
* @hw: pointer to the HW struct
* @phy: phy index of port the CPI action is taken on
*
* Return: sideband queue destination PHY device.
*/
static enum ice_sbq_dev_id ice_cpi_get_dest_dev(struct ice_hw *hw, u8 phy)
{
u8 curr_phy = hw->lane_num / hw->ptp.ports_per_phy;
/* In the driver, lanes 4..7 are in fact 0..3 on a second PHY.
* On a single complex E825C, PHY 0 is always destination device phy_0
* and PHY 1 is phy_0_peer.
* On dual complex E825C, device phy_0 points to PHY on a current
* complex and phy_0_peer to PHY on a different complex.
*/
if ((!ice_is_dual(hw) && phy) ||
(ice_is_dual(hw) && phy != curr_phy))
return ice_sbq_dev_phy_0_peer;
else
return ice_sbq_dev_phy_0;
}
/**
* ice_cpi_write_phy - Write a CPI port register
* @hw: pointer to the HW struct
* @phy: phy index of port the CPI action is taken on
* @addr: PHY register address
* @val: Value to write
*
* Return:
* * 0 on success
* * other error codes when failed to write to PHY
*/
static int ice_cpi_write_phy(struct ice_hw *hw, u8 phy, u32 addr, u32 val)
{
struct ice_sbq_msg_input msg = {
.dest_dev = ice_cpi_get_dest_dev(hw, phy),
.opcode = ice_sbq_msg_wr_np,
.msg_addr_low = lower_16_bits(addr),
.msg_addr_high = upper_16_bits(addr),
.data = val
};
int err;
err = ice_sbq_rw_reg(hw, &msg, LIBIE_AQ_FLAG_RD);
if (err)
ice_debug(hw, ICE_DBG_PTP,
"Failed to write CPI msg to phy %d, err: %d\n",
phy, err);
return err;
}
/**
* ice_cpi_read_phy - Read a CPI port register
* @hw: pointer to the HW struct
* @phy: phy index of port the CPI action is taken on
* @addr: PHY register address
* @val: storage for register value
*
* Return:
* * 0 on success
* * other error codes when failed to read from PHY
*/
static int ice_cpi_read_phy(struct ice_hw *hw, u8 phy, u32 addr, u32 *val)
{
struct ice_sbq_msg_input msg = {
.dest_dev = ice_cpi_get_dest_dev(hw, phy),
.opcode = ice_sbq_msg_rd,
.msg_addr_low = lower_16_bits(addr),
.msg_addr_high = upper_16_bits(addr)
};
int err;
err = ice_sbq_rw_reg(hw, &msg, LIBIE_AQ_FLAG_RD);
if (err) {
ice_debug(hw, ICE_DBG_PTP,
"Failed to read CPI msg from phy %d, err: %d\n",
phy, err);
return err;
}
*val = msg.data;
return 0;
}
/**
* ice_cpi_wait_req0_ack0 - waits for CPI interface to be available
* @hw: pointer to the HW struct
* @phy: phy index of port the CPI action is taken on
*
* This function checks if CPI interface is ready to use by CPI client.
* It's done by assuring LM.CMD.REQ and PHY.CMD.ACK bit in CPI
* interface registers to be 0.
*
* Return: 0 on success, negative on error
*/
static int ice_cpi_wait_req0_ack0(struct ice_hw *hw, int phy)
{
u32 phy_val;
u32 lm_val;
for (int i = 0; i < CPI_RETRIES_COUNT; i++) {
int err;
/* check if another CPI Client is also accessing CPI */
err = ice_cpi_read_phy(hw, phy, CPI0_LM1_CMD_DATA, &lm_val);
if (err)
return err;
if (FIELD_GET(CPI_LM_CMD_REQ_M, lm_val))
goto retry;
/* check if PHY.ACK is deasserted */
err = ice_cpi_read_phy(hw, phy, CPI0_PHY1_CMD_DATA, &phy_val);
if (err)
return err;
if (!FIELD_GET(CPI_PHY_CMD_ACK_M, phy_val))
/* req0 and ack0 at this point - ready to go */
return 0;
retry:
msleep(CPI_RETRIES_CADENCE_MS);
}
return -ETIMEDOUT;
}
/**
* ice_cpi_wait_ack - Waits for the PHY.ACK bit to be asserted/deasserted
* @hw: pointer to the HW struct
* @phy: phy index of port the CPI action is taken on
* @asserted: desired state of PHY.ACK bit
* @data: pointer to the user data where PHY.data is stored
*
* This function checks if PHY.ACK bit is asserted or deasserted, depending
* on the phase of CPI handshake. If 'asserted' state is required, PHY command
* data is stored in the 'data' storage.
*
* Return: 0 on success, negative on error
*/
static int ice_cpi_wait_ack(struct ice_hw *hw, u8 phy, bool asserted,
u32 *data)
{
u32 phy_val;
for (int i = 0; i < CPI_RETRIES_COUNT; i++) {
int err;
err = ice_cpi_read_phy(hw, phy, CPI0_PHY1_CMD_DATA, &phy_val);
if (err)
return err;
if (asserted && FIELD_GET(CPI_PHY_CMD_ERROR_M, phy_val))
return -EFAULT;
if (asserted && FIELD_GET(CPI_PHY_CMD_ACK_M, phy_val)) {
if (data)
*data = phy_val;
return 0;
}
if (!asserted && !FIELD_GET(CPI_PHY_CMD_ACK_M, phy_val))
return 0;
msleep(CPI_RETRIES_CADENCE_MS);
}
return -ETIMEDOUT;
}
#define ice_cpi_wait_ack0(hw, port) \
ice_cpi_wait_ack(hw, port, false, NULL)
#define ice_cpi_wait_ack1(hw, port, data) \
ice_cpi_wait_ack(hw, port, true, data)
/**
* ice_cpi_req0 - deasserts LM.REQ bit
* @hw: pointer to the HW struct
* @phy: phy index of port the CPI action is taken on
* @data: the command data
*
* Return: 0 on success, negative on CPI write error
*/
static int ice_cpi_req0(struct ice_hw *hw, u8 phy, u32 data)
{
data &= ~CPI_LM_CMD_REQ_M;
return ice_cpi_write_phy(hw, phy, CPI0_LM1_CMD_DATA, data);
}
/**
* ice_cpi_exec_cmd - writes command data to CPI interface
* @hw: pointer to the HW struct
* @phy: phy index of port the CPI action is taken on
* @data: the command data
*
* Return: 0 on success, otherwise negative on error
*/
static int ice_cpi_exec_cmd(struct ice_hw *hw, int phy, u32 data)
{
return ice_cpi_write_phy(hw, phy, CPI0_LM1_CMD_DATA, data);
}
/**
* ice_cpi_phy_lock - get per-PHY lock for CPI transaction serialization
* @hw: pointer to the HW struct
* @phy: PHY index
*
* Return: pointer to PHY mutex, or %NULL when context is unavailable.
*/
static struct mutex *ice_cpi_phy_lock(struct ice_hw *hw, u8 phy)
{
struct ice_pf *pf = hw->back;
if (!pf || !pf->adapter || phy >= ICE_E825_MAX_PHYS)
return NULL;
return &pf->adapter->cpi_phy_lock[phy];
}
/**
* ice_cpi_exec - executes CPI command
* @hw: pointer to the HW struct
* @phy: phy index of port the CPI action is taken on
* @cmd: pointer to the command struct to execute
* @resp: pointer to user allocated CPI response struct
*
* This function executes CPI request with respect to CPI handshake
* mechanism.
*
* Return: 0 on success, otherwise negative on error
*/
int ice_cpi_exec(struct ice_hw *hw, u8 phy,
const struct ice_cpi_cmd *cmd,
struct ice_cpi_resp *resp)
{
struct mutex *cpi_lock; /* serializes CPI transactions per PHY */
u32 phy_cmd, lm_cmd = 0;
int err, err1 = 0;
if (!cmd || !resp)
return -EINVAL;
cpi_lock = ice_cpi_phy_lock(hw, phy);
if (!cpi_lock)
return -EINVAL;
mutex_lock(cpi_lock);
lm_cmd =
FIELD_PREP(CPI_LM_CMD_REQ_M, CPI_LM_CMD_REQ) |
FIELD_PREP(CPI_LM_CMD_GET_SET_M, cmd->set) |
FIELD_PREP(CPI_LM_CMD_OPCODE_M, cmd->opcode) |
FIELD_PREP(CPI_LM_CMD_PORTLANE_M, cmd->port) |
FIELD_PREP(CPI_LM_CMD_DATA_M, cmd->data);
/* 1. Try to acquire the bus, PHY ACK should be low before we begin */
err = ice_cpi_wait_req0_ack0(hw, phy);
if (err)
goto cpi_exec_exit;
/* 2. We start the CPI request */
err = ice_cpi_exec_cmd(hw, phy, lm_cmd);
if (err)
goto cpi_deassert;
/*
* 3. Wait for CPI confirmation, PHY ACK should be asserted and opcode
* echoed in the response
*/
err = ice_cpi_wait_ack1(hw, phy, &phy_cmd);
if (err)
goto cpi_deassert;
if (FIELD_GET(CPI_LM_CMD_OPCODE_M, lm_cmd) !=
FIELD_GET(CPI_PHY_CMD_OPCODE_M, phy_cmd)) {
err = -EFAULT;
goto cpi_deassert;
}
resp->opcode = FIELD_GET(CPI_PHY_CMD_OPCODE_M, phy_cmd);
resp->data = FIELD_GET(CPI_PHY_CMD_DATA_M, phy_cmd);
resp->port = FIELD_GET(CPI_PHY_CMD_PORTLANE_M, phy_cmd);
cpi_deassert:
/* 4. We deassert REQ */
err1 = ice_cpi_req0(hw, phy, lm_cmd);
if (err1)
goto cpi_exec_exit;
/* 5. PHY ACK should be deasserted in response */
err1 = ice_cpi_wait_ack0(hw, phy);
cpi_exec_exit:
if (!err)
err = err1;
mutex_unlock(cpi_lock);
return err;
}
/**
* ice_cpi_set_cmd - execute CPI SET command
* @hw: pointer to the HW struct
* @opcode: CPI command opcode
* @phy: phy index CPI command is applied for
* @port_lane: ephy index CPI command is applied for
* @data: CPI opcode context specific data
*
* Return: 0 on success, negative error code on failure.
*/
static int ice_cpi_set_cmd(struct ice_hw *hw, u8 opcode, u8 phy, u8 port_lane,
u16 data)
{
struct ice_cpi_resp cpi_resp = {0};
struct ice_cpi_cmd cpi_cmd = {
.opcode = opcode,
.set = true,
.port = port_lane,
.data = data,
};
return ice_cpi_exec(hw, phy, &cpi_cmd, &cpi_resp);
}
/**
* ice_cpi_ena_dis_clk_ref - enables/disables Tx reference clock on port
* @hw: pointer to the HW struct
* @phy: phy index of port for which Tx reference clock is enabled/disabled
* @clk: Tx reference clock to enable or disable
* @enable: bool value to enable or disable Tx reference clock
*
* This function executes CPI request to enable or disable specific
* Tx reference clock on given PHY.
*
* Return: 0 on success, negative error code on failure.
*/
int ice_cpi_ena_dis_clk_ref(struct ice_hw *hw, u8 phy,
enum ice_e825c_ref_clk clk, bool enable)
{
u16 val;
val = FIELD_PREP(CPI_OPCODE_PHY_CLK_PHY_SEL_M, phy) |
FIELD_PREP(CPI_OPCODE_PHY_CLK_REF_CTRL_M,
enable ? CPI_OPCODE_PHY_CLK_ENABLE :
CPI_OPCODE_PHY_CLK_DISABLE) |
FIELD_PREP(CPI_OPCODE_PHY_CLK_REF_SEL_M, clk);
return ice_cpi_set_cmd(hw, CPI_OPCODE_PHY_CLK, phy, 0, val);
}

View File

@ -0,0 +1,58 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* Copyright (C) 2026 Intel Corporation */
#ifndef _ICE_CPI_H_
#define _ICE_CPI_H_
#include "ice_type.h"
#include "ice_ptp_hw.h"
#define CPI0_PHY1_CMD_DATA 0x7FD028
#define CPI0_LM1_CMD_DATA 0x7FD024
#define CPI_RETRIES_COUNT 10
#define CPI_RETRIES_CADENCE_MS 100
/* CPI PHY CMD DATA register (CPI0_PHY1_CMD_DATA) */
#define CPI_PHY_CMD_DATA_M GENMASK(15, 0)
#define CPI_PHY_CMD_OPCODE_M GENMASK(23, 16)
#define CPI_PHY_CMD_PORTLANE_M GENMASK(26, 24)
#define CPI_PHY_CMD_RSVD_M GENMASK(29, 27)
#define CPI_PHY_CMD_ERROR_M BIT(30)
#define CPI_PHY_CMD_ACK_M BIT(31)
/* CPI LM CMD DATA register (CPI0_LM1_CMD_DATA) */
#define CPI_LM_CMD_DATA_M GENMASK(15, 0)
#define CPI_LM_CMD_OPCODE_M GENMASK(23, 16)
#define CPI_LM_CMD_PORTLANE_M GENMASK(26, 24)
#define CPI_LM_CMD_RSVD_M GENMASK(28, 27)
#define CPI_LM_CMD_GET_SET_M BIT(29)
#define CPI_LM_CMD_REQ_M BIT(31)
#define CPI_OPCODE_PHY_CLK 0xF1
#define CPI_OPCODE_PHY_CLK_PHY_SEL_M GENMASK(9, 6)
#define CPI_OPCODE_PHY_CLK_REF_CTRL_M GENMASK(5, 4)
#define CPI_OPCODE_PHY_CLK_DISABLE 1
#define CPI_OPCODE_PHY_CLK_ENABLE 2
#define CPI_OPCODE_PHY_CLK_REF_SEL_M GENMASK(3, 0)
#define CPI_LM_CMD_REQ 1
struct ice_cpi_cmd {
u8 port;
u8 opcode;
u16 data;
bool set;
};
struct ice_cpi_resp {
u8 port;
u8 opcode;
u16 data;
};
int ice_cpi_exec(struct ice_hw *hw, u8 phy,
const struct ice_cpi_cmd *cmd,
struct ice_cpi_resp *resp);
int ice_cpi_ena_dis_clk_ref(struct ice_hw *hw, u8 phy,
enum ice_e825c_ref_clk clk, bool enable);
#endif /* _ICE_CPI_H_ */

View File

@ -537,14 +537,14 @@ void ice_dcb_rebuild(struct ice_pf *pf)
struct ice_dcbx_cfg *err_cfg;
int ret;
mutex_lock(&pf->tc_mutex);
ret = ice_query_port_ets(pf->hw.port_info, &buf, sizeof(buf), NULL);
if (ret) {
dev_err(dev, "Query Port ETS failed\n");
goto dcb_error;
}
mutex_lock(&pf->tc_mutex);
if (!pf->hw.port_info->qos_cfg.is_sw_lldp)
ice_cfg_etsrec_defaults(pf->hw.port_info);
@ -943,7 +943,7 @@ ice_tx_prepare_vlan_flags_dcb(struct ice_tx_ring *tx_ring,
/* if this is not already set it means a VLAN 0 + priority needs
* to be offloaded
*/
if (tx_ring->flags & ICE_TX_FLAGS_RING_VLAN_L2TAG2)
if (test_bit(ICE_TX_RING_FLAGS_VLAN_L2TAG2, tx_ring->flags))
first->tx_flags |= ICE_TX_FLAGS_HW_OUTER_SINGLE_VLAN;
else
first->tx_flags |= ICE_TX_FLAGS_HW_VLAN;

View File

@ -4,6 +4,7 @@
#include "ice.h"
#include "ice_lib.h"
#include "ice_trace.h"
#include "ice_txclk.h"
#include <linux/dpll.h>
#include <linux/property.h>
@ -19,6 +20,9 @@
#define ICE_DPLL_SW_PIN_INPUT_BASE_QSFP 6
#define ICE_DPLL_SW_PIN_OUTPUT_BASE 0
#define E825_RCLK_PARENT_0_PIN_IDX 0
#define E825_RCLK_PARENT_1_PIN_IDX 1
#define ICE_DPLL_PIN_SW_INPUT_ABS(in_idx) \
(ICE_DPLL_SW_PIN_INPUT_BASE_SFP + (in_idx))
@ -57,6 +61,7 @@
* @ICE_DPLL_PIN_TYPE_OUTPUT: output pin
* @ICE_DPLL_PIN_TYPE_RCLK_INPUT: recovery clock input pin
* @ICE_DPLL_PIN_TYPE_SOFTWARE: software controlled SMA/U.FL pins
* @ICE_DPLL_PIN_TYPE_TXCLK: transmit clock reference input pin
*/
enum ice_dpll_pin_type {
ICE_DPLL_PIN_INVALID,
@ -64,6 +69,7 @@ enum ice_dpll_pin_type {
ICE_DPLL_PIN_TYPE_OUTPUT,
ICE_DPLL_PIN_TYPE_RCLK_INPUT,
ICE_DPLL_PIN_TYPE_SOFTWARE,
ICE_DPLL_PIN_TYPE_TXCLK,
};
static const char * const pin_type_name[] = {
@ -71,10 +77,13 @@ static const char * const pin_type_name[] = {
[ICE_DPLL_PIN_TYPE_OUTPUT] = "output",
[ICE_DPLL_PIN_TYPE_RCLK_INPUT] = "rclk-input",
[ICE_DPLL_PIN_TYPE_SOFTWARE] = "software",
[ICE_DPLL_PIN_TYPE_TXCLK] = "txclk-input",
};
static const char * const ice_dpll_sw_pin_sma[] = { "SMA1", "SMA2" };
static const char * const ice_dpll_sw_pin_ufl[] = { "U.FL1", "U.FL2" };
static const char * const ice_dpll_ext_eref_pin = "EXT_EREF0";
static const char * const ice_dpll_fwnode_ext_synce = "clk_ref_synce";
static const struct dpll_pin_frequency ice_esync_range[] = {
DPLL_PIN_FREQUENCY_RANGE(0, DPLL_PIN_FREQUENCY_1_HZ),
@ -1154,6 +1163,32 @@ ice_dpll_input_state_get(const struct dpll_pin *pin, void *pin_priv,
extack, ICE_DPLL_PIN_TYPE_INPUT);
}
/**
* ice_dpll_sw_pin_notify_peer - notify the paired SW pin after a state change
* @d: pointer to dplls struct
* @changed: the SW pin that was explicitly changed (already notified by dpll core)
*
* SMA and U.FL pins share physical signal paths in pairs (SMA1/U.FL1 and
* SMA2/U.FL2). When one pin's routing changes via the PCA9575 GPIO
* expander, the paired pin's state may also change. Send a change
* notification for the peer pin so userspace consumers monitoring the
* peer via dpll netlink learn about the update.
*
* Context: Called from dpll_pin_ops callbacks after pf->dplls.lock is
* released. Uses __dpll_pin_change_ntf() because dpll_lock is
* still held by the dpll netlink layer.
*/
static void ice_dpll_sw_pin_notify_peer(struct ice_dplls *d,
struct ice_dpll_pin *changed)
{
struct ice_dpll_pin *peer;
peer = (changed >= d->sma && changed < d->sma + ICE_DPLL_PIN_SW_NUM) ?
&d->ufl[changed->idx] : &d->sma[changed->idx];
if (peer->pin)
__dpll_pin_change_ntf(peer->pin);
}
/**
* ice_dpll_sma_direction_set - set direction of SMA pin
* @p: pointer to a pin
@ -1171,6 +1206,8 @@ static int ice_dpll_sma_direction_set(struct ice_dpll_pin *p,
enum dpll_pin_direction direction,
struct netlink_ext_ack *extack)
{
struct ice_dplls *d = &p->pf->dplls;
struct ice_dpll_pin *peer;
u8 data;
int ret;
@ -1189,8 +1226,9 @@ static int ice_dpll_sma_direction_set(struct ice_dpll_pin *p,
case ICE_DPLL_PIN_SW_2_IDX:
if (direction == DPLL_PIN_DIRECTION_INPUT) {
data &= ~ICE_SMA2_DIR_EN;
data |= ICE_SMA2_UFL2_RX_DIS;
} else {
data &= ~ICE_SMA2_TX_EN;
data &= ~(ICE_SMA2_TX_EN | ICE_SMA2_UFL2_RX_DIS);
data |= ICE_SMA2_DIR_EN;
}
break;
@ -1202,6 +1240,34 @@ static int ice_dpll_sma_direction_set(struct ice_dpll_pin *p,
ret = ice_dpll_pin_state_update(p->pf, p,
ICE_DPLL_PIN_TYPE_SOFTWARE,
extack);
if (ret)
return ret;
/* When a direction change activates the paired U.FL pin, enable
* its backing CGU pin so the pin reports as connected. Without
* this the U.FL routing is correct but the CGU pin stays disabled
* and userspace sees the pin as disconnected. Do not disable the
* backing pin when U.FL becomes inactive because the SMA pin may
* still be using it.
*/
peer = &d->ufl[p->idx];
if (peer->active) {
struct ice_dpll_pin *target;
enum ice_dpll_pin_type type;
if (peer->output) {
target = peer->output;
type = ICE_DPLL_PIN_TYPE_OUTPUT;
} else {
target = peer->input;
type = ICE_DPLL_PIN_TYPE_INPUT;
}
ret = ice_dpll_pin_enable(&p->pf->hw, target,
d->eec.dpll_idx, type, extack);
if (!ret)
ret = ice_dpll_pin_state_update(p->pf, target,
type, extack);
}
return ret;
}
@ -1253,6 +1319,14 @@ ice_dpll_ufl_pin_state_set(const struct dpll_pin *pin, void *pin_priv,
data &= ~ICE_SMA1_MASK;
enable = true;
} else if (state == DPLL_PIN_STATE_DISCONNECTED) {
/* Skip if U.FL1 is not active, setting TX_EN
* while DIR_EN is set would also deactivate
* the paired SMA1 output.
*/
if (data & (ICE_SMA1_DIR_EN | ICE_SMA1_TX_EN)) {
ret = 0;
goto unlock;
}
data |= ICE_SMA1_TX_EN;
enable = false;
} else {
@ -1267,6 +1341,15 @@ ice_dpll_ufl_pin_state_set(const struct dpll_pin *pin, void *pin_priv,
data &= ~ICE_SMA2_UFL2_RX_DIS;
enable = true;
} else if (state == DPLL_PIN_STATE_DISCONNECTED) {
/* Skip if U.FL2 is not active, setting
* UFL2_RX_DIS could also disable the paired
* SMA2 input.
*/
if (!(data & ICE_SMA2_DIR_EN) ||
(data & ICE_SMA2_UFL2_RX_DIS)) {
ret = 0;
goto unlock;
}
data |= ICE_SMA2_UFL2_RX_DIS;
enable = false;
} else {
@ -1296,6 +1379,8 @@ ice_dpll_ufl_pin_state_set(const struct dpll_pin *pin, void *pin_priv,
unlock:
mutex_unlock(&pf->dplls.lock);
if (!ret)
ice_dpll_sw_pin_notify_peer(&pf->dplls, p);
return ret;
}
@ -1414,6 +1499,8 @@ ice_dpll_sma_pin_state_set(const struct dpll_pin *pin, void *pin_priv,
unlock:
mutex_unlock(&pf->dplls.lock);
if (!ret)
ice_dpll_sw_pin_notify_peer(&pf->dplls, sma);
return ret;
}
@ -1609,6 +1696,8 @@ ice_dpll_pin_sma_direction_set(const struct dpll_pin *pin, void *pin_priv,
mutex_lock(&pf->dplls.lock);
ret = ice_dpll_sma_direction_set(p, direction, extack);
mutex_unlock(&pf->dplls.lock);
if (!ret)
ice_dpll_sw_pin_notify_peer(&pf->dplls, p);
return ret;
}
@ -1915,7 +2004,10 @@ ice_dpll_phase_offset_get(const struct dpll_pin *pin, void *pin_priv,
d->active_input == p->input->pin))
*phase_offset = d->phase_offset * ICE_DPLL_PHASE_OFFSET_FACTOR;
else if (d->phase_offset_monitor_period)
*phase_offset = p->phase_offset * ICE_DPLL_PHASE_OFFSET_FACTOR;
*phase_offset = (p->input &&
p->direction == DPLL_PIN_DIRECTION_INPUT ?
p->input->phase_offset :
p->phase_offset) * ICE_DPLL_PHASE_OFFSET_FACTOR;
else
*phase_offset = 0;
mutex_unlock(&pf->dplls.lock);
@ -2522,12 +2614,206 @@ unlock:
return ret;
}
/**
* ice_dpll_txclk_work - apply a pending TX reference clock change
* @work: work_struct embedded in struct ice_dplls
*
* This worker executes an outstanding TX reference clock switch request
* that was previously queued via the DPLL TXCLK pin set callback.
*
* The worker performs only the operational part of the switch, issuing
* the necessary firmware commands to request a new TX reference clock
* selection (e.g. triggering an AN restart). It does not verify whether
* the requested clock was ultimately accepted by the hardware.
*
* Hardware verification, software state reconciliation, pin state
* notification, and TXC DPLL lock-status updates are performed later,
* after link-up, by ice_txclk_update_and_notify().
*
* Context:
* - Runs in process context on pf->dplls.wq and may sleep.
* - Serializes access to shared TXCLK state using pf->dplls.lock.
*/
static void ice_dpll_txclk_work(struct work_struct *work)
{
struct ice_dplls *dplls =
container_of(work, struct ice_dplls, txclk_work);
struct ice_pf *pf = container_of(dplls, struct ice_pf, dplls);
struct dpll_pin *old_pin = NULL;
struct dpll_pin *new_pin = NULL;
enum ice_e825c_ref_clk clk;
bool do_switch;
int err;
mutex_lock(&pf->dplls.lock);
do_switch = pf->dplls.txclk_switch_requested;
clk = pf->ptp.port.tx_clk_req;
mutex_unlock(&pf->dplls.lock);
if (!do_switch)
return;
err = ice_txclk_set_clk(pf, clk);
mutex_lock(&pf->dplls.lock);
/* Only clear the request flag if no newer request arrived while
* the lock was dropped. Otherwise leave it set so the re-queued
* worker run picks up the updated tx_clk_req value.
*/
if (pf->ptp.port.tx_clk_req == clk)
pf->dplls.txclk_switch_requested = false;
if (err) {
/* Roll back the requested clock to match the current hardware
* state so that ice_txclk_update_and_notify() does not
* misinterpret a future link-up as a failed switch. Only roll
* back if no newer request arrived in the meantime; otherwise
* the re-queued worker run will apply the updated value.
*/
dev_err(ice_pf_to_dev(pf),
"TX clock switch to %u failed, err=%d; reverting\n",
clk, err);
if (pf->ptp.port.tx_clk_req == clk) {
/* Capture pins for post-unlock notification so that
* userspace observes the requested pin flipping back
* to DISCONNECTED and the effective pin to CONNECTED.
*/
new_pin = ice_txclk_get_pin(pf, clk);
old_pin = ice_txclk_get_pin(pf, pf->ptp.port.tx_clk);
pf->ptp.port.tx_clk_req = pf->ptp.port.tx_clk;
}
}
mutex_unlock(&pf->dplls.lock);
if (old_pin)
dpll_pin_change_ntf(old_pin);
if (new_pin)
dpll_pin_change_ntf(new_pin);
}
/**
* ice_dpll_txclk_state_on_dpll_set - set a state on TX clk pin
* @pin: pointer to a pin
* @pin_priv: private data pointer passed on pin registration
* @dpll: registered dpll pointer
* @dpll_priv: private data pointer passed on dpll registration
* @state: state to be set on pin
* @extack: error reporting
*
* Dpll subsystem callback, set a state of a Tx reference clock pin
*
* Context: Acquires and releases pf->dplls.lock.
* Return:
* * 0 - success
* * negative - failure
*/
static int
ice_dpll_txclk_state_on_dpll_set(const struct dpll_pin *pin, void *pin_priv,
const struct dpll_device *dpll,
void *dpll_priv, enum dpll_pin_state state,
struct netlink_ext_ack *extack)
{
struct ice_dpll_pin *p = pin_priv;
struct ice_pf *pf = p->pf;
enum ice_e825c_ref_clk new_clk;
int ret = 0;
if (ice_dpll_is_reset(pf, extack))
return -EBUSY;
if (state != DPLL_PIN_STATE_CONNECTED &&
state != DPLL_PIN_STATE_DISCONNECTED) {
NL_SET_ERR_MSG(extack,
"unsupported pin state for TX reference clock");
return -EINVAL;
}
/* Check ICE_FLAG_DPLL and queue_work() under pf->dplls.lock.
* ice_dpll_deinit() clears the flag under the same lock before
* cancel_work_sync() and wq destruction, so a callback arriving
* after teardown observes the cleared flag and bails out.
*/
mutex_lock(&pf->dplls.lock);
if (!test_bit(ICE_FLAG_DPLL, pf->flags)) {
ret = -ENODEV;
goto unlock;
}
if (state == DPLL_PIN_STATE_DISCONNECTED &&
p->tx_ref_src != pf->ptp.port.tx_clk_req)
goto unlock;
new_clk = (state == DPLL_PIN_STATE_DISCONNECTED) ? ICE_REF_CLK_ENET :
p->tx_ref_src;
if (new_clk == pf->ptp.port.tx_clk_req)
goto unlock;
pf->ptp.port.tx_clk_req = new_clk;
pf->dplls.txclk_switch_requested = true;
queue_work(pf->dplls.wq, &pf->dplls.txclk_work);
unlock:
mutex_unlock(&pf->dplls.lock);
return ret;
}
/**
* ice_dpll_txclk_state_on_dpll_get - get a state of Tx clk reference pin
* @pin: pointer to a pin
* @pin_priv: private data pointer passed on pin registration
* @dpll: registered dpll pointer
* @dpll_priv: private data pointer passed on dpll registration
* @state: on success holds pin state on parent pin
* @extack: error reporting
*
* TXCLK DPLL pin state is derived and not stored explicitly.
*
* Only external TX reference clocks (SYNCE, EREF0) are modeled
* as DPLL pins. The internal ENET (TXCO) clock has no pin and,
* when selected, all TXCLK pins are reported DISCONNECTED.
*
* During a pending TXCLK switch, the requested pin may be
* reported as CONNECTED before hardware verification.
* Hardware acceptance and synchronization are reported
* exclusively via TXC DPLL lock-status.
*
* Context: Acquires and releases pf->dplls.lock
* Return:
* * 0 - success
* * negative - failure
*/
static int
ice_dpll_txclk_state_on_dpll_get(const struct dpll_pin *pin, void *pin_priv,
const struct dpll_device *dpll,
void *dpll_priv,
enum dpll_pin_state *state,
struct netlink_ext_ack *extack)
{
struct ice_dpll_pin *p = pin_priv;
struct ice_pf *pf = p->pf;
if (ice_dpll_is_reset(pf, extack))
return -EBUSY;
mutex_lock(&pf->dplls.lock);
if (pf->ptp.port.tx_clk_req == p->tx_ref_src)
*state = DPLL_PIN_STATE_CONNECTED;
else
*state = DPLL_PIN_STATE_DISCONNECTED;
mutex_unlock(&pf->dplls.lock);
return 0;
}
static const struct dpll_pin_ops ice_dpll_rclk_ops = {
.state_on_pin_set = ice_dpll_rclk_state_on_pin_set,
.state_on_pin_get = ice_dpll_rclk_state_on_pin_get,
.direction_get = ice_dpll_input_direction,
};
static const struct dpll_pin_ops ice_dpll_txclk_ops = {
.state_on_dpll_set = ice_dpll_txclk_state_on_dpll_set,
.state_on_dpll_get = ice_dpll_txclk_state_on_dpll_get,
.direction_get = ice_dpll_input_direction,
};
static const struct dpll_pin_ops ice_dpll_pin_sma_ops = {
.state_on_dpll_set = ice_dpll_sma_pin_state_set,
.state_on_dpll_get = ice_dpll_sw_pin_state_get,
@ -2550,6 +2836,8 @@ static const struct dpll_pin_ops ice_dpll_pin_ufl_ops = {
.state_on_dpll_set = ice_dpll_ufl_pin_state_set,
.state_on_dpll_get = ice_dpll_sw_pin_state_get,
.direction_get = ice_dpll_pin_sw_direction_get,
.prio_get = ice_dpll_sw_input_prio_get,
.prio_set = ice_dpll_sw_input_prio_set,
.frequency_get = ice_dpll_sw_pin_frequency_get,
.frequency_set = ice_dpll_sw_pin_frequency_set,
.esync_set = ice_dpll_sw_esync_set,
@ -2614,6 +2902,27 @@ static u64 ice_generate_clock_id(struct ice_pf *pf)
return pci_get_dsn(pf->pdev);
}
/**
* ice_dpll_pin_ntf - notify pin change including any SW pin wrappers
* @dplls: pointer to dplls struct
* @pin: the dpll_pin that changed
*
* Send a change notification for @pin and for any registered SMA/U.FL pin
* whose backing CGU input matches @pin.
*/
static void ice_dpll_pin_ntf(struct ice_dplls *dplls, struct dpll_pin *pin)
{
dpll_pin_change_ntf(pin);
for (int i = 0; i < ICE_DPLL_PIN_SW_NUM; i++) {
if (dplls->sma[i].pin && dplls->sma[i].input &&
dplls->sma[i].input->pin == pin)
dpll_pin_change_ntf(dplls->sma[i].pin);
if (dplls->ufl[i].pin && dplls->ufl[i].input &&
dplls->ufl[i].input->pin == pin)
dpll_pin_change_ntf(dplls->ufl[i].pin);
}
}
/**
* ice_dpll_notify_changes - notify dpll subsystem about changes
* @d: pointer do dpll
@ -2622,6 +2931,7 @@ static u64 ice_generate_clock_id(struct ice_pf *pf)
*/
static void ice_dpll_notify_changes(struct ice_dpll *d)
{
struct ice_dplls *dplls = &d->pf->dplls;
bool pin_notified = false;
if (d->prev_dpll_state != d->dpll_state) {
@ -2630,17 +2940,17 @@ static void ice_dpll_notify_changes(struct ice_dpll *d)
}
if (d->prev_input != d->active_input) {
if (d->prev_input)
dpll_pin_change_ntf(d->prev_input);
ice_dpll_pin_ntf(dplls, d->prev_input);
d->prev_input = d->active_input;
if (d->active_input) {
dpll_pin_change_ntf(d->active_input);
ice_dpll_pin_ntf(dplls, d->active_input);
pin_notified = true;
}
}
if (d->prev_phase_offset != d->phase_offset) {
d->prev_phase_offset = d->phase_offset;
if (!pin_notified && d->active_input)
dpll_pin_change_ntf(d->active_input);
ice_dpll_pin_ntf(dplls, d->active_input);
}
}
@ -2669,6 +2979,7 @@ static bool ice_dpll_is_pps_phase_monitor(struct ice_pf *pf)
/**
* ice_dpll_pins_notify_mask - notify dpll subsystem about bulk pin changes
* @dplls: pointer to dplls struct
* @pins: array of ice_dpll_pin pointers registered within dpll subsystem
* @pin_num: number of pins
* @phase_offset_ntf_mask: bitmask of pin indexes to notify
@ -2678,15 +2989,14 @@ static bool ice_dpll_is_pps_phase_monitor(struct ice_pf *pf)
*
* Context: Must be called while pf->dplls.lock is released.
*/
static void ice_dpll_pins_notify_mask(struct ice_dpll_pin *pins,
static void ice_dpll_pins_notify_mask(struct ice_dplls *dplls,
struct ice_dpll_pin *pins,
u8 pin_num,
u32 phase_offset_ntf_mask)
{
int i = 0;
for (i = 0; i < pin_num; i++)
if (phase_offset_ntf_mask & (1 << i))
dpll_pin_change_ntf(pins[i].pin);
for (int i = 0; i < pin_num; i++)
if (phase_offset_ntf_mask & BIT(i))
ice_dpll_pin_ntf(dplls, pins[i].pin);
}
/**
@ -2862,7 +3172,7 @@ static void ice_dpll_periodic_work(struct kthread_work *work)
ice_dpll_notify_changes(de);
ice_dpll_notify_changes(dp);
if (phase_offset_ntf)
ice_dpll_pins_notify_mask(d->inputs, d->num_inputs,
ice_dpll_pins_notify_mask(d, d->inputs, d->num_inputs,
phase_offset_ntf);
resched:
@ -3028,9 +3338,13 @@ ice_dpll_unregister_pins(struct dpll_device *dpll, struct ice_dpll_pin *pins,
{
int i;
for (i = 0; i < count; i++)
if (!pins[i].hidden)
dpll_pin_unregister(dpll, pins[i].pin, ops, &pins[i]);
for (i = 0; i < count; i++) {
if (pins[i].hidden)
continue;
if (IS_ERR_OR_NULL(pins[i].pin))
continue;
dpll_pin_unregister(dpll, pins[i].pin, ops, &pins[i]);
}
}
/**
@ -3204,19 +3518,42 @@ static bool ice_dpll_is_fwnode_pin(struct ice_dpll_pin *pin)
return !IS_ERR_OR_NULL(pin->fwnode);
}
static bool ice_dpll_fwnode_eq(const struct fwnode_handle *a,
const struct fwnode_handle *b)
{
return a && a == b;
}
static void ice_dpll_pin_notify_work(struct work_struct *work)
{
struct ice_dpll_pin_work *w = container_of(work,
struct ice_dpll_pin_work,
work);
struct ice_dpll_pin *pin, *parent = w->pin;
bool is_tx_synce_parent = false;
struct ice_pf *pf = parent->pf;
bool is_rclk_parent = false;
int ret;
wait_for_completion(&pf->dplls.dpll_init);
if (!test_bit(ICE_FLAG_DPLL, pf->flags))
goto out; /* DPLL initialization failed */
/* Decide which parent we are handling, defensively checking FWNs */
for (int i = 0; i < pf->dplls.rclk.num_parents; i++) {
if (ice_dpll_fwnode_eq(parent->fwnode,
pf->dplls.inputs[i].fwnode)) {
is_rclk_parent = true;
break;
}
}
is_tx_synce_parent =
ice_dpll_fwnode_eq(parent->fwnode,
pf->dplls.txclks[E825_EXT_SYNCE_PIN_IDX].fwnode);
if (!is_rclk_parent && !is_tx_synce_parent)
goto out;
switch (w->action) {
case DPLL_PIN_CREATED:
if (!IS_ERR_OR_NULL(parent->pin)) {
@ -3233,16 +3570,28 @@ static void ice_dpll_pin_notify_work(struct work_struct *work)
goto out;
}
/* Register rclk pin */
pin = &pf->dplls.rclk;
ret = dpll_pin_on_pin_register(parent->pin, pin->pin,
&ice_dpll_rclk_ops, pin);
if (ret) {
dev_err(ice_pf_to_dev(pf),
"Failed to register pin: %pe\n", ERR_PTR(ret));
dpll_pin_put(parent->pin, &parent->tracker);
parent->pin = NULL;
goto out;
if (is_rclk_parent) {
/* Register rclk pin via on-pin relationship */
pin = &pf->dplls.rclk;
ret = dpll_pin_on_pin_register(parent->pin, pin->pin,
&ice_dpll_rclk_ops, pin);
if (ret) {
dev_err(ice_pf_to_dev(pf),
"RCLK pin register failed: %pe\n",
ERR_PTR(ret));
goto drop_parent_ref;
}
} else if (is_tx_synce_parent) {
/* Register TX-CLK SYNCE pin directly to TXC DPLL */
pin = &pf->dplls.txclks[E825_EXT_SYNCE_PIN_IDX];
ret = dpll_pin_register(pf->dplls.txc.dpll, pin->pin,
&ice_dpll_txclk_ops, pin);
if (ret) {
dev_err(ice_pf_to_dev(pf),
"TX SYNCE pin register failed: %pe\n",
ERR_PTR(ret));
goto drop_parent_ref;
}
}
break;
case DPLL_PIN_DELETED:
@ -3251,11 +3600,18 @@ static void ice_dpll_pin_notify_work(struct work_struct *work)
goto out;
}
/* Unregister rclk pin */
pin = &pf->dplls.rclk;
dpll_pin_on_pin_unregister(parent->pin, pin->pin,
&ice_dpll_rclk_ops, pin);
if (is_rclk_parent) {
/* Unregister rclk pin */
pin = &pf->dplls.rclk;
dpll_pin_on_pin_unregister(parent->pin, pin->pin,
&ice_dpll_rclk_ops, pin);
} else if (is_tx_synce_parent) {
/* Unregister TX-CLK SYNCE pin from TXC DPLL */
pin = &pf->dplls.txclks[E825_EXT_SYNCE_PIN_IDX];
dpll_pin_unregister(pf->dplls.txc.dpll, pin->pin,
&ice_dpll_txclk_ops, pin);
}
drop_parent_ref:
/* Drop fwnode pin reference */
dpll_pin_put(parent->pin, &parent->tracker);
parent->pin = NULL;
@ -3281,6 +3637,12 @@ static int ice_dpll_pin_notify(struct notifier_block *nb, unsigned long action,
if (pin->fwnode != info->fwnode)
return NOTIFY_DONE; /* Not this pin */
/* Ignore notification which are the outcome of internal pin
* registration/unregistration calls - synce pin case.
*/
if (info->src_clock_id == pin->pf->dplls.clock_id)
return NOTIFY_DONE;
work = kzalloc(sizeof(*work), GFP_KERNEL);
if (!work)
return NOTIFY_DONE;
@ -3383,10 +3745,16 @@ ice_dpll_init_rclk_pin(struct ice_pf *pf, int start_idx,
}
static void
ice_dpll_deinit_fwnode_pin(struct ice_dpll_pin *pin)
ice_dpll_stop_fwnode_pin_activity(struct ice_dpll_pin *pin, bool flush)
{
unregister_dpll_notifier(&pin->nb);
flush_workqueue(pin->pf->dplls.wq);
if (flush)
flush_workqueue(pin->pf->dplls.wq);
}
static void
ice_dpll_release_fwnode_pin(struct ice_dpll_pin *pin)
{
if (!IS_ERR_OR_NULL(pin->pin)) {
dpll_pin_put(pin->pin, &pin->tracker);
pin->pin = NULL;
@ -3395,6 +3763,13 @@ ice_dpll_deinit_fwnode_pin(struct ice_dpll_pin *pin)
pin->fwnode = NULL;
}
static void
ice_dpll_deinit_fwnode_pin(struct ice_dpll_pin *pin)
{
ice_dpll_stop_fwnode_pin_activity(pin, true);
ice_dpll_release_fwnode_pin(pin);
}
static void
ice_dpll_deinit_fwnode_pins(struct ice_pf *pf, struct ice_dpll_pin *pins,
int start_idx)
@ -3406,6 +3781,26 @@ ice_dpll_deinit_fwnode_pins(struct ice_pf *pf, struct ice_dpll_pin *pins,
destroy_workqueue(pf->dplls.wq);
}
static int ice_dpll_deinit_txclk_pins(struct ice_pf *pf)
{
struct ice_dpll_pin *synce_pin = &pf->dplls.txclks[E825_EXT_SYNCE_PIN_IDX];
struct ice_dpll *dt = &pf->dplls.txc;
ice_dpll_stop_fwnode_pin_activity(synce_pin, true);
ice_dpll_unregister_pins(dt->dpll, pf->dplls.txclks,
&ice_dpll_txclk_ops,
ARRAY_SIZE(pf->dplls.txclks));
ice_dpll_release_pins(&pf->dplls.txclks[E825_EXT_EREF_PIN_IDX], 1);
/* ice_dpll_release_pins() puts the pin but does not clear the slot,
* unlike ice_dpll_release_fwnode_pin() used for SYNCE below. NULL it
* so a late ice_txclk_get_pin() returns NULL rather than a dangling
* pointer.
*/
pf->dplls.txclks[E825_EXT_EREF_PIN_IDX].pin = NULL;
ice_dpll_release_fwnode_pin(synce_pin);
return 0;
}
/**
* ice_dpll_deinit_pins - deinitialize direct pins
* @pf: board private structure
@ -3425,8 +3820,10 @@ static void ice_dpll_deinit_pins(struct ice_pf *pf, bool cgu)
struct ice_dpll *dp = &d->pps;
ice_dpll_deinit_rclk_pin(pf);
if (pf->hw.mac_type == ICE_MAC_GENERIC_3K_E825)
if (pf->hw.mac_type == ICE_MAC_GENERIC_3K_E825) {
ice_dpll_deinit_txclk_pins(pf);
ice_dpll_deinit_fwnode_pins(pf, pf->dplls.inputs, 0);
}
if (cgu) {
ice_dpll_unregister_pins(dp->dpll, inputs, &ice_dpll_input_ops,
num_inputs);
@ -3532,7 +3929,7 @@ static int
ice_dpll_init_fwnode_pins(struct ice_pf *pf, struct ice_dpll_pin *pins,
int start_idx)
{
char pin_name[8];
char pin_name[16];
int i, ret;
pf->dplls.wq = create_singlethread_workqueue("ice_dpll_wq");
@ -3549,6 +3946,11 @@ ice_dpll_init_fwnode_pins(struct ice_pf *pf, struct ice_dpll_pin *pins,
return 0;
error:
/*
* A notifier worker may already be queued and blocked on dpll_init;
* release it so the per-pin flush below does not deadlock.
*/
complete_all(&pf->dplls.dpll_init);
while (i--)
ice_dpll_deinit_fwnode_pin(&pins[start_idx + i]);
@ -3557,6 +3959,64 @@ error:
return ret;
}
static int ice_dpll_init_txclk_pins(struct ice_pf *pf, int start_idx)
{
struct ice_dpll_pin *ref_pin = pf->dplls.txclks;
struct ice_dpll *txc = &pf->dplls.txc;
int ret;
/* Configure EXT_EREF0 pin */
ret = ice_dpll_get_pins(pf, ref_pin, start_idx, 1, pf->dplls.clock_id);
if (ret)
return ret;
ret = dpll_pin_register(txc->dpll, ref_pin->pin, &ice_dpll_txclk_ops,
ref_pin);
if (ret)
goto err_release_ext_eref;
/*
* Configure EXT_SYNCE pin (fwnode-backed).
* The pin may not yet be available; in that case registration
* will be deferred via the notifier path.
*/
ref_pin++;
ret = ice_dpll_init_fwnode_pin(ref_pin, ice_dpll_fwnode_ext_synce);
if (ret)
goto err_unregister_ext_eref;
if (IS_ERR_OR_NULL(ref_pin->pin)) {
dev_dbg(ice_pf_to_dev(pf),
"Tx-clk SYNCE pin not registered yet\n");
return 0;
}
ret = dpll_pin_register(txc->dpll, ref_pin->pin, &ice_dpll_txclk_ops,
ref_pin);
if (ret)
goto err_deinit_synce;
return 0;
err_deinit_synce:
/*
* Avoid deadlock against notifier workers blocked on dpll_init.
* The outer init error path will complete dpll_init and flush the
* shared workqueue before destroying it.
*/
ice_dpll_stop_fwnode_pin_activity(ref_pin, false);
ice_dpll_release_fwnode_pin(ref_pin);
err_unregister_ext_eref:
dpll_pin_unregister(txc->dpll,
pf->dplls.txclks[E825_EXT_EREF_PIN_IDX].pin,
&ice_dpll_txclk_ops,
&pf->dplls.txclks[E825_EXT_EREF_PIN_IDX]);
err_release_ext_eref:
ice_dpll_release_pins(&pf->dplls.txclks[E825_EXT_EREF_PIN_IDX], 1);
return ret;
}
/**
* ice_dpll_init_pins_e825 - init pins and register pins with a dplls
* @pf: board private structure
@ -3579,6 +4039,15 @@ static int ice_dpll_init_pins_e825(struct ice_pf *pf)
ret = ice_dpll_init_rclk_pin(pf, DPLL_PIN_IDX_UNSPEC,
&ice_dpll_rclk_ops);
if (ret)
goto unregister_pins;
ret = ice_dpll_init_txclk_pins(pf, 0);
if (ret)
ice_dpll_deinit_rclk_pin(pf);
unregister_pins:
if (ret) {
/* Inform DPLL notifier works that DPLL init was finished
* unsuccessfully (ICE_DPLL_FLAG not set).
@ -3697,7 +4166,7 @@ deinit_inputs:
static void
ice_dpll_deinit_dpll(struct ice_pf *pf, struct ice_dpll *d, bool cgu)
{
if (cgu)
if (cgu || pf->hw.mac_type == ICE_MAC_GENERIC_3K_E825)
dpll_device_unregister(d->dpll, d->ops, d);
dpll_device_put(d->dpll, &d->tracker);
}
@ -3732,12 +4201,13 @@ ice_dpll_init_dpll(struct ice_pf *pf, struct ice_dpll *d, bool cgu,
return ret;
}
d->pf = pf;
if (cgu) {
if (cgu || pf->hw.mac_type == ICE_MAC_GENERIC_3K_E825) {
const struct dpll_device_ops *ops = &ice_dpll_ops;
if (type == DPLL_TYPE_PPS && ice_dpll_is_pps_phase_monitor(pf))
ops = &ice_dpll_pom_ops;
ice_dpll_update_state(pf, d, true);
if (cgu)
ice_dpll_update_state(pf, d, true);
ret = dpll_device_register(d->dpll, type, ops, d);
if (ret) {
dpll_device_put(d->dpll, &d->tracker);
@ -4019,6 +4489,7 @@ static int ice_dpll_init_info_sw_pins(struct ice_pf *pf)
struct ice_dpll_pin *pin;
u32 phase_adj_max, caps;
int i, ret;
u8 data;
if (pf->hw.device_id == ICE_DEV_ID_E810C_QSFP)
input_idx_offset = ICE_E810_RCLK_PINS_NUM;
@ -4078,6 +4549,22 @@ static int ice_dpll_init_info_sw_pins(struct ice_pf *pf)
}
ice_dpll_phase_range_set(&pin->prop.phase_range, phase_adj_max);
}
/* Initialize the SMA control register to a known-good default state.
* Without this write the PCA9575 GPIO expander retains its power-on
* default (all outputs high) which makes all SW pins appear inactive.
* Set SMA1 and SMA2 as active inputs, disable U.FL1 output and
* U.FL2 input.
*/
ret = ice_read_sma_ctrl(&pf->hw, &data);
if (ret)
return ret;
data &= ~ICE_ALL_SMA_MASK;
data |= ICE_SMA1_TX_EN | ICE_SMA2_TX_EN | ICE_SMA2_UFL2_RX_DIS;
ret = ice_write_sma_ctrl(&pf->hw, data);
if (ret)
return ret;
ret = ice_dpll_pin_state_update(pf, pin, ICE_DPLL_PIN_TYPE_SOFTWARE,
NULL);
if (ret)
@ -4086,6 +4573,36 @@ static int ice_dpll_init_info_sw_pins(struct ice_pf *pf)
return 0;
}
/**
* ice_dpll_init_info_txclk_pins_e825c - initializes tx-clk pins information
* @pf: board private structure
*
* Init information for tx-clks pin, cache them in pf->dplls.txclks
*
* Return:
* * 0 - success
*/
static int ice_dpll_init_info_txclk_pins_e825c(struct ice_pf *pf)
{
struct ice_dpll_pin *tx_pin;
for (int i = 0; i < ICE_DPLL_TXCLK_NUM_MAX; i++) {
tx_pin = &pf->dplls.txclks[i];
tx_pin->prop.type = DPLL_PIN_TYPE_EXT;
tx_pin->prop.capabilities |=
DPLL_PIN_CAPABILITIES_STATE_CAN_CHANGE;
tx_pin->pf = pf;
if (i == E825_EXT_EREF_PIN_IDX) {
tx_pin->prop.board_label = ice_dpll_ext_eref_pin;
tx_pin->tx_ref_src = ICE_REF_CLK_EREF0;
} else if (i == E825_EXT_SYNCE_PIN_IDX) {
tx_pin->tx_ref_src = ICE_REF_CLK_SYNCE;
}
}
return 0;
}
/**
* ice_dpll_init_pins_info - init pins info wrapper
* @pf: board private structure
@ -4111,6 +4628,9 @@ ice_dpll_init_pins_info(struct ice_pf *pf, enum ice_dpll_pin_type pin_type)
return ice_dpll_init_info_rclk_pin(pf);
case ICE_DPLL_PIN_TYPE_SOFTWARE:
return ice_dpll_init_info_sw_pins(pf);
case ICE_DPLL_PIN_TYPE_TXCLK:
return ice_dpll_init_info_txclk_pins_e825c(pf);
default:
return -EINVAL;
}
@ -4144,11 +4664,15 @@ static void ice_dpll_deinit_info(struct ice_pf *pf)
static int ice_dpll_init_info_e825c(struct ice_pf *pf)
{
struct ice_dplls *d = &pf->dplls;
struct ice_dpll *dt = &d->txc;
int ret = 0;
int i;
d->clock_id = ice_generate_clock_id(pf);
d->num_inputs = ICE_SYNCE_CLK_NUM;
dt->dpll_state = ice_txclk_lock_status(pf->ptp.port.tx_clk);
dt->mode = DPLL_MODE_MANUAL;
dt->dpll_idx = pf->ptp.port.port_num;
d->inputs = kcalloc(d->num_inputs, sizeof(*d->inputs), GFP_KERNEL);
if (!d->inputs)
@ -4165,6 +4689,11 @@ static int ice_dpll_init_info_e825c(struct ice_pf *pf)
ret = ice_dpll_init_pins_info(pf, ICE_DPLL_PIN_TYPE_RCLK_INPUT);
if (ret)
goto deinit_info;
ret = ice_dpll_init_pins_info(pf, ICE_DPLL_PIN_TYPE_TXCLK);
if (ret)
goto deinit_info;
dev_dbg(ice_pf_to_dev(pf),
"%s - success, inputs: %u, outputs: %u, rclk-parents: %u\n",
__func__, d->num_inputs, d->num_outputs, d->rclk.num_parents);
@ -4288,15 +4817,38 @@ void ice_dpll_deinit(struct ice_pf *pf)
{
bool cgu = ice_is_feature_supported(pf, ICE_F_CGU);
/* Clear ICE_FLAG_DPLL under the lock so that any new caller of
* ice_txclk_update_and_notify() observes the cleared flag and
* returns early. In-flight callers that already passed the flag
* check hold txclk_notify_rwsem for read across the out-of-lock
* dpll_*_change_ntf() calls; the down_write/up_write barrier
* below waits for them to finish before pins and the TXC DPLL
* device may be freed.
*/
mutex_lock(&pf->dplls.lock);
clear_bit(ICE_FLAG_DPLL, pf->flags);
mutex_unlock(&pf->dplls.lock);
/* Wait for in-flight ice_txclk_update_and_notify() readers */
if (pf->hw.mac_type == ICE_MAC_GENERIC_3K_E825) {
down_write(&pf->dplls.txclk_notify_rwsem);
up_write(&pf->dplls.txclk_notify_rwsem);
}
if (cgu)
ice_dpll_deinit_worker(pf);
if (pf->hw.mac_type == ICE_MAC_GENERIC_3K_E825)
cancel_work_sync(&pf->dplls.txclk_work);
ice_dpll_deinit_pins(pf, cgu);
if (!IS_ERR_OR_NULL(pf->dplls.pps.dpll))
ice_dpll_deinit_dpll(pf, &pf->dplls.pps, cgu);
if (!IS_ERR_OR_NULL(pf->dplls.eec.dpll))
ice_dpll_deinit_dpll(pf, &pf->dplls.eec, cgu);
if (!IS_ERR_OR_NULL(pf->dplls.txc.dpll))
ice_dpll_deinit_dpll(pf, &pf->dplls.txc, false);
ice_dpll_deinit_info(pf);
mutex_destroy(&pf->dplls.lock);
}
@ -4316,20 +4868,42 @@ static void ice_dpll_init_e825(struct ice_pf *pf)
struct ice_dplls *d = &pf->dplls;
int err;
/* E825 sets ICE_F_PHY_RCLK unconditionally, so DPLL may run without
* PTP. Populate the HW-topology fields the TX-clk path divides by,
* otherwise userspace can trigger div-by-zero in ice_txclk_set_clk().
* When PTP is supported, ice_ptp_init() handles this.
*/
if (!test_bit(ICE_FLAG_PTP_SUPPORTED, pf->flags)) {
ice_ptp_init_hw(&pf->hw);
if (pf->hw.lane_num >= 0)
pf->ptp.port.port_num = pf->hw.lane_num;
}
mutex_init(&d->lock);
/* Initialize the txclk worker and its notification rwsem before any
* code path can fail: ice_dpll_deinit() runs unconditionally on
* failure and calls cancel_work_sync() / down_write() on these.
*/
INIT_WORK(&d->txclk_work, ice_dpll_txclk_work);
init_rwsem(&d->txclk_notify_rwsem);
init_completion(&d->dpll_init);
err = ice_dpll_init_info_e825c(pf);
if (err)
goto err_exit;
err = ice_dpll_init_pins_e825(pf);
err = ice_dpll_init_dpll(pf, &pf->dplls.txc, false, DPLL_TYPE_GENERIC);
if (err)
goto deinit_info;
err = ice_dpll_init_pins_e825(pf);
if (err)
goto deinit_txclk;
set_bit(ICE_FLAG_DPLL, pf->flags);
complete_all(&d->dpll_init);
return;
deinit_txclk:
ice_dpll_deinit_dpll(pf, &pf->dplls.txc, false);
deinit_info:
ice_dpll_deinit_info(pf);
err_exit:

View File

@ -7,6 +7,9 @@
#include "ice.h"
#define ICE_DPLL_RCLK_NUM_MAX 4
#define ICE_DPLL_TXCLK_NUM_MAX 2
#define E825_EXT_EREF_PIN_IDX 0
#define E825_EXT_SYNCE_PIN_IDX 1
#define ICE_CGU_R10 0x28
#define ICE_CGU_R10_SYNCE_CLKO_SEL GENMASK(8, 5)
@ -79,6 +82,7 @@ struct ice_dpll_pin {
u8 ref_sync;
bool active;
bool hidden;
enum ice_e825c_ref_clk tx_ref_src;
};
/** ice_dpll - store info required for DPLL control
@ -124,12 +128,15 @@ struct ice_dpll {
/** ice_dplls - store info required for CCU (clock controlling unit)
* @kworker: periodic worker
* @work: periodic work
* @lock: locks access to configuration of a dpll
* @wq: workqueue used to schedule DPLL-related deferred work
* @lock: protects DPLL configuration (see Locking below)
* @eec: pointer to EEC dpll dev
* @pps: pointer to PPS dpll dev
* @txc: pointer to TXC dpll dev
* @inputs: input pins pointer
* @outputs: output pins pointer
* @rclk: recovered pins pointer
* @txclks: TX clock reference pins pointer
* @num_inputs: number of input pins available on dpll
* @num_outputs: number of output pins available on dpll
* @cgu_state_acq_err_num: number of errors returned during periodic work
@ -138,6 +145,28 @@ struct ice_dpll {
* @input_phase_adj_max: max phase adjust value for an input pins
* @output_phase_adj_max: max phase adjust value for an output pins
* @periodic_counter: counter of periodic work executions
* @generic: true when generic DPLL ops are used
* @txclk_work: deferred TX reference clock switch worker
* @txclk_switch_requested: a TX ref clock switch is queued in @txclk_work
* @txclk_notify_rwsem: drains in-flight TXCLK notifications on teardown
*
* Locking:
* Acquisition order (top to bottom):
*
* txclk_notify_rwsem (read)
* -> pf->dplls.lock
* -> ctrl_pf->dplls.lock
*
* - @lock serializes all DPLL state mutations on this PF. When the
* controlling PF's lock must also be taken (e.g. updating the shared
* tx_refclks usage map), acquire pf->dplls.lock first, then
* ctrl_pf->dplls.lock. Skip the second acquire when pf == ctrl_pf
* to avoid recursive locking.
* - @txclk_notify_rwsem is held for read across
* ice_txclk_update_and_notify(), including the out-of-lock
* dpll_*_change_ntf() calls. ice_dpll_deinit() takes the write side
* standalone (not nested under any other lock) to drain in-flight
* readers before pins and the TXC DPLL device are freed.
*/
struct ice_dplls {
struct kthread_worker *kworker;
@ -147,11 +176,13 @@ struct ice_dplls {
struct completion dpll_init;
struct ice_dpll eec;
struct ice_dpll pps;
struct ice_dpll txc;
struct ice_dpll_pin *inputs;
struct ice_dpll_pin *outputs;
struct ice_dpll_pin sma[ICE_DPLL_PIN_SW_NUM];
struct ice_dpll_pin ufl[ICE_DPLL_PIN_SW_NUM];
struct ice_dpll_pin rclk;
struct ice_dpll_pin txclks[ICE_DPLL_TXCLK_NUM_MAX];
u8 num_inputs;
u8 num_outputs;
u8 sma_data;
@ -162,6 +193,9 @@ struct ice_dplls {
s32 output_phase_adj_max;
u32 periodic_counter;
bool generic;
struct work_struct txclk_work;
bool txclk_switch_requested;
struct rw_semaphore txclk_notify_rwsem;
};
#if IS_ENABLED(CONFIG_PTP_1588_CLOCK)

View File

@ -3290,6 +3290,7 @@ ice_set_ringparam(struct net_device *netdev, struct ethtool_ringparam *ring,
tx_rings[i].desc = NULL;
tx_rings[i].tx_buf = NULL;
tx_rings[i].tstamp_ring = NULL;
clear_bit(ICE_TX_RING_FLAGS_TXTIME, tx_rings[i].flags);
tx_rings[i].tx_tstamps = &pf->ptp.port.tx;
err = ice_setup_tx_ring(&tx_rings[i]);
if (err) {
@ -3340,7 +3341,7 @@ process_rx:
rx_rings = kcalloc(vsi->num_rxq, sizeof(*rx_rings), GFP_KERNEL);
if (!rx_rings) {
err = -ENOMEM;
goto done;
goto free_xdp;
}
ice_for_each_rxq(vsi, i) {
@ -3368,7 +3369,7 @@ rx_unwind:
}
kfree(rx_rings);
err = -ENOMEM;
goto free_tx;
goto free_xdp;
}
}
@ -3420,6 +3421,13 @@ process_link:
}
goto done;
free_xdp:
if (xdp_rings) {
ice_for_each_xdp_txq(vsi, i)
ice_free_tx_ring(&xdp_rings[i]);
kfree(xdp_rings);
}
free_tx:
/* error cleanup if the Rx allocations failed after getting Tx */
if (tx_rings) {

View File

@ -726,7 +726,7 @@ static int ice_finalize_update(struct pldmfw *context)
switch (priv->reset_level) {
case ICE_AQC_NVM_EMPR_FLAG:
devlink_flash_update_status_notify(devlink,
"Activate new firmware by devlink reload",
"Activate new firmware by devlink reload action fw_activate",
NULL, 0, 0);
break;
case ICE_AQC_NVM_PERST_FLAG:

View File

@ -1414,9 +1414,9 @@ static int ice_vsi_alloc_rings(struct ice_vsi *vsi)
ring->count = vsi->num_tx_desc;
ring->txq_teid = ICE_INVAL_TEID;
if (dvm_ena)
ring->flags |= ICE_TX_FLAGS_RING_VLAN_L2TAG2;
set_bit(ICE_TX_RING_FLAGS_VLAN_L2TAG2, ring->flags);
else
ring->flags |= ICE_TX_FLAGS_RING_VLAN_L2TAG1;
set_bit(ICE_TX_RING_FLAGS_VLAN_L2TAG1, ring->flags);
WRITE_ONCE(vsi->tx_rings[i], ring);
}
@ -3772,7 +3772,8 @@ int ice_set_link(struct ice_vsi *vsi, bool ena)
if (vsi->type != ICE_VSI_PF)
return -EINVAL;
status = ice_aq_set_link_restart_an(pi, ena, NULL);
status = ice_aq_set_link_restart_an(pi, ena, NULL,
ICE_AQC_RESTART_AN_REFCLK_NOCHANGE);
/* if link is owned by manageability, FW will return LIBIE_AQ_RC_EMODE.
* this is not a fatal error, so print a warning message and return

View File

@ -1922,82 +1922,6 @@ static void ice_handle_mdd_event(struct ice_pf *pf)
ice_print_vfs_mdd_events(pf);
}
/**
* ice_force_phys_link_state - Force the physical link state
* @vsi: VSI to force the physical link state to up/down
* @link_up: true/false indicates to set the physical link to up/down
*
* Force the physical link state by getting the current PHY capabilities from
* hardware and setting the PHY config based on the determined capabilities. If
* link changes a link event will be triggered because both the Enable Automatic
* Link Update and LESM Enable bits are set when setting the PHY capabilities.
*
* Returns 0 on success, negative on failure
*/
static int ice_force_phys_link_state(struct ice_vsi *vsi, bool link_up)
{
struct ice_aqc_get_phy_caps_data *pcaps;
struct ice_aqc_set_phy_cfg_data *cfg;
struct ice_port_info *pi;
struct device *dev;
int retcode;
if (!vsi || !vsi->port_info || !vsi->back)
return -EINVAL;
if (vsi->type != ICE_VSI_PF)
return 0;
dev = ice_pf_to_dev(vsi->back);
pi = vsi->port_info;
pcaps = kzalloc(sizeof(*pcaps), GFP_KERNEL);
if (!pcaps)
return -ENOMEM;
retcode = ice_aq_get_phy_caps(pi, false, ICE_AQC_REPORT_ACTIVE_CFG, pcaps,
NULL);
if (retcode) {
dev_err(dev, "Failed to get phy capabilities, VSI %d error %d\n",
vsi->vsi_num, retcode);
retcode = -EIO;
goto out;
}
/* No change in link */
if (link_up == !!(pcaps->caps & ICE_AQC_PHY_EN_LINK) &&
link_up == !!(pi->phy.link_info.link_info & ICE_AQ_LINK_UP))
goto out;
/* Use the current user PHY configuration. The current user PHY
* configuration is initialized during probe from PHY capabilities
* software mode, and updated on set PHY configuration.
*/
cfg = kmemdup(&pi->phy.curr_user_phy_cfg, sizeof(*cfg), GFP_KERNEL);
if (!cfg) {
retcode = -ENOMEM;
goto out;
}
cfg->caps |= ICE_AQ_PHY_ENA_AUTO_LINK_UPDT;
if (link_up)
cfg->caps |= ICE_AQ_PHY_ENA_LINK;
else
cfg->caps &= ~ICE_AQ_PHY_ENA_LINK;
retcode = ice_aq_set_phy_cfg(&vsi->back->hw, pi, cfg, NULL);
if (retcode) {
dev_err(dev, "Failed to set phy config, VSI %d error %d\n",
vsi->vsi_num, retcode);
retcode = -EIO;
}
kfree(cfg);
out:
kfree(pcaps);
return retcode;
}
/**
* ice_init_nvm_phy_type - Initialize the NVM PHY type
* @pi: port info structure
@ -2066,7 +1990,7 @@ static void ice_init_link_dflt_override(struct ice_port_info *pi)
* first time media is available. The ICE_LINK_DEFAULT_OVERRIDE_PENDING state
* is used to indicate that the user PHY cfg default override is initialized
* and the PHY has not been configured with the default override settings. The
* state is set here, and cleared in ice_configure_phy the first time the PHY is
* state is set here, and cleared in ice_phy_cfg the first time the PHY is
* configured.
*
* This function should be called only if the FW doesn't support default
@ -2172,14 +2096,18 @@ err_out:
}
/**
* ice_configure_phy - configure PHY
* ice_phy_cfg - configure PHY
* @vsi: VSI of PHY
* @link_en: true/false indicates to set link to enable/disable
*
* Set the PHY configuration. If the current PHY configuration is the same as
* the curr_user_phy_cfg, then do nothing to avoid link flap. Otherwise
* configure the based get PHY capabilities for topology with media.
* the curr_user_phy_cfg and link_en hasn't changed, then do nothing to avoid
* link flap. Otherwise configure the PHY based get PHY capabilities for
* topology with media and link_en.
*
* Return: 0 on success, negative on failure
*/
static int ice_configure_phy(struct ice_vsi *vsi)
static int ice_phy_cfg(struct ice_vsi *vsi, bool link_en)
{
struct device *dev = ice_pf_to_dev(vsi->back);
struct ice_port_info *pi = vsi->port_info;
@ -2199,9 +2127,6 @@ static int ice_configure_phy(struct ice_vsi *vsi)
phy->link_info.topo_media_conflict == ICE_AQ_LINK_TOPO_UNSUPP_MEDIA)
return -EPERM;
if (test_bit(ICE_FLAG_LINK_DOWN_ON_CLOSE_ENA, pf->flags))
return ice_force_phys_link_state(vsi, true);
pcaps = kzalloc(sizeof(*pcaps), GFP_KERNEL);
if (!pcaps)
return -ENOMEM;
@ -2215,10 +2140,8 @@ static int ice_configure_phy(struct ice_vsi *vsi)
goto done;
}
/* If PHY enable link is configured and configuration has not changed,
* there's nothing to do
*/
if (pcaps->caps & ICE_AQC_PHY_EN_LINK &&
/* Configuration has not changed. There's nothing to do. */
if (link_en == !!(pcaps->caps & ICE_AQC_PHY_EN_LINK) &&
ice_phy_caps_equals_cfg(pcaps, &phy->curr_user_phy_cfg))
goto done;
@ -2282,8 +2205,12 @@ static int ice_configure_phy(struct ice_vsi *vsi)
*/
ice_cfg_phy_fc(pi, cfg, phy->curr_user_fc_req);
/* Enable link and link update */
cfg->caps |= ICE_AQ_PHY_ENA_AUTO_LINK_UPDT | ICE_AQ_PHY_ENA_LINK;
/* Enable/Disable link and link update */
cfg->caps |= ICE_AQ_PHY_ENA_AUTO_LINK_UPDT;
if (link_en)
cfg->caps |= ICE_AQ_PHY_ENA_LINK;
else
cfg->caps &= ~ICE_AQ_PHY_ENA_LINK;
err = ice_aq_set_phy_cfg(&pf->hw, pi, cfg, NULL);
if (err)
@ -2336,7 +2263,7 @@ static void ice_check_media_subtask(struct ice_pf *pf)
test_bit(ICE_FLAG_LINK_DOWN_ON_CLOSE_ENA, vsi->back->flags))
return;
err = ice_configure_phy(vsi);
err = ice_phy_cfg(vsi, true);
if (!err)
clear_bit(ICE_FLAG_NO_MEDIA, pf->flags);
@ -3755,7 +3682,7 @@ int ice_vlan_rx_add_vid(struct net_device *netdev, __be16 proto, u16 vid)
ret = ice_fltr_set_vsi_promisc(&vsi->back->hw, vsi->idx,
ICE_MCAST_VLAN_PROMISC_BITS,
vid);
if (ret)
if (ret && ret != -EEXIST)
goto finish;
}
@ -4177,6 +4104,12 @@ int ice_vsi_recfg_qs(struct ice_vsi *vsi, int new_rx, int new_tx, bool locked)
}
ice_pf_dcb_recfg(pf, locked);
ice_vsi_open(vsi);
/* Rx rings are reallocated during VSI rebuild and lose their ptp_rx
* flag. Restore timestamp mode so newly allocated rings are set up
* for hardware Rx timestamping.
*/
if (test_bit(ICE_FLAG_PTP_SUPPORTED, pf->flags))
ice_ptp_restore_timestamp_mode(pf);
goto done;
rebuild_err:
@ -4892,9 +4825,15 @@ static int ice_init_link(struct ice_pf *pf)
if (!test_bit(ICE_FLAG_LINK_DOWN_ON_CLOSE_ENA, pf->flags)) {
struct ice_vsi *vsi = ice_get_main_vsi(pf);
struct ice_link_default_override_tlv *ldo;
bool link_en;
ldo = &pf->link_dflt_override;
link_en = !(ldo->options &
ICE_LINK_OVERRIDE_AUTO_LINK_DIS);
if (vsi)
ice_configure_phy(vsi);
ice_phy_cfg(vsi, link_en);
}
} else {
set_bit(ICE_FLAG_NO_MEDIA, pf->flags);
@ -5312,6 +5251,8 @@ ice_probe(struct pci_dev *pdev, const struct pci_device_id __always_unused *ent)
return err;
}
ice_init_dev_hw(pf);
adapter = ice_adapter_get(pdev);
if (IS_ERR(adapter)) {
err = PTR_ERR(adapter);
@ -8113,7 +8054,7 @@ int ice_set_rss_hfunc(struct ice_vsi *vsi, u8 hfunc)
ctx->info.q_opt_rss |=
FIELD_PREP(ICE_AQ_VSI_Q_OPT_RSS_HASH_M, hfunc);
ctx->info.q_opt_tc = vsi->info.q_opt_tc;
ctx->info.q_opt_flags = vsi->info.q_opt_rss;
ctx->info.q_opt_flags = vsi->info.q_opt_flags;
err = ice_update_vsi(hw, vsi->idx, ctx, NULL);
if (err) {
@ -9709,7 +9650,7 @@ int ice_open_internal(struct net_device *netdev)
}
}
err = ice_configure_phy(vsi);
err = ice_phy_cfg(vsi, true);
if (err) {
netdev_err(netdev, "Failed to set physical link up, error %d\n",
err);
@ -9750,7 +9691,7 @@ int ice_stop(struct net_device *netdev)
}
if (test_bit(ICE_FLAG_LINK_DOWN_ON_CLOSE_ENA, vsi->back->flags)) {
int link_err = ice_force_phys_link_state(vsi, false);
int link_err = ice_phy_cfg(vsi, false);
if (link_err) {
if (link_err == -ENOMEDIUM)

View File

@ -4,6 +4,7 @@
#include "ice.h"
#include "ice_lib.h"
#include "ice_trace.h"
#include "ice_txclk.h"
static const char ice_pin_names[][64] = {
"SDP0",
@ -54,11 +55,6 @@ static const struct ice_ptp_pin_desc ice_pin_desc_dpll[] = {
{ SDP3, { 3, -1 }, { 0, 0 }},
};
static struct ice_pf *ice_get_ctrl_pf(struct ice_pf *pf)
{
return !pf->adapter ? NULL : pf->adapter->ctrl_pf;
}
static struct ice_ptp *ice_get_ctrl_ptp(struct ice_pf *pf)
{
struct ice_pf *ctrl_pf = ice_get_ctrl_pf(pf);
@ -1328,6 +1324,9 @@ void ice_ptp_link_change(struct ice_pf *pf, bool linkup)
}
}
mutex_unlock(&pf->dplls.lock);
if (linkup)
ice_txclk_update_and_notify(pf);
}
switch (hw->mac_type) {
@ -2710,7 +2709,7 @@ static bool ice_any_port_has_timestamps(struct ice_pf *pf)
bool ice_ptp_tx_tstamps_pending(struct ice_pf *pf)
{
struct ice_hw *hw = &pf->hw;
unsigned int i;
int ret;
/* Check software indicator */
switch (pf->ptp.tx_interrupt_mode) {
@ -2731,16 +2730,19 @@ bool ice_ptp_tx_tstamps_pending(struct ice_pf *pf)
}
/* Check hardware indicator */
for (i = 0; i < ICE_GET_QUAD_NUM(hw->ptp.num_lports); i++) {
u64 tstamp_ready = 0;
int err;
err = ice_get_phy_tx_tstamp_ready(&pf->hw, i, &tstamp_ready);
if (err || tstamp_ready)
return true;
ret = ice_check_phy_tx_tstamp_ready(hw);
if (ret < 0) {
dev_dbg(ice_pf_to_dev(pf), "Unable to read PHY Tx timestamp ready bitmap, err %d\n",
ret);
/* Stop triggering IRQs if we're unable to read PHY */
return false;
}
return false;
/* ice_check_phy_tx_tstamp_ready() returns 1 if there are timestamps
* available, 0 if there are no waiting timestamps, and a negative
* value if there was an error (which we checked for above).
*/
return ret > 0;
}
/**
@ -2824,8 +2826,7 @@ static void ice_ptp_maybe_trigger_tx_interrupt(struct ice_pf *pf)
{
struct device *dev = ice_pf_to_dev(pf);
struct ice_hw *hw = &pf->hw;
bool trigger_oicr = false;
unsigned int i;
int ret;
if (!pf->ptp.port.tx.has_ready_bitmap)
return;
@ -2833,21 +2834,11 @@ static void ice_ptp_maybe_trigger_tx_interrupt(struct ice_pf *pf)
if (!ice_pf_src_tmr_owned(pf))
return;
for (i = 0; i < ICE_GET_QUAD_NUM(hw->ptp.num_lports); i++) {
u64 tstamp_ready;
int err;
err = ice_get_phy_tx_tstamp_ready(&pf->hw, i, &tstamp_ready);
if (!err && tstamp_ready) {
trigger_oicr = true;
break;
}
}
if (trigger_oicr) {
/* Trigger a software interrupt, to ensure this data
* gets processed.
*/
ret = ice_check_phy_tx_tstamp_ready(hw);
if (ret < 0) {
dev_dbg(dev, "PTP periodic task unable to read PHY timestamp ready bitmap, err %d\n",
ret);
} else if (ret) {
dev_dbg(dev, "PTP periodic task detected waiting timestamps. Triggering Tx timestamp interrupt now.\n");
wr32(hw, PFINT_OICR, PFINT_OICR_TSYN_TX_M);
@ -3067,14 +3058,9 @@ err:
dev_err(ice_pf_to_dev(pf), "PTP reset failed %d\n", err);
}
static int ice_ptp_setup_adapter(struct ice_pf *pf)
static void ice_ptp_setup_adapter(struct ice_pf *pf)
{
if (!ice_pf_src_tmr_owned(pf) || !ice_is_primary(&pf->hw))
return -EPERM;
pf->adapter->ctrl_pf = pf;
return 0;
}
static int ice_ptp_setup_pf(struct ice_pf *pf)
@ -3098,6 +3084,21 @@ static int ice_ptp_setup_pf(struct ice_pf *pf)
&pf->adapter->ports.ports);
mutex_unlock(&pf->adapter->ports.lock);
/* Seed the per-PHY Tx reference clock usage map for this port.
* Only meaningful on E825 (other MAC types don't expose tx-clk
* selection). No locking is needed because this runs during
* ice_ptp_init() before pf->dplls.lock exists and before any
* link event or DPLL callback can observe the map.
*/
if (pf->hw.mac_type == ICE_MAC_GENERIC_3K_E825) {
u8 port_num, phy;
port_num = ptp->port.port_num;
phy = port_num / pf->hw.ptp.ports_per_phy;
set_bit(port_num,
&ctrl_ptp->tx_refclks[phy][pf->ptp.port.tx_clk]);
}
return 0;
}
@ -3317,15 +3318,27 @@ void ice_ptp_init(struct ice_pf *pf)
/* If this function owns the clock hardware, it must allocate and
* configure the PTP clock device to represent it.
*/
if (ice_pf_src_tmr_owned(pf) && ice_is_primary(hw)) {
err = ice_ptp_setup_adapter(pf);
if (err)
goto err_exit;
if (ice_pf_src_tmr_owned(pf)) {
ice_ptp_setup_adapter(pf);
err = ice_ptp_init_owner(pf);
if (err)
goto err_exit;
}
ptp->port.tx_clk = ICE_REF_CLK_ENET;
ptp->port.tx_clk_req = ICE_REF_CLK_ENET;
if (hw->mac_type == ICE_MAC_GENERIC_3K_E825) {
enum ice_e825c_ref_clk tx_ref_clk;
err = ice_get_serdes_ref_sel_e825c(hw, ptp->port.port_num,
&tx_ref_clk);
if (!err) {
ptp->port.tx_clk = tx_ref_clk;
ptp->port.tx_clk_req = tx_ref_clk;
}
}
err = ice_ptp_setup_pf(pf);
if (err)
goto err_exit;

View File

@ -144,6 +144,8 @@ struct ice_ptp_tx {
* @link_up: indicates whether the link is up
* @tx_fifo_busy_cnt: number of times the Tx FIFO was busy
* @port_num: the port number this structure represents
* @tx_clk: currently active Tx reference clock source
* @tx_clk_req: requested Tx reference clock source (new target)
*/
struct ice_ptp_port {
struct list_head list_node;
@ -153,6 +155,8 @@ struct ice_ptp_port {
bool link_up;
u8 tx_fifo_busy_cnt;
u8 port_num;
enum ice_e825c_ref_clk tx_clk;
enum ice_e825c_ref_clk tx_clk_req;
};
enum ice_ptp_tx_interrupt {
@ -236,6 +240,7 @@ struct ice_ptp_pin_desc {
* @info: structure defining PTP hardware capabilities
* @clock: pointer to registered PTP clock device
* @tstamp_config: hardware timestamping configuration
* @tx_refclks: bitmaps table to store the information about TX reference clocks
* @reset_time: kernel time after clock stop on reset
* @tx_hwtstamp_good: number of completed Tx timestamp requests
* @tx_hwtstamp_skipped: number of Tx time stamp requests skipped
@ -261,6 +266,7 @@ struct ice_ptp {
struct ptp_clock_info info;
struct ptp_clock *clock;
struct kernel_hwtstamp_config tstamp_config;
unsigned long tx_refclks[ICE_E825_MAX_PHYS][ICE_REF_CLK_MAX];
u64 reset_time;
u64 tx_hwtstamp_good;
u32 tx_hwtstamp_skipped;

View File

@ -377,6 +377,31 @@ static void ice_ptp_cfg_sync_delay(const struct ice_hw *hw, u32 delay)
* The following functions operate on devices with the ETH 56G PHY.
*/
/**
* ice_ptp_init_phc_e825c - Perform E825C specific PHC initialization
* @hw: pointer to HW struct
*
* Perform E825C-specific PTP hardware clock initialization steps.
*
* Return: 0 on success, or a negative error value on failure.
*/
static int ice_ptp_init_phc_e825c(struct ice_hw *hw)
{
int err;
/* Soft reset all ports, to ensure everything is at a clean state */
for (int port = 0; port < hw->ptp.num_lports; port++) {
err = ice_ptp_phy_soft_reset_eth56g(hw, port);
if (err) {
ice_debug(hw, ICE_DBG_PTP, "Failed to soft reset port %d, err %d\n",
port, err);
return err;
}
}
return 0;
}
/**
* ice_ptp_get_dest_dev_e825 - get destination PHY for given port number
* @hw: pointer to the HW struct
@ -461,6 +486,43 @@ static int ice_read_phy_eth56g(struct ice_hw *hw, u8 port, u32 addr, u32 *val)
return err;
}
/**
* ice_get_serdes_ref_sel_e825c - Read current Tx ref clock source
* @hw: pointer to the HW struct
* @port: port number for which Tx reference clock is read
* @clk: Tx reference clock value (output)
*
* Return: 0 on success, other error codes when failed to read from PHY
*/
int ice_get_serdes_ref_sel_e825c(struct ice_hw *hw, u8 port,
enum ice_e825c_ref_clk *clk)
{
u8 lane = port % hw->ptp.ports_per_phy;
u32 serdes_rx_nt, serdes_tx_nt;
u32 val;
int ret;
ret = ice_read_phy_eth56g(hw, port,
SERDES_IP_IF_LN_FLXM_GENERAL(lane, 0),
&val);
if (ret)
return ret;
serdes_rx_nt = FIELD_GET(CFG_ICTL_PCS_REF_SEL_RX_NT, val);
serdes_tx_nt = FIELD_GET(CFG_ICTL_PCS_REF_SEL_TX_NT, val);
if (serdes_tx_nt == REF_SEL_NT_SYNCE &&
serdes_rx_nt == REF_SEL_NT_SYNCE)
*clk = ICE_REF_CLK_SYNCE;
else if (serdes_tx_nt == REF_SEL_NT_EREF0 &&
serdes_rx_nt == REF_SEL_NT_EREF0)
*clk = ICE_REF_CLK_EREF0;
else
*clk = ICE_REF_CLK_ENET;
return 0;
}
/**
* ice_phy_res_address_eth56g - Calculate a PHY port register address
* @hw: pointer to the HW struct
@ -1847,6 +1909,8 @@ static int ice_phy_cfg_mac_eth56g(struct ice_hw *hw, u8 port)
* @ena: enable or disable interrupt
* @threshold: interrupt threshold
*
* The threshold cannot be 0 while the interrupt is enabled.
*
* Configure TX timestamp interrupt for the specified port
*
* Return:
@ -1858,19 +1922,45 @@ int ice_phy_cfg_intr_eth56g(struct ice_hw *hw, u8 port, bool ena, u8 threshold)
int err;
u32 val;
if (ena && !threshold)
return -EINVAL;
err = ice_read_ptp_reg_eth56g(hw, port, PHY_REG_TS_INT_CONFIG, &val);
if (err)
return err;
val &= ~PHY_TS_INT_CONFIG_ENA_M;
if (ena) {
val |= PHY_TS_INT_CONFIG_ENA_M;
val &= ~PHY_TS_INT_CONFIG_THRESHOLD_M;
val |= FIELD_PREP(PHY_TS_INT_CONFIG_THRESHOLD_M, threshold);
} else {
val &= ~PHY_TS_INT_CONFIG_ENA_M;
err = ice_write_ptp_reg_eth56g(hw, port, PHY_REG_TS_INT_CONFIG,
val);
if (err) {
ice_debug(hw, ICE_DBG_PTP,
"Failed to update 'threshold' PHY_REG_TS_INT_CONFIG port=%u ena=%u threshold=%u\n",
port, !!ena, threshold);
return err;
}
val |= PHY_TS_INT_CONFIG_ENA_M;
}
return ice_write_ptp_reg_eth56g(hw, port, PHY_REG_TS_INT_CONFIG, val);
err = ice_write_ptp_reg_eth56g(hw, port, PHY_REG_TS_INT_CONFIG, val);
if (err) {
ice_debug(hw, ICE_DBG_PTP,
"Failed to update 'ena' PHY_REG_TS_INT_CONFIG port=%u ena=%u threshold=%u\n",
port, !!ena, threshold);
return err;
}
err = ice_read_ptp_reg_eth56g(hw, port, PHY_REG_TS_INT_CONFIG, &val);
if (err) {
ice_debug(hw, ICE_DBG_PTP,
"Failed to read PHY_REG_TS_INT_CONFIG port=%u ena=%u threshold=%u\n",
port, !!ena, threshold);
return err;
}
return 0;
}
/**
@ -2088,16 +2178,23 @@ int ice_start_phy_timer_eth56g(struct ice_hw *hw, u8 port)
}
incval = (u64)hi << 32 | lo;
if (!ice_ptp_lock(hw)) {
dev_err(ice_hw_to_dev(hw), "Failed to acquire PTP semaphore\n");
return -EBUSY;
}
err = ice_write_40b_ptp_reg_eth56g(hw, port, PHY_REG_TIMETUS_L, incval);
if (err)
return err;
goto err_ptp_unlock;
err = ice_ptp_one_port_cmd(hw, port, ICE_PTP_INIT_INCVAL);
if (err)
return err;
goto err_ptp_unlock;
ice_ptp_exec_tmr_cmd(hw);
ice_ptp_unlock(hw);
err = ice_sync_phy_timer_eth56g(hw, port);
if (err)
return err;
@ -2112,6 +2209,39 @@ int ice_start_phy_timer_eth56g(struct ice_hw *hw, u8 port)
ice_debug(hw, ICE_DBG_PTP, "Enabled clock on PHY port %u\n", port);
return 0;
err_ptp_unlock:
ice_ptp_unlock(hw);
return err;
}
/**
* ice_check_phy_tx_tstamp_ready_eth56g - Check Tx memory status for all ports
* @hw: pointer to the HW struct
*
* Check the PHY_REG_TX_MEMORY_STATUS for all ports. A set bit indicates
* a waiting timestamp.
*
* Return: 1 if any port has at least one timestamp ready bit set,
* 0 otherwise, and a negative error code if unable to read the bitmap.
*/
static int ice_check_phy_tx_tstamp_ready_eth56g(struct ice_hw *hw)
{
int port;
for (port = 0; port < hw->ptp.num_lports; port++) {
u64 tstamp_ready;
int err;
err = ice_get_phy_tx_tstamp_ready(hw, port, &tstamp_ready);
if (err)
return err;
if (tstamp_ready)
return 1;
}
return 0;
}
@ -2137,13 +2267,19 @@ int ice_ptp_read_tx_hwtstamp_status_eth56g(struct ice_hw *hw, u32 *ts_status)
*ts_status = 0;
for (phy = 0; phy < params->num_phys; phy++) {
u8 port;
int err;
err = ice_read_phy_eth56g(hw, phy, PHY_PTP_INT_STATUS, &status);
/* ice_read_phy_eth56g expects a port index, so use the first
* port of the PHY
*/
port = phy * hw->ptp.ports_per_phy;
err = ice_read_phy_eth56g(hw, port, PHY_PTP_INT_STATUS, &status);
if (err)
return err;
*ts_status |= (status & mask) << (phy * hw->ptp.ports_per_phy);
*ts_status |= (status & mask) << port;
}
ice_debug(hw, ICE_DBG_PTP, "PHY interrupt err: %x\n", *ts_status);
@ -2151,6 +2287,69 @@ int ice_ptp_read_tx_hwtstamp_status_eth56g(struct ice_hw *hw, u32 *ts_status)
return 0;
}
/**
* ice_ptp_phy_soft_reset_eth56g - Perform a PHY soft reset on ETH56G
* @hw: pointer to the HW structure
* @port: PHY port number
*
* Trigger a soft reset of the ETH56G PHY by toggling the soft reset
* bit in the PHY global register. The reset sequence consists of:
* 1. Clearing the soft reset bit
* 2. Asserting the soft reset bit
* 3. Clearing the soft reset bit again
*
* Short delays are inserted between each step to allow the hardware
* to settle. This provides a controlled way to reinitialize the PHY
* without requiring a full device reset.
*
* Return: 0 on success, or a negative error code on failure when
* reading or writing the PHY register.
*/
int ice_ptp_phy_soft_reset_eth56g(struct ice_hw *hw, u8 port)
{
u32 global_val;
int err;
err = ice_read_ptp_reg_eth56g(hw, port, PHY_REG_GLOBAL, &global_val);
if (err) {
ice_debug(hw, ICE_DBG_PTP, "Failed to read PHY_REG_GLOBAL for port %d, err %d\n",
port, err);
return err;
}
global_val &= ~PHY_REG_GLOBAL_SOFT_RESET_M;
ice_debug(hw, ICE_DBG_PTP, "Clearing soft reset bit for port %d, val: 0x%x\n",
port, global_val);
err = ice_write_ptp_reg_eth56g(hw, port, PHY_REG_GLOBAL, global_val);
if (err) {
ice_debug(hw, ICE_DBG_PTP, "Failed to write PHY_REG_GLOBAL for port %d, err %d\n",
port, err);
return err;
}
usleep_range(5000, 6000);
global_val |= PHY_REG_GLOBAL_SOFT_RESET_M;
ice_debug(hw, ICE_DBG_PTP, "Set soft reset bit for port %d, val: 0x%x\n",
port, global_val);
err = ice_write_ptp_reg_eth56g(hw, port, PHY_REG_GLOBAL, global_val);
if (err) {
ice_debug(hw, ICE_DBG_PTP, "Failed to write PHY_REG_GLOBAL for port %d, err %d\n",
port, err);
return err;
}
usleep_range(5000, 6000);
global_val &= ~PHY_REG_GLOBAL_SOFT_RESET_M;
ice_debug(hw, ICE_DBG_PTP, "Clear soft reset bit for port %d, val: 0x%x\n",
port, global_val);
err = ice_write_ptp_reg_eth56g(hw, port, PHY_REG_GLOBAL, global_val);
if (err)
ice_debug(hw, ICE_DBG_PTP, "Failed to write PHY_REG_GLOBAL for port %d, err %d\n",
port, err);
return err;
}
/**
* ice_get_phy_tx_tstamp_ready_eth56g - Read the Tx memory status register
* @hw: pointer to the HW struct
@ -4202,6 +4401,35 @@ ice_get_phy_tx_tstamp_ready_e82x(struct ice_hw *hw, u8 quad, u64 *tstamp_ready)
return 0;
}
/**
* ice_check_phy_tx_tstamp_ready_e82x - Check Tx memory status for all quads
* @hw: pointer to the HW struct
*
* Check the Q_REG_TX_MEMORY_STATUS for all quads. A set bit indicates
* a waiting timestamp.
*
* Return: 1 if any quad has at least one timestamp ready bit set,
* 0 otherwise, and a negative error value if unable to read the bitmap.
*/
static int ice_check_phy_tx_tstamp_ready_e82x(struct ice_hw *hw)
{
int quad;
for (quad = 0; quad < ICE_GET_QUAD_NUM(hw->ptp.num_lports); quad++) {
u64 tstamp_ready;
int err;
err = ice_get_phy_tx_tstamp_ready(hw, quad, &tstamp_ready);
if (err)
return err;
if (tstamp_ready)
return 1;
}
return 0;
}
/**
* ice_phy_cfg_intr_e82x - Configure TX timestamp interrupt
* @hw: pointer to the HW struct
@ -4323,18 +4551,17 @@ static int
ice_read_phy_tstamp_ll_e810(struct ice_hw *hw, u8 idx, u8 *hi, u32 *lo)
{
struct ice_e810_params *params = &hw->ptp.phy.e810;
unsigned long flags;
u32 val;
int err;
spin_lock_irqsave(&params->atqbal_wq.lock, flags);
spin_lock_irq(&params->atqbal_wq.lock);
/* Wait for any pending in-progress low latency interrupt */
err = wait_event_interruptible_locked_irq(params->atqbal_wq,
!(params->atqbal_flags &
ATQBAL_FLAGS_INTR_IN_PROGRESS));
if (err) {
spin_unlock_irqrestore(&params->atqbal_wq.lock, flags);
spin_unlock_irq(&params->atqbal_wq.lock);
return err;
}
@ -4349,7 +4576,7 @@ ice_read_phy_tstamp_ll_e810(struct ice_hw *hw, u8 idx, u8 *hi, u32 *lo)
REG_LL_PROXY_H);
if (err) {
ice_debug(hw, ICE_DBG_PTP, "Failed to read PTP timestamp using low latency read\n");
spin_unlock_irqrestore(&params->atqbal_wq.lock, flags);
spin_unlock_irq(&params->atqbal_wq.lock);
return err;
}
@ -4359,7 +4586,7 @@ ice_read_phy_tstamp_ll_e810(struct ice_hw *hw, u8 idx, u8 *hi, u32 *lo)
/* Read the low 32 bit value and set the TS valid bit */
*lo = rd32(hw, REG_LL_PROXY_L) | TS_VALID;
spin_unlock_irqrestore(&params->atqbal_wq.lock, flags);
spin_unlock_irq(&params->atqbal_wq.lock);
return 0;
}
@ -4755,6 +4982,23 @@ ice_get_phy_tx_tstamp_ready_e810(struct ice_hw *hw, u8 port, u64 *tstamp_ready)
return 0;
}
/**
* ice_check_phy_tx_tstamp_ready_e810 - Check Tx memory status register
* @hw: pointer to the HW struct
*
* The E810 devices do not have a Tx memory status register. Note this is
* intentionally different behavior from ice_get_phy_tx_tstamp_ready_e810
* which always says that all bits are ready. This function is called in cases
* where code will trigger interrupts if timestamps are waiting, and should
* not be called for E810 hardware.
*
* Return: 0.
*/
static int ice_check_phy_tx_tstamp_ready_e810(struct ice_hw *hw)
{
return 0;
}
/* E810 SMA functions
*
* The following functions operate specifically on E810 hardware and are used
@ -5009,6 +5253,21 @@ static void ice_get_phy_tx_tstamp_ready_e830(const struct ice_hw *hw, u8 port,
*tstamp_ready |= rd32(hw, E830_PRTMAC_TS_TX_MEM_VALID_L);
}
/**
* ice_check_phy_tx_tstamp_ready_e830 - Check Tx memory status register
* @hw: pointer to the HW struct
*
* Return: 1 if the device has waiting timestamps, 0 otherwise.
*/
static int ice_check_phy_tx_tstamp_ready_e830(struct ice_hw *hw)
{
u64 tstamp_ready;
ice_get_phy_tx_tstamp_ready_e830(hw, 0, &tstamp_ready);
return !!tstamp_ready;
}
/**
* ice_ptp_init_phy_e830 - initialize PHY parameters
* @ptp: pointer to the PTP HW struct
@ -5042,9 +5301,13 @@ static void ice_ptp_init_phy_e830(struct ice_ptp_hw *ptp)
*/
bool ice_ptp_lock(struct ice_hw *hw)
{
struct ice_pf *pf = container_of(hw, struct ice_pf, hw);
u32 hw_lock;
int i;
if (!ice_is_primary(hw))
hw = ice_get_primary_hw(pf);
#define MAX_TRIES 15
for (i = 0; i < MAX_TRIES; i++) {
@ -5071,6 +5334,11 @@ bool ice_ptp_lock(struct ice_hw *hw)
*/
void ice_ptp_unlock(struct ice_hw *hw)
{
struct ice_pf *pf = container_of(hw, struct ice_pf, hw);
if (!ice_is_primary(hw))
hw = ice_get_primary_hw(pf);
wr32(hw, PFTSYN_SEM + (PFTSYN_SEM_BYTES * hw->pf_id), 0);
}
@ -5564,7 +5832,7 @@ int ice_ptp_init_phc(struct ice_hw *hw)
case ICE_MAC_GENERIC:
return ice_ptp_init_phc_e82x(hw);
case ICE_MAC_GENERIC_3K_E825:
return 0;
return ice_ptp_init_phc_e825c(hw);
default:
return -EOPNOTSUPP;
}
@ -5601,6 +5869,33 @@ int ice_get_phy_tx_tstamp_ready(struct ice_hw *hw, u8 block, u64 *tstamp_ready)
}
}
/**
* ice_check_phy_tx_tstamp_ready - Check PHY Tx timestamp memory status
* @hw: pointer to the HW struct
*
* Check the PHY for Tx timestamp memory status on all ports. If you need to
* see individual timestamp status for each index, use
* ice_get_phy_tx_tstamp_ready() instead.
*
* Return: 1 if any port has timestamps available, 0 if there are no timestamps
* available, and a negative error code on failure.
*/
int ice_check_phy_tx_tstamp_ready(struct ice_hw *hw)
{
switch (hw->mac_type) {
case ICE_MAC_E810:
return ice_check_phy_tx_tstamp_ready_e810(hw);
case ICE_MAC_E830:
return ice_check_phy_tx_tstamp_ready_e830(hw);
case ICE_MAC_GENERIC:
return ice_check_phy_tx_tstamp_ready_e82x(hw);
case ICE_MAC_GENERIC_3K_E825:
return ice_check_phy_tx_tstamp_ready_eth56g(hw);
default:
return -EOPNOTSUPP;
}
}
/**
* ice_cgu_get_pin_desc_e823 - get pin description array
* @hw: pointer to the hw struct

View File

@ -265,6 +265,13 @@ struct ice_cgu_pin_desc {
struct dpll_pin_frequency *freq_supp;
};
enum ice_e825c_ref_clk {
ICE_REF_CLK_ENET,
ICE_REF_CLK_SYNCE,
ICE_REF_CLK_EREF0,
ICE_REF_CLK_MAX,
};
#define E810C_QSFP_C827_0_HANDLE 2
#define E810C_QSFP_C827_1_HANDLE 3
@ -300,6 +307,7 @@ void ice_ptp_reset_ts_memory(struct ice_hw *hw);
int ice_ptp_init_phc(struct ice_hw *hw);
void ice_ptp_init_hw(struct ice_hw *hw);
int ice_get_phy_tx_tstamp_ready(struct ice_hw *hw, u8 block, u64 *tstamp_ready);
int ice_check_phy_tx_tstamp_ready(struct ice_hw *hw);
int ice_ptp_one_port_cmd(struct ice_hw *hw, u8 configured_port,
enum ice_ptp_tmr_cmd configured_cmd);
@ -374,6 +382,9 @@ int ice_stop_phy_timer_eth56g(struct ice_hw *hw, u8 port, bool soft_reset);
int ice_start_phy_timer_eth56g(struct ice_hw *hw, u8 port);
int ice_phy_cfg_intr_eth56g(struct ice_hw *hw, u8 port, bool ena, u8 threshold);
int ice_phy_cfg_ptp_1step_eth56g(struct ice_hw *hw, u8 port);
int ice_ptp_phy_soft_reset_eth56g(struct ice_hw *hw, u8 port);
int ice_get_serdes_ref_sel_e825c(struct ice_hw *hw, u8 port,
enum ice_e825c_ref_clk *clk);
#define ICE_ETH56G_NOMINAL_INCVAL 0x140000000ULL
#define ICE_ETH56G_NOMINAL_PCS_REF_TUS 0x100000000ULL
@ -676,6 +687,9 @@ static inline u64 ice_get_base_incval(struct ice_hw *hw)
#define ICE_P0_GNSS_PRSNT_N BIT(4)
/* ETH56G PHY register addresses */
#define PHY_REG_GLOBAL 0x0
#define PHY_REG_GLOBAL_SOFT_RESET_M BIT(11)
/* Timestamp PHY incval registers */
#define PHY_REG_TIMETUS_L 0x8
#define PHY_REG_TIMETUS_U 0xC
@ -783,4 +797,12 @@ static inline u64 ice_get_base_incval(struct ice_hw *hw)
#define PHY_PTP_1STEP_PD_DELAY_M GENMASK(30, 1)
#define PHY_PTP_1STEP_PD_DLY_V_M BIT(31)
#define SERDES_IP_IF_LN_FLXM_GENERAL(n, m) \
(0x32B800 + (m) * 0x100000 + (n) * 0x8000)
#define CFG_ICTL_PCS_REF_SEL_RX_NT GENMASK(9, 6)
#define CFG_ICTL_PCS_REF_SEL_TX_NT GENMASK(28, 25)
#define REF_SEL_NT_ENET 0
#define REF_SEL_NT_EREF0 1
#define REF_SEL_NT_SYNCE 2
#endif /* _ICE_PTP_HW_H_ */

View File

@ -54,8 +54,9 @@ enum ice_sbq_dev_id {
};
enum ice_sbq_msg_opcode {
ice_sbq_msg_rd = 0x00,
ice_sbq_msg_wr = 0x01
ice_sbq_msg_rd = 0x00,
ice_sbq_msg_wr = 0x01,
ice_sbq_msg_wr_np = 0x02
};
#define ICE_SBQ_MSG_FLAGS 0x40

View File

@ -0,0 +1,354 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright (C) 2026 Intel Corporation */
#include "ice.h"
#include "ice_cpi.h"
#include "ice_txclk.h"
#define ICE_PHY0 0
#define ICE_PHY1 1
/**
* ice_txclk_get_pin - map TX reference clock to its DPLL pin
* @pf: pointer to the PF structure
* @ref_clk: TX reference clock selection
*
* Return the DPLL pin corresponding to a given external TX reference
* clock. Only external TX reference clocks (SYNCE and EREF0) are
* represented as DPLL pins. The internal ENET (TXCO) clock has no
* associated DPLL pin and therefore yields %NULL.
*
* This helper is used when emitting DPLL pin change notifications
* after TX reference clock transitions have been verified.
*
* Return: Pointer to the corresponding struct dpll_pin, or %NULL if
* the TX reference clock has no DPLL pin representation.
*/
struct dpll_pin *
ice_txclk_get_pin(struct ice_pf *pf, enum ice_e825c_ref_clk ref_clk)
{
switch (ref_clk) {
case ICE_REF_CLK_SYNCE:
return pf->dplls.txclks[E825_EXT_SYNCE_PIN_IDX].pin;
case ICE_REF_CLK_EREF0:
return pf->dplls.txclks[E825_EXT_EREF_PIN_IDX].pin;
case ICE_REF_CLK_ENET:
default:
return NULL;
}
}
/**
* ice_txclk_enable_peer - Enable required TX reference clock on peer PHY
* @pf: pointer to the PF structure
* @clk: TX reference clock that must be enabled
*
* Some TX reference clocks on E825-class devices (SyncE and EREF0) must
* be enabled on both PHY complexes to allow proper routing:
*
* - SyncE must be enabled on both PHYs when used by PHY0
* - EREF0 must be enabled on both PHYs when used by PHY1
*
* If the requested clock is not yet enabled on the peer PHY, enable it.
* ENET does not require duplication and is ignored.
*
* Return: 0 on success or negative error code on failure.
*/
static int ice_txclk_enable_peer(struct ice_pf *pf, enum ice_e825c_ref_clk clk)
{
struct ice_pf *ctrl_pf = ice_get_ctrl_pf(pf);
bool peer_clk_in_use;
u8 port_num, phy;
int err;
if (clk == ICE_REF_CLK_ENET)
return 0;
if (IS_ERR_OR_NULL(ctrl_pf)) {
dev_err(ice_pf_to_dev(pf),
"Can't enable tx-clk on peer: no controlling PF\n");
return -EINVAL;
}
port_num = pf->ptp.port.port_num;
phy = port_num / pf->hw.ptp.ports_per_phy;
peer_clk_in_use = true;
/* Hold ctrl_pf->dplls.lock across both the peer-usage check and
* the enable AQ command so that two PFs racing to enable the same
* peer-PHY clock cannot both observe peer_clk_in_use == false and
* issue duplicate enables.
*/
mutex_lock(&ctrl_pf->dplls.lock);
if (clk == ICE_REF_CLK_SYNCE && phy == ICE_PHY0)
peer_clk_in_use = ice_txclk_any_port_uses(ctrl_pf,
ICE_PHY1,
clk);
else if (clk == ICE_REF_CLK_EREF0 && phy == ICE_PHY1)
peer_clk_in_use = ice_txclk_any_port_uses(ctrl_pf,
ICE_PHY0,
clk);
if ((clk == ICE_REF_CLK_SYNCE && phy == ICE_PHY0 && !peer_clk_in_use) ||
(clk == ICE_REF_CLK_EREF0 && phy == ICE_PHY1 && !peer_clk_in_use)) {
u8 peer_phy = phy ? ICE_PHY0 : ICE_PHY1;
err = ice_cpi_ena_dis_clk_ref(&pf->hw, peer_phy, clk, true);
if (err) {
mutex_unlock(&ctrl_pf->dplls.lock);
dev_err(ice_pf_to_dev(pf),
"Failed to enable the %u TX clock for the %u PHY\n",
clk, peer_phy);
return err;
}
}
mutex_unlock(&ctrl_pf->dplls.lock);
return 0;
}
#define ICE_REFCLK_USER_TO_AQ_IDX(x) ((x) + 1)
/**
* ice_txclk_set_clk - Set Tx reference clock
* @pf: pointer to pf structure
* @clk: new Tx clock
*
* Return: 0 on success, negative value otherwise.
*/
int ice_txclk_set_clk(struct ice_pf *pf, enum ice_e825c_ref_clk clk)
{
struct ice_pf *ctrl_pf = ice_get_ctrl_pf(pf);
struct ice_port_info *port_info;
bool clk_in_use;
u8 port_num, phy;
int err;
if (pf->ptp.port.tx_clk == clk)
return 0;
if (IS_ERR_OR_NULL(ctrl_pf)) {
dev_err(ice_pf_to_dev(pf),
"Can't set tx-clk: no controlling PF\n");
return -EINVAL;
}
if (!test_bit(ICE_FLAG_DPLL, ctrl_pf->flags)) {
dev_err(ice_pf_to_dev(pf),
"Can't set tx-clk: ctrl PF DPLL not available\n");
return -EOPNOTSUPP;
}
port_num = pf->ptp.port.port_num;
phy = port_num / pf->hw.ptp.ports_per_phy;
port_info = pf->hw.port_info;
/* Hold ctrl_pf->dplls.lock across both the usage check and the
* enable AQ command so that two PFs racing to switch to the same
* (phy, clk) cannot both observe clk_in_use == false and issue
* duplicate enables. The tx_refclks bitmap is updated only later
* by ice_txclk_update_and_notify() after link-up, so without this
* the check-then-act window is wide open.
*/
mutex_lock(&ctrl_pf->dplls.lock);
clk_in_use = ice_txclk_any_port_uses(ctrl_pf, phy, clk);
if (!clk_in_use) {
err = ice_cpi_ena_dis_clk_ref(&pf->hw, phy, clk, true);
if (err) {
mutex_unlock(&ctrl_pf->dplls.lock);
dev_err(ice_pf_to_dev(pf), "Failed to enable the %u TX clock for the %u PHY\n",
clk, phy);
return err;
}
}
mutex_unlock(&ctrl_pf->dplls.lock);
if (!clk_in_use) {
err = ice_txclk_enable_peer(pf, clk);
if (err)
return err;
}
/* We are ready to switch to the new TX clk. */
err = ice_aq_set_link_restart_an(port_info, true, NULL,
ICE_REFCLK_USER_TO_AQ_IDX(clk));
if (err) {
dev_err(ice_pf_to_dev(pf),
"AN restart AQ command failed with err %d\n",
err);
return err;
}
/* Clear txclk_switch_requested only after the AN restart AQ has been
* accepted by FW. Clearing earlier would race with any asynchronous
* link-up event: ice_txclk_update_and_notify() would observe the
* cleared flag, read the stale SERDES selector, and misinterpret the
* not-yet-applied switch as a HW rejection. Only clear if no newer
* request has overwritten tx_clk_req while we were dropping locks.
*/
mutex_lock(&pf->dplls.lock);
if (pf->ptp.port.tx_clk_req == clk)
pf->dplls.txclk_switch_requested = false;
mutex_unlock(&pf->dplls.lock);
return 0;
}
/**
* ice_txclk_update_and_notify - Validate TX reference clock switching
* @pf: pointer to PF structure
*
* After a link-up event, verify whether the previously requested TX reference
* clock transition actually succeeded. The SERDES reference selector reflects
* the effective hardware choice, which may differ from the requested clock
* when Auto-Negotiation or firmware applies additional policy.
*
* If the hardware-selected clock differs from the requested one, update the
* software state accordingly and stop further processing.
*
* When the switch is successful, update the perPHY usage bitmaps so that the
* driver knows which reference clock is currently in use by this port.
*
* This function does not initiate a clock switch; it only validates the result
* of a previously triggered transition and performs cleanup of unused clocks.
*/
void ice_txclk_update_and_notify(struct ice_pf *pf)
{
struct ice_ptp_port *ptp_port = &pf->ptp.port;
struct ice_pf *ctrl_pf = ice_get_ctrl_pf(pf);
struct dpll_pin *old_pin = NULL;
struct dpll_pin *new_pin = NULL;
struct ice_hw *hw = &pf->hw;
enum ice_e825c_ref_clk clk;
bool notify_dpll = false;
int err;
u8 phy;
phy = ptp_port->port_num / hw->ptp.ports_per_phy;
/* Hold txclk_notify_rwsem for read across the entire critical
* region, including the out-of-lock dpll_*_change_ntf() calls
* below. ice_dpll_deinit() takes the write side to wait for all
* in-flight notifications to complete before freeing pins and the
* TXC DPLL device, preventing a use-after-free on rmmod.
*/
down_read(&pf->dplls.txclk_notify_rwsem);
mutex_lock(&pf->dplls.lock);
/* Bail out if DPLL subsystem is being torn down. ice_dpll_deinit()
* clears ICE_FLAG_DPLL before freeing pins and the dpll device, so a
* cleared flag under the lock means those objects can no longer be
* safely dereferenced.
*/
if (!test_bit(ICE_FLAG_DPLL, pf->flags)) {
mutex_unlock(&pf->dplls.lock);
goto out;
}
/* If a switch is still pending, the link-up event preceded the
* worker's AN restart. Hardware hasn't applied the new clock yet,
* so reading the SERDES selector now would produce a false failure.
* Let the worker run first; the link-up that follows the AN restart
* will trigger the verification.
*/
if (pf->dplls.txclk_switch_requested) {
mutex_unlock(&pf->dplls.lock);
goto out;
}
/* no TX clock change requested */
if (pf->ptp.port.tx_clk == pf->ptp.port.tx_clk_req) {
mutex_unlock(&pf->dplls.lock);
goto out;
}
/* verify current Tx reference settings */
err = ice_get_serdes_ref_sel_e825c(hw,
ptp_port->port_num,
&clk);
if (err) {
mutex_unlock(&pf->dplls.lock);
goto out;
}
if (clk != pf->ptp.port.tx_clk_req) {
dev_warn(ice_pf_to_dev(pf),
"Failed to switch tx-clk for phy %d and clk %u (current: %u)\n",
phy, pf->ptp.port.tx_clk_req, clk);
old_pin = ice_txclk_get_pin(pf, pf->ptp.port.tx_clk_req);
new_pin = ice_txclk_get_pin(pf, clk);
pf->ptp.port.tx_clk = clk;
pf->ptp.port.tx_clk_req = clk;
/* Update the reference clock bitmap to match the hardware
* clock that was actually accepted, so that
* ice_txclk_any_port_uses() reflects reality even on failure.
* The map is owned by ctrl_pf; take its lock per documented
* order (pf->dplls.lock first, then ctrl_pf->dplls.lock) so
* readers on other PFs observe a consistent snapshot.
*/
if (!IS_ERR_OR_NULL(ctrl_pf)) {
if (ctrl_pf != pf)
mutex_lock(&ctrl_pf->dplls.lock);
for (int i = 0; i < ICE_REF_CLK_MAX; i++) {
if (clk == i)
set_bit(ptp_port->port_num,
&ctrl_pf->ptp.tx_refclks[phy][i]);
else
clear_bit(ptp_port->port_num,
&ctrl_pf->ptp.tx_refclks[phy][i]);
}
if (ctrl_pf != pf)
mutex_unlock(&ctrl_pf->dplls.lock);
}
goto err_notify;
}
old_pin = ice_txclk_get_pin(pf, pf->ptp.port.tx_clk);
pf->ptp.port.tx_clk = clk;
pf->ptp.port.tx_clk_req = clk;
if (IS_ERR_OR_NULL(ctrl_pf)) {
dev_err(ice_pf_to_dev(pf),
"Can't set tx-clk: no controlling PF\n");
goto err_notify;
}
/* update Tx reference clock usage map; map is owned by ctrl_pf,
* take its lock per documented order so readers on other PFs see
* a consistent view.
*/
if (ctrl_pf != pf)
mutex_lock(&ctrl_pf->dplls.lock);
for (int i = 0; i < ICE_REF_CLK_MAX; i++)
if (clk == i)
set_bit(ptp_port->port_num,
&ctrl_pf->ptp.tx_refclks[phy][i]);
else
clear_bit(ptp_port->port_num,
&ctrl_pf->ptp.tx_refclks[phy][i]);
if (ctrl_pf != pf)
mutex_unlock(&ctrl_pf->dplls.lock);
err_notify:
/* Update TXC DPLL lock status based on effective TX clk, while still
* holding the lock to prevent concurrent link-up events from racing
* on dpll_state.
*/
if (!IS_ERR_OR_NULL(pf->dplls.txc.dpll)) {
enum dpll_lock_status new_lock = ice_txclk_lock_status(clk);
if (pf->dplls.txc.dpll_state != new_lock) {
pf->dplls.txc.dpll_state = new_lock;
notify_dpll = true;
}
}
mutex_unlock(&pf->dplls.lock);
/* Notify TX clk pins state transition */
if (old_pin)
dpll_pin_change_ntf(old_pin);
if (new_pin)
dpll_pin_change_ntf(new_pin);
if (notify_dpll && !IS_ERR_OR_NULL(pf->dplls.txc.dpll))
dpll_device_change_ntf(pf->dplls.txc.dpll);
out:
up_read(&pf->dplls.txclk_notify_rwsem);
}

View File

@ -0,0 +1,40 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (C) 2026 Intel Corporation */
#ifndef _ICE_TXCLK_H_
#define _ICE_TXCLK_H_
/**
* ice_txclk_any_port_uses - check if any port on a PHY uses this TX refclk
* @ctrl_pf: control PF (owner of the shared tx_refclks map)
* @phy: PHY index
* @clk: TX reference clock
*
* Return: true if any bit (port) is set for this clock on this PHY
*/
static inline bool
ice_txclk_any_port_uses(const struct ice_pf *ctrl_pf, u8 phy,
enum ice_e825c_ref_clk clk)
{
return find_first_bit(&ctrl_pf->ptp.tx_refclks[phy][clk],
BITS_PER_LONG) < BITS_PER_LONG;
}
static inline enum dpll_lock_status
ice_txclk_lock_status(enum ice_e825c_ref_clk clk)
{
switch (clk) {
case ICE_REF_CLK_SYNCE:
case ICE_REF_CLK_EREF0:
return DPLL_LOCK_STATUS_LOCKED;
case ICE_REF_CLK_ENET:
default:
return DPLL_LOCK_STATUS_UNLOCKED;
}
}
int ice_txclk_set_clk(struct ice_pf *pf, enum ice_e825c_ref_clk clk);
void ice_txclk_update_and_notify(struct ice_pf *pf);
struct dpll_pin *ice_txclk_get_pin(struct ice_pf *pf,
enum ice_e825c_ref_clk ref_clk);
#endif /* _ICE_TXCLK_H_ */

View File

@ -190,9 +190,10 @@ void ice_free_tstamp_ring(struct ice_tx_ring *tx_ring)
void ice_free_tx_tstamp_ring(struct ice_tx_ring *tx_ring)
{
ice_free_tstamp_ring(tx_ring);
clear_bit(ICE_TX_RING_FLAGS_TXTIME, tx_ring->flags);
smp_wmb(); /* order flag clear before pointer NULL */
kfree_rcu(tx_ring->tstamp_ring, rcu);
tx_ring->tstamp_ring = NULL;
tx_ring->flags &= ~ICE_TX_FLAGS_TXTIME;
WRITE_ONCE(tx_ring->tstamp_ring, NULL);
}
/**
@ -405,7 +406,7 @@ static int ice_alloc_tstamp_ring(struct ice_tx_ring *tx_ring)
tx_ring->tstamp_ring = tstamp_ring;
tstamp_ring->desc = NULL;
tstamp_ring->count = ice_calc_ts_ring_count(tx_ring);
tx_ring->flags |= ICE_TX_FLAGS_TXTIME;
set_bit(ICE_TX_RING_FLAGS_TXTIME, tx_ring->flags);
return 0;
}
@ -1521,13 +1522,20 @@ ice_tx_map(struct ice_tx_ring *tx_ring, struct ice_tx_buf *first,
return;
if (ice_is_txtime_cfg(tx_ring)) {
struct ice_tstamp_ring *tstamp_ring = tx_ring->tstamp_ring;
u32 tstamp_count = tstamp_ring->count;
u32 j = tstamp_ring->next_to_use;
struct ice_tstamp_ring *tstamp_ring;
u32 tstamp_count, j;
struct ice_ts_desc *ts_desc;
struct timespec64 ts;
u32 tstamp;
smp_rmb(); /* order flag read before pointer read */
tstamp_ring = READ_ONCE(tx_ring->tstamp_ring);
if (unlikely(!tstamp_ring))
goto ring_kick;
tstamp_count = tstamp_ring->count;
j = tstamp_ring->next_to_use;
ts = ktime_to_timespec64(first->skb->tstamp);
tstamp = ts.tv_nsec >> ICE_TXTIME_CTX_RESOLUTION_128NS;
@ -1555,6 +1563,7 @@ ice_tx_map(struct ice_tx_ring *tx_ring, struct ice_tx_buf *first,
tstamp_ring->next_to_use = j;
writel_relaxed(j, tstamp_ring->tail);
} else {
ring_kick:
writel_relaxed(i, tx_ring->tail);
}
return;
@ -1814,7 +1823,7 @@ ice_tx_prepare_vlan_flags(struct ice_tx_ring *tx_ring, struct ice_tx_buf *first)
*/
if (skb_vlan_tag_present(skb)) {
first->vid = skb_vlan_tag_get(skb);
if (tx_ring->flags & ICE_TX_FLAGS_RING_VLAN_L2TAG2)
if (test_bit(ICE_TX_RING_FLAGS_VLAN_L2TAG2, tx_ring->flags))
first->tx_flags |= ICE_TX_FLAGS_HW_OUTER_SINGLE_VLAN;
else
first->tx_flags |= ICE_TX_FLAGS_HW_VLAN;

View File

@ -212,6 +212,14 @@ enum ice_rx_dtype {
ICE_RX_DTYPE_SPLIT_ALWAYS = 2,
};
enum ice_tx_ring_flags {
ICE_TX_RING_FLAGS_XDP,
ICE_TX_RING_FLAGS_VLAN_L2TAG1,
ICE_TX_RING_FLAGS_VLAN_L2TAG2,
ICE_TX_RING_FLAGS_TXTIME,
ICE_TX_RING_FLAGS_NBITS,
};
struct ice_pkt_ctx {
u64 cached_phctime;
__be16 vlan_proto;
@ -352,11 +360,7 @@ struct ice_tx_ring {
u16 count; /* Number of descriptors */
u16 q_index; /* Queue number of ring */
u8 flags;
#define ICE_TX_FLAGS_RING_XDP BIT(0)
#define ICE_TX_FLAGS_RING_VLAN_L2TAG1 BIT(1)
#define ICE_TX_FLAGS_RING_VLAN_L2TAG2 BIT(2)
#define ICE_TX_FLAGS_TXTIME BIT(3)
DECLARE_BITMAP(flags, ICE_TX_RING_FLAGS_NBITS);
struct xsk_buff_pool *xsk_pool;
@ -398,7 +402,7 @@ static inline bool ice_ring_ch_enabled(struct ice_tx_ring *ring)
static inline bool ice_ring_is_xdp(struct ice_tx_ring *ring)
{
return !!(ring->flags & ICE_TX_FLAGS_RING_XDP);
return test_bit(ICE_TX_RING_FLAGS_XDP, ring->flags);
}
enum ice_container_type {

View File

@ -893,6 +893,8 @@ struct ice_ptp_hw {
u8 ports_per_phy;
};
#define ICE_E825_MAX_PHYS 2
/* Port hardware description */
struct ice_hw {
u8 __iomem *hw_addr;

View File

@ -811,7 +811,12 @@ void ice_reset_all_vfs(struct ice_pf *pf)
ice_vf_ctrl_invalidate_vsi(vf);
ice_vf_pre_vsi_rebuild(vf);
ice_vf_rebuild_vsi(vf);
if (ice_vf_rebuild_vsi(vf)) {
dev_err(dev, "VF %u VSI rebuild failed, leaving VF disabled\n",
vf->vf_id);
mutex_unlock(&vf->cfg_lock);
continue;
}
ice_vf_post_vsi_rebuild(vf);
ice_eswitch_attach_vf(pf, vf);

View File

@ -840,7 +840,7 @@ int ice_vc_cfg_qs_msg(struct ice_vf *vf, u8 *msg)
if (qpi->rxq.databuffer_size != 0 &&
(qpi->rxq.databuffer_size > ((16 * 1024) - 128) ||
qpi->rxq.databuffer_size < 1024))
qpi->rxq.databuffer_size < 128))
goto error_param;
ring->rx_buf_len = qpi->rxq.databuffer_size;

View File

@ -196,7 +196,7 @@ static void octep_vf_setup_iq_regs_cn93(struct octep_vf_device *oct, int iq_no)
}
/* Setup registers for a hardware Rx Queue */
static void octep_vf_setup_oq_regs_cn93(struct octep_vf_device *oct, int oq_no)
static int octep_vf_setup_oq_regs_cn93(struct octep_vf_device *oct, int oq_no)
{
struct octep_vf_oq *oq = oct->oq[oq_no];
u32 time_threshold = 0;
@ -239,6 +239,7 @@ static void octep_vf_setup_oq_regs_cn93(struct octep_vf_device *oct, int oq_no)
time_threshold = CFG_GET_OQ_INTR_TIME(oct->conf);
reg_val = ((u64)time_threshold << 32) | CFG_GET_OQ_INTR_PKT(oct->conf);
octep_vf_write_csr64(oct, CN93_VF_SDP_R_OUT_INT_LEVELS(oq_no), reg_val);
return 0;
}
/* Setup registers for a VF mailbox */

View File

@ -199,11 +199,13 @@ static void octep_vf_setup_iq_regs_cnxk(struct octep_vf_device *oct, int iq_no)
}
/* Setup registers for a hardware Rx Queue */
static void octep_vf_setup_oq_regs_cnxk(struct octep_vf_device *oct, int oq_no)
static int octep_vf_setup_oq_regs_cnxk(struct octep_vf_device *oct, int oq_no)
{
struct octep_vf_oq *oq = oct->oq[oq_no];
unsigned long t_out_jiffies;
u32 time_threshold = 0;
u64 oq_ctl = ULL(0);
u64 reg_ba_val;
u64 reg_val;
reg_val = octep_vf_read_csr64(oct, CNXK_VF_SDP_R_OUT_CONTROL(oq_no));
@ -214,6 +216,38 @@ static void octep_vf_setup_oq_regs_cnxk(struct octep_vf_device *oct, int oq_no)
reg_val = octep_vf_read_csr64(oct, CNXK_VF_SDP_R_OUT_CONTROL(oq_no));
} while (!(reg_val & CNXK_VF_R_OUT_CTL_IDLE));
}
octep_vf_write_csr64(oct, CNXK_VF_SDP_R_OUT_WMARK(oq_no),
oq->max_count);
/* Wait for WMARK to get applied */
usleep_range(10, 15);
octep_vf_write_csr64(oct, CNXK_VF_SDP_R_OUT_SLIST_BADDR(oq_no),
oq->desc_ring_dma);
octep_vf_write_csr64(oct, CNXK_VF_SDP_R_OUT_SLIST_RSIZE(oq_no),
oq->max_count);
reg_ba_val = octep_vf_read_csr64(oct,
CNXK_VF_SDP_R_OUT_SLIST_BADDR(oq_no));
if (reg_ba_val != oq->desc_ring_dma) {
t_out_jiffies = jiffies + 10 * HZ;
do {
if (reg_ba_val == ULLONG_MAX)
return -EFAULT;
octep_vf_write_csr64(oct,
CNXK_VF_SDP_R_OUT_SLIST_BADDR
(oq_no), oq->desc_ring_dma);
octep_vf_write_csr64(oct,
CNXK_VF_SDP_R_OUT_SLIST_RSIZE
(oq_no), oq->max_count);
reg_ba_val =
octep_vf_read_csr64(oct,
CNXK_VF_SDP_R_OUT_SLIST_BADDR
(oq_no));
} while ((reg_ba_val != oq->desc_ring_dma) &&
time_before(jiffies, t_out_jiffies));
if (reg_ba_val != oq->desc_ring_dma)
return -EAGAIN;
}
reg_val &= ~(CNXK_VF_R_OUT_CTL_IMODE);
reg_val &= ~(CNXK_VF_R_OUT_CTL_ROR_P);
@ -227,8 +261,6 @@ static void octep_vf_setup_oq_regs_cnxk(struct octep_vf_device *oct, int oq_no)
reg_val |= (CNXK_VF_R_OUT_CTL_ES_P);
octep_vf_write_csr64(oct, CNXK_VF_SDP_R_OUT_CONTROL(oq_no), reg_val);
octep_vf_write_csr64(oct, CNXK_VF_SDP_R_OUT_SLIST_BADDR(oq_no), oq->desc_ring_dma);
octep_vf_write_csr64(oct, CNXK_VF_SDP_R_OUT_SLIST_RSIZE(oq_no), oq->max_count);
oq_ctl = octep_vf_read_csr64(oct, CNXK_VF_SDP_R_OUT_CONTROL(oq_no));
/* Clear the ISIZE and BSIZE (22-0) */
@ -250,6 +282,7 @@ static void octep_vf_setup_oq_regs_cnxk(struct octep_vf_device *oct, int oq_no)
reg_val &= ~GENMASK_ULL(31, 0);
reg_val |= CFG_GET_OQ_WMARK(oct->conf);
octep_vf_write_csr64(oct, CNXK_VF_SDP_R_OUT_WMARK(oq_no), reg_val);
return 0;
}
/* Setup registers for a VF mailbox */

View File

@ -220,7 +220,7 @@ static int octep_vf_request_irqs(struct octep_vf_device *oct)
ioq_irq_err:
while (i) {
--i;
free_irq(oct->msix_entries[i].vector, oct);
free_irq(oct->msix_entries[i].vector, oct->ioq_vector[i]);
}
return -1;
}
@ -287,29 +287,46 @@ static void octep_vf_clean_irqs(struct octep_vf_device *oct)
octep_vf_free_ioq_vectors(oct);
}
/**
* octep_vf_update_pkt() - Update IQ/OQ IN/OUT_CNT registers.
*
* @iq: Octeon Tx queue data structure.
* @oq: Octeon Rx queue data structure.
*/
static void octep_vf_update_pkt(struct octep_vf_iq *iq, struct octep_vf_oq *oq)
{
u32 pkts_pend = READ_ONCE(oq->pkts_pending);
u32 last_pkt_count = READ_ONCE(oq->last_pkt_count);
u32 pkts_processed = READ_ONCE(iq->pkts_processed);
u32 pkt_in_done = READ_ONCE(iq->pkt_in_done);
netdev_dbg(iq->netdev, "enabling intr for Q-%u\n", iq->q_no);
if (pkts_processed) {
writel(pkts_processed, iq->inst_cnt_reg);
readl(iq->inst_cnt_reg);
WRITE_ONCE(iq->pkt_in_done, (pkt_in_done - pkts_processed));
WRITE_ONCE(iq->pkts_processed, 0);
}
if (last_pkt_count - pkts_pend) {
writel(last_pkt_count - pkts_pend, oq->pkts_sent_reg);
readl(oq->pkts_sent_reg);
WRITE_ONCE(oq->last_pkt_count, pkts_pend);
}
/* Flush the previous wrties before writing to RESEND bit */
smp_wmb();
}
/**
* octep_vf_enable_ioq_irq() - Enable MSI-x interrupt of a Tx/Rx queue.
*
* @iq: Octeon Tx queue data structure.
* @oq: Octeon Rx queue data structure.
*/
static void octep_vf_enable_ioq_irq(struct octep_vf_iq *iq, struct octep_vf_oq *oq)
static void octep_vf_enable_ioq_irq(struct octep_vf_iq *iq,
struct octep_vf_oq *oq)
{
u32 pkts_pend = oq->pkts_pending;
netdev_dbg(iq->netdev, "enabling intr for Q-%u\n", iq->q_no);
if (iq->pkts_processed) {
writel(iq->pkts_processed, iq->inst_cnt_reg);
iq->pkt_in_done -= iq->pkts_processed;
iq->pkts_processed = 0;
}
if (oq->last_pkt_count - pkts_pend) {
writel(oq->last_pkt_count - pkts_pend, oq->pkts_sent_reg);
oq->last_pkt_count = pkts_pend;
}
/* Flush the previous wrties before writing to RESEND bit */
smp_wmb();
writeq(1UL << OCTEP_VF_OQ_INTR_RESEND_BIT, oq->pkts_sent_reg);
writeq(1UL << OCTEP_VF_IQ_INTR_RESEND_BIT, iq->inst_cnt_reg);
}
@ -335,6 +352,7 @@ static int octep_vf_napi_poll(struct napi_struct *napi, int budget)
if (tx_pending || rx_done >= budget)
return budget;
octep_vf_update_pkt(ioq_vector->iq, ioq_vector->oq);
if (likely(napi_complete_done(napi, rx_done)))
octep_vf_enable_ioq_irq(ioq_vector->iq, ioq_vector->oq);

View File

@ -55,7 +55,7 @@ struct octep_vf_mmio {
struct octep_vf_hw_ops {
void (*setup_iq_regs)(struct octep_vf_device *oct, int q);
void (*setup_oq_regs)(struct octep_vf_device *oct, int q);
int (*setup_oq_regs)(struct octep_vf_device *oct, int q);
void (*setup_mbox_regs)(struct octep_vf_device *oct, int mbox);
irqreturn_t (*non_ioq_intr_handler)(void *ioq_vector);

View File

@ -12,6 +12,8 @@
#include "octep_vf_config.h"
#include "octep_vf_main.h"
static void octep_vf_oq_free_ring_buffers(struct octep_vf_oq *oq);
static void octep_vf_oq_reset_indices(struct octep_vf_oq *oq)
{
oq->host_read_idx = 0;
@ -171,11 +173,15 @@ static int octep_vf_setup_oq(struct octep_vf_device *oct, int q_no)
goto oq_fill_buff_err;
octep_vf_oq_reset_indices(oq);
oct->hw_ops.setup_oq_regs(oct, q_no);
if (oct->hw_ops.setup_oq_regs(oct, q_no))
goto oq_setup_err;
oct->num_oqs++;
return 0;
oq_setup_err:
octep_vf_oq_free_ring_buffers(oq);
oq_fill_buff_err:
vfree(oq->buff_info);
oq->buff_info = NULL;
@ -319,9 +325,16 @@ static int octep_vf_oq_check_hw_for_pkts(struct octep_vf_device *oct,
struct octep_vf_oq *oq)
{
u32 pkt_count, new_pkts;
u32 last_pkt_count, pkts_pending;
pkt_count = readl(oq->pkts_sent_reg);
new_pkts = pkt_count - oq->last_pkt_count;
last_pkt_count = READ_ONCE(oq->last_pkt_count);
new_pkts = pkt_count - last_pkt_count;
if (pkt_count < last_pkt_count) {
dev_err(oq->dev, "OQ-%u pkt_count(%u) < oq->last_pkt_count(%u)\n",
oq->q_no, pkt_count, last_pkt_count);
}
/* Clear the hardware packets counter register if the rx queue is
* being processed continuously with-in a single interrupt and
@ -333,11 +346,17 @@ static int octep_vf_oq_check_hw_for_pkts(struct octep_vf_device *oct,
pkt_count = readl(oq->pkts_sent_reg);
new_pkts += pkt_count;
}
oq->last_pkt_count = pkt_count;
oq->pkts_pending += new_pkts;
WRITE_ONCE(oq->last_pkt_count, pkt_count);
pkts_pending = READ_ONCE(oq->pkts_pending);
WRITE_ONCE(oq->pkts_pending, (pkts_pending + new_pkts));
return new_pkts;
}
static inline u32 octep_vf_oq_next_idx(struct octep_vf_oq *oq, u32 idx)
{
return (idx + 1 == oq->max_count) ? 0 : idx + 1;
}
/**
* __octep_vf_oq_process_rx() - Process hardware Rx queue and push to stack.
*
@ -363,7 +382,7 @@ static int __octep_vf_oq_process_rx(struct octep_vf_device *oct,
struct sk_buff *skb;
u32 read_idx;
read_idx = oq->host_read_idx;
read_idx = READ_ONCE(oq->host_read_idx);
rx_bytes = 0;
desc_used = 0;
for (pkt = 0; pkt < pkts_to_process; pkt++) {
@ -395,30 +414,52 @@ static int __octep_vf_oq_process_rx(struct octep_vf_device *oct,
data_offset = OCTEP_VF_OQ_RESP_HW_SIZE;
rx_ol_flags = 0;
}
rx_bytes += buff_info->len;
if (buff_info->len <= oq->max_single_buffer_size) {
skb = napi_build_skb((void *)resp_hw, PAGE_SIZE);
if (!skb) {
oq->stats->alloc_failures++;
desc_used++;
read_idx = octep_vf_oq_next_idx(oq, read_idx);
continue;
}
rx_bytes += buff_info->len;
skb_reserve(skb, data_offset);
skb_put(skb, buff_info->len);
read_idx++;
desc_used++;
if (read_idx == oq->max_count)
read_idx = 0;
read_idx = octep_vf_oq_next_idx(oq, read_idx);
} else {
struct skb_shared_info *shinfo;
u16 data_len;
skb = napi_build_skb((void *)resp_hw, PAGE_SIZE);
if (!skb) {
oq->stats->alloc_failures++;
desc_used++;
read_idx = octep_vf_oq_next_idx(oq, read_idx);
data_len = buff_info->len - oq->max_single_buffer_size;
while (data_len) {
dma_unmap_page(oq->dev, oq->desc_ring[read_idx].buffer_ptr,
PAGE_SIZE, DMA_FROM_DEVICE);
buff_info = (struct octep_vf_rx_buffer *)
&oq->buff_info[read_idx];
buff_info->page = NULL;
if (data_len < oq->buffer_size)
data_len = 0;
else
data_len -= oq->buffer_size;
desc_used++;
read_idx = octep_vf_oq_next_idx(oq, read_idx);
}
continue;
}
rx_bytes += buff_info->len;
skb_reserve(skb, data_offset);
/* Head fragment includes response header(s);
* subsequent fragments contains only data.
*/
skb_put(skb, oq->max_single_buffer_size);
read_idx++;
desc_used++;
if (read_idx == oq->max_count)
read_idx = 0;
read_idx = octep_vf_oq_next_idx(oq, read_idx);
shinfo = skb_shinfo(skb);
data_len = buff_info->len - oq->max_single_buffer_size;
@ -440,10 +481,8 @@ static int __octep_vf_oq_process_rx(struct octep_vf_device *oct,
buff_info->len,
buff_info->len);
buff_info->page = NULL;
read_idx++;
desc_used++;
if (read_idx == oq->max_count)
read_idx = 0;
read_idx = octep_vf_oq_next_idx(oq, read_idx);
}
}
@ -457,7 +496,7 @@ static int __octep_vf_oq_process_rx(struct octep_vf_device *oct,
napi_gro_receive(oq->napi, skb);
}
oq->host_read_idx = read_idx;
WRITE_ONCE(oq->host_read_idx, read_idx);
oq->refill_count += desc_used;
oq->stats->packets += pkt;
oq->stats->bytes += rx_bytes;
@ -480,22 +519,26 @@ int octep_vf_oq_process_rx(struct octep_vf_oq *oq, int budget)
{
u32 pkts_available, pkts_processed, total_pkts_processed;
struct octep_vf_device *oct = oq->octep_vf_dev;
u32 pkts_pending;
pkts_available = 0;
pkts_processed = 0;
total_pkts_processed = 0;
while (total_pkts_processed < budget) {
/* update pending count only when current one exhausted */
if (oq->pkts_pending == 0)
pkts_pending = READ_ONCE(oq->pkts_pending);
if (pkts_pending == 0)
octep_vf_oq_check_hw_for_pkts(oct, oq);
pkts_pending = READ_ONCE(oq->pkts_pending);
pkts_available = min(budget - total_pkts_processed,
oq->pkts_pending);
pkts_pending);
if (!pkts_available)
break;
pkts_processed = __octep_vf_oq_process_rx(oct, oq,
pkts_available);
oq->pkts_pending -= pkts_processed;
pkts_pending = READ_ONCE(oq->pkts_pending);
WRITE_ONCE(oq->pkts_pending, (pkts_pending - pkts_processed));
total_pkts_processed += pkts_processed;
}

View File

@ -60,6 +60,8 @@ void ioam6_fill_trace_data(struct sk_buff *skb,
struct ioam6_trace_hdr *trace,
bool is_input);
u8 ioam6_trace_compute_nodelen(u32 trace_type);
int ioam6_init(void);
void ioam6_exit(void);

View File

@ -1,2 +1,2 @@
sbat,1,SBAT Version,sbat,1,https://github.com/rhboot/shim/blob/main/SBAT.md
kernel.almalinux,1,AlmaLinux,kernel-core,6.12.0-211.43.1.el10.x86_64,mailto:security@almalinux.org
kernel.almalinux,1,AlmaLinux,kernel-core,6.12.0-211.44.1.el10.x86_64,mailto:security@almalinux.org

View File

@ -935,6 +935,11 @@ static bool ipv6_hop_ioam(struct sk_buff *skb, int optoff)
if (hdr->opt_len < 2 + sizeof(*trace) + trace->remlen * 4)
goto drop;
/* Inconsistent Pre-allocated Trace header */
if (trace->nodelen !=
ioam6_trace_compute_nodelen(be32_to_cpu(trace->type_be32)))
goto drop;
/* Ignore if the IOAM namespace is unknown */
ns = ioam6_namespace(dev_net(skb->dev), trace->namespace_id);
if (!ns)

View File

@ -694,6 +694,20 @@ struct ioam6_namespace *ioam6_namespace(struct net *net, __be16 id)
return rhashtable_lookup_fast(&nsdata->namespaces, &id, rht_ns_params);
}
#define IOAM6_MASK_SHORT_FIELDS 0xff1ffc00
#define IOAM6_MASK_WIDE_FIELDS 0x00e00000
u8 ioam6_trace_compute_nodelen(u32 trace_type)
{
u8 nodelen = hweight32(trace_type & IOAM6_MASK_SHORT_FIELDS)
* (sizeof(__be32) / 4);
nodelen += hweight32(trace_type & IOAM6_MASK_WIDE_FIELDS)
* (sizeof(__be64) / 4);
return nodelen;
}
static void __ioam6_fill_trace_data(struct sk_buff *skb,
struct ioam6_namespace *ns,
struct ioam6_trace_hdr *trace,

View File

@ -22,9 +22,6 @@
#include <net/ip6_route.h>
#include <net/addrconf.h>
#define IOAM6_MASK_SHORT_FIELDS 0xff100000
#define IOAM6_MASK_WIDE_FIELDS 0xe00000
struct ioam6_lwt_encap {
struct ipv6_hopopt_hdr eh;
u8 pad[2]; /* 2-octet padding for 4n-alignment */
@ -92,13 +89,8 @@ static bool ioam6_validate_trace_hdr(struct ioam6_trace_hdr *trace)
trace->type.bit21 | trace->type.bit23)
return false;
trace->nodelen = 0;
fields = be32_to_cpu(trace->type_be32);
trace->nodelen += hweight32(fields & IOAM6_MASK_SHORT_FIELDS)
* (sizeof(__be32) / 4);
trace->nodelen += hweight32(fields & IOAM6_MASK_WIDE_FIELDS)
* (sizeof(__be64) / 4);
trace->nodelen = ioam6_trace_compute_nodelen(fields);
return true;
}

View File

@ -1,3 +1,76 @@
* Tue Aug 04 2026 CKI KWF Bot <cki-ci-bot+kwf-gitlab-com@redhat.com> [6.12.0-211.44.1.el10_2]
- ice: remove redundant checks from PTP init (Jakub Ramaseuski) [RHEL-193134]
- ice: implement E825 TX ref clock control and TXC hardware sync status (Jakub Ramaseuski) [RHEL-193134]
- ice: add Tx reference clock index handling to AN restart command (Jakub Ramaseuski) [RHEL-193134]
- ice: implement CPI support for E825C (Jakub Ramaseuski) [RHEL-193134]
- ice: introduce TXC DPLL device and TX ref clock pin framework for E825 (Jakub Ramaseuski) [RHEL-193134]
- ice: fix missing priority callbacks for U.FL DPLL pins (Jakub Ramaseuski) [RHEL-193134]
- ice: restore PTP Rx timestamp config after ethtool set-channels (Jakub Ramaseuski) [RHEL-193134]
- ice: ptp: use primary NAC semaphore on E825 (Jakub Ramaseuski) [RHEL-193134]
- ice: ptp: serialize E825 PHY timer start with PTP lock (Jakub Ramaseuski) [RHEL-193134]
- ice: fix setting promisc mode while adding VID filter (Jakub Ramaseuski) [RHEL-193134]
- ice: fix VF queue configuration with low MTU values (Jakub Ramaseuski) [RHEL-193134]
- ice: fix locking around wait_event_interruptible_locked_irq (Jakub Ramaseuski) [RHEL-193134]
- ice: dpll: Fix compilation warning (Jakub Ramaseuski) [RHEL-193134]
- ice: mention fw_activate action along with devlink reload (Jakub Ramaseuski) [RHEL-193134]
- ice: fix locking in ice_dcb_rebuild() (Jakub Ramaseuski) [RHEL-193134]
- ice: fix setting RSS VSI hash for E830 (Jakub Ramaseuski) [RHEL-193134]
- ice: add dpll peer notification for paired SMA and U.FL pins (Jakub Ramaseuski) [RHEL-193134]
- ice: fix missing dpll notifications for SW pins (Jakub Ramaseuski) [RHEL-193134]
- ice: fix SMA and U.FL pin state changes affecting paired pin (Jakub Ramaseuski) [RHEL-193134]
- ice: fix missing SMA pin initialization in DPLL subsystem (Jakub Ramaseuski) [RHEL-193134]
- ice: fix infinite recursion in ice_cfg_tx_topo via ice_init_dev_hw (Jakub Ramaseuski) [RHEL-193134]
- ice: fix NULL pointer dereference in ice_reset_all_vfs() (Jakub Ramaseuski) [RHEL-193134]
- ice: fix ice_ptp_read_tx_hwtstamp_status_eth56g (Jakub Ramaseuski) [RHEL-193134]
- ice: fix ready bitmap check for non-E822 devices (Jakub Ramaseuski) [RHEL-193134]
- ice: perform PHY soft reset for E825C ports at initialization (Jakub Ramaseuski) [RHEL-193134]
- ice: fix timestamp interrupt configuration for E825C (Jakub Ramaseuski) [RHEL-193134]
- ice: fix potential NULL pointer deref in error path of ice_set_ringparam() (Jakub Ramaseuski) [RHEL-193134]
- ice: fix race condition in TX timestamp ring cleanup (Jakub Ramaseuski) [RHEL-193134]
- ice: fix ICE_AQ_LINK_SPEED_M for 200G (Jakub Ramaseuski) [RHEL-193134]
- ice: fix PHY config on media change with link-down-on-close (Jakub Ramaseuski) [RHEL-193134]
- ice: Fix memory leak in ice_set_ringparam() (Jakub Ramaseuski) [RHEL-193134]
- ice: dpll: fix misplaced header macros (Jakub Ramaseuski) [RHEL-193134]
- ice: dpll: fix rclk pin state get for E810 (Jakub Ramaseuski) [RHEL-193134]
- Revert "ice: dpll: fix rclk pin state get and misplaced header macros" (Jakub Ramaseuski) [RHEL-193134]
- octeon_ep_vf: add NULL check for napi_build_skb() (CKI Backport Bot) [RHEL-186345]
- octeon_ep_vf: introduce octep_vf_oq_next_idx() helper (CKI Backport Bot) [RHEL-186345]
- octeon_ep_vf: avoid compiler and IQ/OQ reordering (CKI Backport Bot) [RHEL-186345]
- octeon_ep_vf: Relocate counter updates before NAPI (CKI Backport Bot) [RHEL-186345]
- octeon_ep_vf: ensure dbell BADDR updation (CKI Backport Bot) [RHEL-186345]
- net: octeon_ep_vf: fix free_irq dev_id mismatch in IRQ rollback (CKI Backport Bot) [RHEL-186345]
- ipv6: ioam: fix heap buffer overflow in __ioam6_fill_trace_data() (CKI Backport Bot) [RHEL-174197] {CVE-2026-43186}
Resolves: RHEL-174197, RHEL-186345, RHEL-193134
* Mon Aug 03 2026 CKI KWF Bot <cki-ci-bot+kwf-gitlab-com@redhat.com> [6.12.0-211.43.1.el10_2]
- redhat/kernel.spec: make module and modules-core provides use variant (Jan Stancek) [RHEL-213965]
- dpll: allow fwnode pins to attempt state change without capability bit (Jakub Ramaseuski) [RHEL-211011]
- dpll: extend pin notifier with notification source ID (Jakub Ramaseuski) [RHEL-211011]
- dpll: balance create/delete notifications in __dpll_pin_(un)register (Jakub Ramaseuski) [RHEL-211011]
- dpll: guard sync-pair removal on full pin unregister (Jakub Ramaseuski) [RHEL-211011]
- dpll: emit per-dpll delete notifications in dpll_pin_on_pin_unregister() (Jakub Ramaseuski) [RHEL-211011]
- dpll: send delete notification before unregister in on-pin rollback (Jakub Ramaseuski) [RHEL-211011]
- dpll: fix stale iteration in dpll_pin_on_pin_unregister() (Jakub Ramaseuski) [RHEL-211011]
- dpll: allow registering FW-identified pin with a different DPLL (Jakub Ramaseuski) [RHEL-211011]
- dpll: add generic DPLL type (Jakub Ramaseuski) [RHEL-211011]
- dpll: zl3073x: make frequency monitor a per-device attribute (Jakub Ramaseuski) [RHEL-211011]
- dpll: zl3073x: use __dpll_device_change_ntf() and remove change_work (Jakub Ramaseuski) [RHEL-211011]
- dpll: export __dpll_device_change_ntf() for use under dpll_lock (Jakub Ramaseuski) [RHEL-211011]
- dpll: change dpll_netdev_pin_handle_size() to assume DPLL_A_PIN_ID will be used (Jakub Ramaseuski) [RHEL-211011]
- dpll: zl3073x: fix memory leak on pin registration failure (Jakub Ramaseuski) [RHEL-211011]
- dpll: zl3073x: Use named initializers for struct i2c_device_id (Jakub Ramaseuski) [RHEL-211011]
- dpll: Prevent duplicate registrations (Jakub Ramaseuski) [RHEL-211011]
- dpll: export __dpll_pin_change_ntf() for use under dpll_lock (Jakub Ramaseuski) [RHEL-211011]
- drm/i915/psr: Use DC_OFF wake reference to block DC6 on vblank enable (Anusha Srivatsa) [RHEL-172832]
- drm/i915/psr: Block DC states on vblank enable when Panel Replay supported (Anusha Srivatsa) [RHEL-172832]
- drm/i915/psr: Don't enable Panel Replay on sink if globally disabled (Anusha Srivatsa) [RHEL-172832]
- drm/i915/alpm: ALPM disable fixes (Anusha Srivatsa) [RHEL-172832]
- procfs: fix possible double mmput() in do_procmap_query() (Rafael Aquini) [RHEL-189665] {CVE-2026-23199}
- procfs: avoid fetching build ID while holding VMA lock (Rafael Aquini) [RHEL-189665] {CVE-2026-23199}
- timers: Fix NULL function pointer race in timer_shutdown_sync() (CKI Backport Bot) [RHEL-189563] {CVE-2025-68214}
- net: wwan: t7xx: Add delay between MD and SAP suspend (CKI Backport Bot) [RHEL-184266]
Resolves: RHEL-172832, RHEL-184266, RHEL-189563, RHEL-189665, RHEL-211011, RHEL-213965
* Wed Jul 29 2026 CKI KWF Bot <cki-ci-bot+kwf-gitlab-com@redhat.com> [6.12.0-211.42.1.el10_2]
- net/sched: cls_api: Handle TC_ACT_CONSUMED in tcf_qevent_handle (CKI Backport Bot) [RHEL-214082] {CVE-2026-64530}
- ksm: use range-walk function to jump over holes in scan_get_next_rmap_item (Rafael Aquini) [RHEL-189554] {CVE-2025-68211}

View File

@ -1550,8 +1550,8 @@ This package provides less commonly used kernel modules for the %{?2:%{2} }kerne
%package %{?1:%{1}-}modules\
Summary: kernel modules to match the %{?2:%{2}-}core kernel\
Provides: %{name}%{?1:-%{1}}-modules-%{_target_cpu} = %{specrpmversion}-%{release}\
Provides: %{name}-modules-%{_target_cpu} = %{specrpmversion}-%{release}%{uname_suffix %{?1}}\
Provides: %{name}-modules = %{specrpmversion}-%{release}%{uname_suffix %{?1}}\
Provides: %{name}%{?1:-%{1}}-modules-%{_target_cpu} = %{specrpmversion}-%{release}%{uname_suffix %{?1}}\
Provides: %{name}%{?1:-%{1}}-modules = %{specrpmversion}-%{release}%{uname_suffix %{?1}}\
Provides: installonlypkg(kernel-module)\
Provides: %{name}%{?1:-%{1}}-modules-uname-r = %{KVERREL}%{uname_suffix %{?1}}\
Requires: %{name}-uname-r = %{KVERREL}%{uname_suffix %{?1}}\
@ -1573,8 +1573,8 @@ This package provides commonly used kernel modules for the %{?2:%{2}-}core kerne
%package %{?1:%{1}-}modules-core\
Summary: Core kernel modules to match the %{?2:%{2}-}core kernel\
Provides: %{name}%{?1:-%{1}}-modules-core-%{_target_cpu} = %{specrpmversion}-%{release}\
Provides: %{name}-modules-core-%{_target_cpu} = %{specrpmversion}-%{release}%{uname_suffix %{?1}}\
Provides: %{name}-modules-core = %{specrpmversion}-%{release}%{uname_suffix %{?1}}\
Provides: %{name}%{?1:-%{1}}-modules-core-%{_target_cpu} = %{specrpmversion}-%{release}%{uname_suffix %{?1}}\
Provides: %{name}%{?1:-%{1}}-modules-core = %{specrpmversion}-%{release}%{uname_suffix %{?1}}\
Provides: installonlypkg(kernel-module)\
Provides: %{name}%{?1:-%{1}}-modules-core-uname-r = %{KVERREL}%{uname_suffix %{?1}}\
Requires: %{name}-uname-r = %{KVERREL}%{uname_suffix %{?1}}\

View File

@ -1,2 +1,2 @@
sbat,1,SBAT Version,sbat,1,https://github.com/rhboot/shim/blob/main/SBAT.md
kernel-uki-virt-addons.almalinux,1,AlmaLinux,kernel-uki-virt-addons,6.12.0-211.43.1.el10.x86_64,mailto:security@almalinux.org
kernel-uki-virt-addons.almalinux,1,AlmaLinux,kernel-uki-virt-addons,6.12.0-211.44.1.el10.x86_64,mailto:security@almalinux.org

View File

@ -1,2 +1,2 @@
sbat,1,SBAT Version,sbat,1,https://github.com/rhboot/shim/blob/main/SBAT.md
kernel-uki-virt.almalinux,1,AlmaLinux,kernel-uki-virt,6.12.0-211.43.1.el10.x86_64,mailto:security@almalinux.org
kernel-uki-virt.almalinux,1,AlmaLinux,kernel-uki-virt,6.12.0-211.44.1.el10.x86_64,mailto:security@almalinux.org