diff --git a/COPYING-6.12.0-211.43.1.el10 b/COPYING-6.12.0-211.44.1.el10 similarity index 100% rename from COPYING-6.12.0-211.43.1.el10 rename to COPYING-6.12.0-211.44.1.el10 diff --git a/Makefile.rhelver b/Makefile.rhelver index 095e4b704f..fc9289e300 100644 --- a/Makefile.rhelver +++ b/Makefile.rhelver @@ -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 diff --git a/drivers/net/ethernet/intel/ice/Makefile b/drivers/net/ethernet/intel/ice/Makefile index 5b2c666496..95fd0c4980 100644 --- a/drivers/net/ethernet/intel/ice/Makefile +++ b/drivers/net/ethernet/intel/ice/Makefile @@ -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 diff --git a/drivers/net/ethernet/intel/ice/devlink/devlink.c b/drivers/net/ethernet/intel/ice/devlink/devlink.c index 862ff1cdd4..839b7bfa19 100644 --- a/drivers/net/ethernet/intel/ice/devlink/devlink.c +++ b/drivers/net/ethernet/intel/ice/devlink/devlink.c @@ -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); diff --git a/drivers/net/ethernet/intel/ice/ice.h b/drivers/net/ethernet/intel/ice/ice.h index eb3a48330c..f72bb1aa40 100644 --- a/drivers/net/ethernet/intel/ice/ice.h +++ b/drivers/net/ethernet/intel/ice/ice.h @@ -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_ */ diff --git a/drivers/net/ethernet/intel/ice/ice_adapter.c b/drivers/net/ethernet/intel/ice/ice_adapter.c index 0a8a48cd4b..ce89a970b6 100644 --- a/drivers/net/ethernet/intel/ice/ice_adapter.c +++ b/drivers/net/ethernet/intel/ice/ice_adapter.c @@ -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); diff --git a/drivers/net/ethernet/intel/ice/ice_adapter.h b/drivers/net/ethernet/intel/ice/ice_adapter.h index e95266c7f2..4f695f32da 100644 --- a/drivers/net/ethernet/intel/ice/ice_adapter.h +++ b/drivers/net/ethernet/intel/ice/ice_adapter.h @@ -5,9 +5,12 @@ #define _ICE_ADAPTER_H_ #include +#include #include #include +#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; diff --git a/drivers/net/ethernet/intel/ice/ice_adminq_cmd.h b/drivers/net/ethernet/intel/ice/ice_adminq_cmd.h index 859e9c66f3..42878abac9 100644 --- a/drivers/net/ethernet/intel/ice/ice_adminq_cmd.h +++ b/drivers/net/ethernet/intel/ice/ice_adminq_cmd.h @@ -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) diff --git a/drivers/net/ethernet/intel/ice/ice_common.c b/drivers/net/ethernet/intel/ice/ice_common.c index 8866902efb..0b0f37a0e7 100644 --- a/drivers/net/ethernet/intel/ice/ice_common.c +++ b/drivers/net/ethernet/intel/ice/ice_common.c @@ -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); } diff --git a/drivers/net/ethernet/intel/ice/ice_common.h b/drivers/net/ethernet/intel/ice/ice_common.h index e700ac0dc3..9f53442121 100644 --- a/drivers/net/ethernet/intel/ice/ice_common.h +++ b/drivers/net/ethernet/intel/ice/ice_common.h @@ -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 diff --git a/drivers/net/ethernet/intel/ice/ice_cpi.c b/drivers/net/ethernet/intel/ice/ice_cpi.c new file mode 100644 index 0000000000..8819fd2cfe --- /dev/null +++ b/drivers/net/ethernet/intel/ice/ice_cpi.c @@ -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); +} diff --git a/drivers/net/ethernet/intel/ice/ice_cpi.h b/drivers/net/ethernet/intel/ice/ice_cpi.h new file mode 100644 index 0000000000..6a45a920f8 --- /dev/null +++ b/drivers/net/ethernet/intel/ice/ice_cpi.h @@ -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_ */ diff --git a/drivers/net/ethernet/intel/ice/ice_dcb_lib.c b/drivers/net/ethernet/intel/ice/ice_dcb_lib.c index 9fc8681cc5..3d0332026c 100644 --- a/drivers/net/ethernet/intel/ice/ice_dcb_lib.c +++ b/drivers/net/ethernet/intel/ice/ice_dcb_lib.c @@ -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; diff --git a/drivers/net/ethernet/intel/ice/ice_dpll.c b/drivers/net/ethernet/intel/ice/ice_dpll.c index 73a4e28ed7..42ad2ca624 100644 --- a/drivers/net/ethernet/intel/ice/ice_dpll.c +++ b/drivers/net/ethernet/intel/ice/ice_dpll.c @@ -4,6 +4,7 @@ #include "ice.h" #include "ice_lib.h" #include "ice_trace.h" +#include "ice_txclk.h" #include #include @@ -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: diff --git a/drivers/net/ethernet/intel/ice/ice_dpll.h b/drivers/net/ethernet/intel/ice/ice_dpll.h index 8678575359..103ba3e490 100644 --- a/drivers/net/ethernet/intel/ice/ice_dpll.h +++ b/drivers/net/ethernet/intel/ice/ice_dpll.h @@ -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) diff --git a/drivers/net/ethernet/intel/ice/ice_ethtool.c b/drivers/net/ethernet/intel/ice/ice_ethtool.c index 928b5855ac..b62acbe1aa 100644 --- a/drivers/net/ethernet/intel/ice/ice_ethtool.c +++ b/drivers/net/ethernet/intel/ice/ice_ethtool.c @@ -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) { diff --git a/drivers/net/ethernet/intel/ice/ice_fw_update.c b/drivers/net/ethernet/intel/ice/ice_fw_update.c index 973a13d3d9..1f35bb9314 100644 --- a/drivers/net/ethernet/intel/ice/ice_fw_update.c +++ b/drivers/net/ethernet/intel/ice/ice_fw_update.c @@ -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: diff --git a/drivers/net/ethernet/intel/ice/ice_lib.c b/drivers/net/ethernet/intel/ice/ice_lib.c index 251bfc9379..9965b1443a 100644 --- a/drivers/net/ethernet/intel/ice/ice_lib.c +++ b/drivers/net/ethernet/intel/ice/ice_lib.c @@ -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 diff --git a/drivers/net/ethernet/intel/ice/ice_main.c b/drivers/net/ethernet/intel/ice/ice_main.c index 2cf3ed9542..2cc67f27f0 100644 --- a/drivers/net/ethernet/intel/ice/ice_main.c +++ b/drivers/net/ethernet/intel/ice/ice_main.c @@ -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) diff --git a/drivers/net/ethernet/intel/ice/ice_ptp.c b/drivers/net/ethernet/intel/ice/ice_ptp.c index 2692ee441c..7adf30f591 100644 --- a/drivers/net/ethernet/intel/ice/ice_ptp.c +++ b/drivers/net/ethernet/intel/ice/ice_ptp.c @@ -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; diff --git a/drivers/net/ethernet/intel/ice/ice_ptp.h b/drivers/net/ethernet/intel/ice/ice_ptp.h index 8c44bd758a..c4b0da7ce2 100644 --- a/drivers/net/ethernet/intel/ice/ice_ptp.h +++ b/drivers/net/ethernet/intel/ice/ice_ptp.h @@ -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; diff --git a/drivers/net/ethernet/intel/ice/ice_ptp_hw.c b/drivers/net/ethernet/intel/ice/ice_ptp_hw.c index 5a5c511ccb..8e5f978359 100644 --- a/drivers/net/ethernet/intel/ice/ice_ptp_hw.c +++ b/drivers/net/ethernet/intel/ice/ice_ptp_hw.c @@ -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(¶ms->atqbal_wq.lock, flags); + spin_lock_irq(¶ms->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(¶ms->atqbal_wq.lock, flags); + spin_unlock_irq(¶ms->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(¶ms->atqbal_wq.lock, flags); + spin_unlock_irq(¶ms->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(¶ms->atqbal_wq.lock, flags); + spin_unlock_irq(¶ms->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 diff --git a/drivers/net/ethernet/intel/ice/ice_ptp_hw.h b/drivers/net/ethernet/intel/ice/ice_ptp_hw.h index 9bfd3e79c5..16b1988e99 100644 --- a/drivers/net/ethernet/intel/ice/ice_ptp_hw.h +++ b/drivers/net/ethernet/intel/ice/ice_ptp_hw.h @@ -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_ */ diff --git a/drivers/net/ethernet/intel/ice/ice_sbq_cmd.h b/drivers/net/ethernet/intel/ice/ice_sbq_cmd.h index 21bb861feb..226243d329 100644 --- a/drivers/net/ethernet/intel/ice/ice_sbq_cmd.h +++ b/drivers/net/ethernet/intel/ice/ice_sbq_cmd.h @@ -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 diff --git a/drivers/net/ethernet/intel/ice/ice_txclk.c b/drivers/net/ethernet/intel/ice/ice_txclk.c new file mode 100644 index 0000000000..48459f971c --- /dev/null +++ b/drivers/net/ethernet/intel/ice/ice_txclk.c @@ -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 per‑PHY 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); +} diff --git a/drivers/net/ethernet/intel/ice/ice_txclk.h b/drivers/net/ethernet/intel/ice/ice_txclk.h new file mode 100644 index 0000000000..21d97afb2a --- /dev/null +++ b/drivers/net/ethernet/intel/ice/ice_txclk.h @@ -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_ */ diff --git a/drivers/net/ethernet/intel/ice/ice_txrx.c b/drivers/net/ethernet/intel/ice/ice_txrx.c index 8f348b222d..da595eb46e 100644 --- a/drivers/net/ethernet/intel/ice/ice_txrx.c +++ b/drivers/net/ethernet/intel/ice/ice_txrx.c @@ -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; diff --git a/drivers/net/ethernet/intel/ice/ice_txrx.h b/drivers/net/ethernet/intel/ice/ice_txrx.h index b6547e1b7c..5e517f2193 100644 --- a/drivers/net/ethernet/intel/ice/ice_txrx.h +++ b/drivers/net/ethernet/intel/ice/ice_txrx.h @@ -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 { diff --git a/drivers/net/ethernet/intel/ice/ice_type.h b/drivers/net/ethernet/intel/ice/ice_type.h index 1e82f4c40b..d9a5c1aae7 100644 --- a/drivers/net/ethernet/intel/ice/ice_type.h +++ b/drivers/net/ethernet/intel/ice/ice_type.h @@ -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; diff --git a/drivers/net/ethernet/intel/ice/ice_vf_lib.c b/drivers/net/ethernet/intel/ice/ice_vf_lib.c index 5161e032fa..ec4129af66 100644 --- a/drivers/net/ethernet/intel/ice/ice_vf_lib.c +++ b/drivers/net/ethernet/intel/ice/ice_vf_lib.c @@ -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); diff --git a/drivers/net/ethernet/intel/ice/virt/queues.c b/drivers/net/ethernet/intel/ice/virt/queues.c index f73d5a3e83..31be2f7618 100644 --- a/drivers/net/ethernet/intel/ice/virt/queues.c +++ b/drivers/net/ethernet/intel/ice/virt/queues.c @@ -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; diff --git a/drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_cn9k.c b/drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_cn9k.c index 88937fce75..4c769b27c2 100644 --- a/drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_cn9k.c +++ b/drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_cn9k.c @@ -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 */ diff --git a/drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_cnxk.c b/drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_cnxk.c index 1f79dfad42..a968b93a67 100644 --- a/drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_cnxk.c +++ b/drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_cnxk.c @@ -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 */ diff --git a/drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_main.c b/drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_main.c index ccb69bc5c9..72c1e9415e 100644 --- a/drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_main.c +++ b/drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_main.c @@ -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); diff --git a/drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_main.h b/drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_main.h index 1a352f41f8..4ee6b4d568 100644 --- a/drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_main.h +++ b/drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_main.h @@ -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); diff --git a/drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_rx.c b/drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_rx.c index d70c8be3cf..d982474082 100644 --- a/drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_rx.c +++ b/drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_rx.c @@ -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; } diff --git a/include/net/ioam6.h b/include/net/ioam6.h index 2cbbee6e80..a75912fe24 100644 --- a/include/net/ioam6.h +++ b/include/net/ioam6.h @@ -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); diff --git a/kernel.sbat b/kernel.sbat index f95cbecbb8..84e764bd54 100644 --- a/kernel.sbat +++ b/kernel.sbat @@ -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 diff --git a/net/ipv6/exthdrs.c b/net/ipv6/exthdrs.c index 5d9293527e..b2e4532f67 100644 --- a/net/ipv6/exthdrs.c +++ b/net/ipv6/exthdrs.c @@ -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) diff --git a/net/ipv6/ioam6.c b/net/ipv6/ioam6.c index bdffb79fb8..bea2cf0163 100644 --- a/net/ipv6/ioam6.c +++ b/net/ipv6/ioam6.c @@ -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, diff --git a/net/ipv6/ioam6_iptunnel.c b/net/ipv6/ioam6_iptunnel.c index 8c3470fd57..a76e81e2a5 100644 --- a/net/ipv6/ioam6_iptunnel.c +++ b/net/ipv6/ioam6_iptunnel.c @@ -22,9 +22,6 @@ #include #include -#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; } diff --git a/redhat/kernel.changelog-10.2 b/redhat/kernel.changelog-10.2 index 2c58256350..ecadb88d92 100644 --- a/redhat/kernel.changelog-10.2 +++ b/redhat/kernel.changelog-10.2 @@ -1,3 +1,76 @@ +* Tue Aug 04 2026 CKI KWF Bot [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 [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 [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} diff --git a/redhat/kernel.spec.template b/redhat/kernel.spec.template index a86ff99518..70971b5d96 100644 --- a/redhat/kernel.spec.template +++ b/redhat/kernel.spec.template @@ -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}}\ diff --git a/uki-addons.sbat b/uki-addons.sbat index 6c55874486..baf46c5e86 100644 --- a/uki-addons.sbat +++ b/uki-addons.sbat @@ -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 diff --git a/uki.sbat b/uki.sbat index 6372a02b20..9b447fd2ae 100644 --- a/uki.sbat +++ b/uki.sbat @@ -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