drivers/net/ethernet/intel/ice/ice_txclk.c

Source file repositories/reference/linux-study-clean/drivers/net/ethernet/intel/ice/ice_txclk.c

File Facts

System
Linux kernel
Corpus path
drivers/net/ethernet/intel/ice/ice_txclk.c
Extension
.c
Size
11122 bytes
Lines
355
Domain
Driver Families
Bucket
drivers/net
Inferred role
Driver Families: implementation source
Status
source implementation candidate

Why This File Exists

Repeatable hardware-adapter layer. Deep compatibility for every driver is out of scope; this atlas records patterns, probe lifecycles, bus glue, IRQ/DMA usage, and links back to core abstractions.

Dependency Surface

Detected Declarations

Annotated Snippet

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

Annotation

Implementation Notes