From nobody Fri Oct 2 08:28:23 2026 Received: from mgamail.intel.com (mgamail.intel.com [192.198.163.11]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 452174052A7; Mon, 3 Aug 2026 11:41:24 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=192.198.163.11 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1785757286; cv=none; b=cjNyrhmNhl2RXtQjPhkQ7mJdzRdmWz7iHQ+nh7+wppDyzmOW4TtsL52T5hXD+Drfrn15T8lP+i/0d/WQUUpbxM7KTbBc3dbYarADMfAPcLA1AgPru0ACVnOX2372q/APKD2DTNKP7/FCQ6fKcUU+VLa9aHOdHqxQrk24DrDOmaA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1785757286; c=relaxed/simple; bh=3VHs14ctbdCHZHJmw79Nv6gyfdb/VhIja/1/P1uZIWk=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=ULHw1p1wNVIcLx7H6bA/IpBTNwZHK24UzGSRw/7c3btl0OtboVjk2oM/2JRLFGheXCu9XjYJeK63+2Jta+iqRHTasdLE2eCtJ+yKdQy9YmDkb4kRippfRMmr4+uaLXfCVHYhqkt81vH14u/R7YXdT/sIg+4vjQ11cuzNE41hMeU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com; spf=pass smtp.mailfrom=intel.com; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b=X/aB07Dg; arc=none smtp.client-ip=192.198.163.11 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=intel.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b="X/aB07Dg" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1785757284; x=1817293284; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=3VHs14ctbdCHZHJmw79Nv6gyfdb/VhIja/1/P1uZIWk=; b=X/aB07DgutWLDGO95mFp+icP8iGgNnZ8xt/dFuvU8A+AfPWbz6LXLSAt q6rxZi+45jvYG9k3QF+/DMD1Cf7pk2gSONmJ3emdwHpOMJspvSqbLm877 WrK9IAl97jJ4JoHeTNbzS9yXirNL1OY3d2akLFsFyFoanGzaU2C6ltSkJ Wo9QOI4SxP3G153hc5+wPc4PXvkFx1ImacZSDtRdS4h2txXjr4UOyO7+L VLpV+bdpzrlkCDSXJhxByI3Jfxu5qPj0+m3nlZ/LBf5YQzvlWm1iGrZPv WmBwNguHmgzAEdKGbd7zpTv740ZdsKKAq0lg5BSwBUbOlMufebqlhFJfL w==; X-CSE-ConnectionGUID: 20Q6dwm7QQ+vAdD9W5zdPQ== X-CSE-MsgGUID: 2jcogvUVQLWbw2T3+Zj01w== X-IronPort-AV: E=McAfee;i="6800,10657,11863"; a="96878871" X-IronPort-AV: E=Sophos;i="6.25,202,1779174000"; d="scan'208";a="96878871" Received: from fmviesa010.fm.intel.com ([10.60.135.150]) by fmvoesa105.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 03 Aug 2026 04:41:17 -0700 X-CSE-ConnectionGUID: PrALrjZ4QoaPpGJwgiarJg== X-CSE-MsgGUID: kWWsoezJQT2oEA1SXM8Vjw== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.25,202,1779174000"; d="scan'208";a="257299045" Received: from gklab-003-001.igk.intel.com ([10.91.173.48]) by fmviesa010.fm.intel.com with ESMTP; 03 Aug 2026 04:40:10 -0700 From: Grzegorz Nitka To: intel-wired-lan@lists.osuosl.org Cc: netdev@vger.kernel.org, linux-kernel@vger.kernel.org, richardcochran@gmail.com, andrew+netdev@lunn.ch, przemyslaw.kitszel@intel.com, anthony.l.nguyen@intel.com, arkadiusz.kubalewski@intel.com, pabeni@redhat.com, kuba@kernel.org, davem@davemloft.net, edumazet@google.com, Grzegorz Nitka , Przemyslaw Korba Subject: [PATCH v3 iwl-next 1/3] ice: monitor TSPLL lock from PTP periodic worker Date: Mon, 3 Aug 2026 13:35:05 +0200 Message-Id: <20260803113507.1858083-2-grzegorz.nitka@intel.com> X-Mailer: git-send-email 2.39.3 In-Reply-To: <20260803113507.1858083-1-grzegorz.nitka@intel.com> References: <20260803113507.1858083-1-grzegorz.nitka@intel.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" On E825 devices that own the source timer, the TSPLL can lose lock when the TCXO or TIME_REF signal is disrupted. Recovery requires re-enabling the TSPLL via CGU register writes; without it, the PHC keeps running on a degraded reference indefinitely. The DPLL periodic worker (ice_dpll_periodic_work()) would be a natural home for this monitoring, but placing it there has two problems: 1. ice_dpll_init_e825() sets ICE_FLAG_DPLL only after all initialization steps succeed. If any earlier step fails, the driver would run without any TSPLL recovery mechanism. 2. When CONFIG_DPLL=3Dn, the DPLL worker is compiled out and TSPLL recovery would be silently absent. Add the monitor to ice_ptp_periodic_work() instead, which always runs on E825 owner PFs regardless of DPLL init state or config. Introduce two small helpers, ice_tspll_lost_lock_e825c() and ice_tspll_restart_e825c(), which encapsulate the CGU register reads/writes required to observe and recover the TSPLL. Cache the observed lock state in pf->ptp.tspll_locked using WRITE_ONCE()/READ_ONCE() so a follow-up change can consume it from the DPLL periodic worker (for user-space notification via dpll_device_change_ntf()) and drop the redundant poll+recovery from that path. Precise synchronization is not required: both workers converge on the same value within one poll period. Reviewed-by: Przemyslaw Korba Signed-off-by: Grzegorz Nitka Tested-by: Alexander Nowlin --- drivers/net/ethernet/intel/ice/ice_ptp.c | 70 +++++++++++++++++ drivers/net/ethernet/intel/ice/ice_ptp.h | 11 +++ drivers/net/ethernet/intel/ice/ice_tspll.c | 88 +++++++++++++++++++++- drivers/net/ethernet/intel/ice/ice_tspll.h | 4 + 4 files changed, 172 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/intel/ice/ice_ptp.c b/drivers/net/etherne= t/intel/ice/ice_ptp.c index 4df3d83b5066..a997be5f7d8f 100644 --- a/drivers/net/ethernet/intel/ice/ice_ptp.c +++ b/drivers/net/ethernet/intel/ice/ice_ptp.c @@ -5,8 +5,11 @@ #include "ice.h" #include "ice_lib.h" #include "ice_trace.h" +#include "ice_tspll.h" #include "ice_txclk.h" =20 +#define ICE_TSPLL_LOG_INTERVAL 120 + static const char ice_pin_names[][64] =3D { "SDP0", "SDP1", @@ -2875,6 +2878,67 @@ static void ice_ptp_maybe_trigger_tx_interrupt(struc= t ice_pf *pf) } } =20 +/** + * ice_ptp_tspll_monitor - poll and recover TSPLL lock on E825 owner PFs + * @pf: Board private structure + * + * Called from the PTP periodic worker. On E825 devices that own the source + * timer, poll the TSPLL lock status via CGU registers and trigger a resta= rt + * if the lock has been lost. The result is cached in @pf->ptp.tspll_locked + * so it can be consumed by the DPLL periodic worker via READ_ONCE(). + * + * TSPLL lock is critical for PHC operation and must be monitored regardle= ss + * of whether DPLL init succeeded or CONFIG_DPLL is enabled. Placing the + * monitor here makes recovery independent of the dpll subsystem. + * + * AQ read errors are rate-limited and do not stop monitoring. Lock-lost + * events are logged every 120 retries (~60 s at normal poll rate) to + * surface persistent failures without flooding the log. + */ +static void ice_ptp_tspll_monitor(struct ice_pf *pf) +{ + bool lock_lost; + int err; + + if (pf->hw.mac_type !=3D ICE_MAC_GENERIC_3K_E825 || + !ice_pf_src_tmr_owned(pf)) + return; + + err =3D ice_tspll_lost_lock_e825c(&pf->hw, &lock_lost); + if (err) { + dev_err_ratelimited(ice_pf_to_dev(pf), + "Failed reading TimeSync PLL lock status (err: %d). Retrying.\n", + err); + return; + } + + if (lock_lost) { + WRITE_ONCE(pf->ptp.tspll_locked, false); + if (!(pf->ptp.tspll_lock_retries % ICE_TSPLL_LOG_INTERVAL)) + dev_warn(ice_pf_to_dev(pf), + "TimeSync PLL lock lost. Retrying to acquire lock.\n"); + err =3D ice_tspll_restart_e825c(&pf->hw); + if (err) + dev_err_ratelimited(ice_pf_to_dev(pf), + "Failed to restart TimeSync PLL (err: %d).\n", + err); + pf->ptp.tspll_lock_retries++; + } else { + if (pf->ptp.tspll_lock_retries) { + enum ice_clk_src clk_src; + const char *src_str =3D "unknown"; + + if (!ice_tspll_get_clk_src(&pf->hw, &clk_src)) + src_str =3D ice_tspll_clk_src_str(clk_src); + dev_info(ice_pf_to_dev(pf), + "TimeSync PLL lock acquired with %s clock source after %u retries.\n", + src_str, pf->ptp.tspll_lock_retries); + } + WRITE_ONCE(pf->ptp.tspll_locked, true); + pf->ptp.tspll_lock_retries =3D 0; + } +} + static void ice_ptp_periodic_work(struct kthread_work *work) { struct ice_ptp *ptp =3D container_of(work, struct ice_ptp, work.work); @@ -2884,6 +2948,8 @@ static void ice_ptp_periodic_work(struct kthread_work= *work) if (pf->ptp.state !=3D ICE_PTP_READY) return; =20 + ice_ptp_tspll_monitor(pf); + err =3D ice_ptp_update_cached_phctime(pf); =20 ice_ptp_maybe_trigger_tx_interrupt(pf); @@ -3001,6 +3067,9 @@ static int ice_ptp_rebuild_owner(struct ice_pf *pf) err =3D ice_tspll_init(hw); if (err) return err; + /* Rebuild reinitialized TSPLL, so reset monitor retry state. */ + WRITE_ONCE(ptp->tspll_locked, true); + ptp->tspll_lock_retries =3D 0; =20 /* Acquire the global hardware lock */ if (!ice_ptp_lock(hw)) { @@ -3364,6 +3433,7 @@ void ice_ptp_init(struct ice_pf *pf) } ptp->port.port_num =3D hw->lane_num; =20 + ptp->tspll_locked =3D true; ice_ptp_init_hw(hw); =20 ice_ptp_init_tx_interrupt_mode(pf); diff --git a/drivers/net/ethernet/intel/ice/ice_ptp.h b/drivers/net/etherne= t/intel/ice/ice_ptp.h index c4b0da7ce20e..0e40fef3b4e8 100644 --- a/drivers/net/ethernet/intel/ice/ice_ptp.h +++ b/drivers/net/ethernet/intel/ice/ice_ptp.h @@ -249,6 +249,15 @@ struct ice_ptp_pin_desc { * @tx_hwtstamp_discarded: number of Tx skbs discarded due to cached PHC t= ime * being too old to correctly extend timestamp * @late_cached_phc_updates: number of times cached PHC update is late + * @tspll_locked: last observed TSPLL lock state on E825 owner PFs. + * Written by the PTP periodic worker after polling the TSPLL and + * intended to be read (without pf->dplls.lock) by the DPLL periodic + * worker in a follow-up change. Access via READ_ONCE()/WRITE_ONCE(); + * precise synchronization is not required because both workers + * converge on the same value within one poll period. + * @tspll_lock_retries: counts consecutive poll cycles in which the TSPLL + * was found unlocked. Reset to zero when lock is re-acquired. Used to + * rate-limit the lock-lost log message (~every 120 retries / ~60 s). */ struct ice_ptp { enum ice_ptp_state state; @@ -274,6 +283,8 @@ struct ice_ptp { u32 tx_hwtstamp_flushed; u32 tx_hwtstamp_discarded; u32 late_cached_phc_updates; + bool tspll_locked; + u32 tspll_lock_retries; }; =20 #define __ptp_port_to_ptp(p) \ diff --git a/drivers/net/ethernet/intel/ice/ice_tspll.c b/drivers/net/ether= net/intel/ice/ice_tspll.c index fd4b58eb9bc0..be8e2da21dd6 100644 --- a/drivers/net/ethernet/intel/ice/ice_tspll.c +++ b/drivers/net/ethernet/intel/ice/ice_tspll.c @@ -129,7 +129,7 @@ static bool ice_tspll_check_params(struct ice_hw *hw, * * Return: specified clock source converted to its string name */ -static const char *ice_tspll_clk_src_str(enum ice_clk_src clk_src) +const char *ice_tspll_clk_src_str(enum ice_clk_src clk_src) { switch (clk_src) { case ICE_CLK_SRC_TCXO: @@ -531,6 +531,64 @@ int ice_tspll_cfg_pps_out_e825c(struct ice_hw *hw, boo= l enable) return ice_write_cgu_reg(hw, ICE_CGU_R9, val); } =20 +/** + * ice_tspll_lost_lock_e825c - check if TSPLL lost lock + * @hw: Pointer to the HW struct + * @lost_lock: Output flag for reporting lost lock + * + * Get E825 device TSPLL DPLL lock status. + * + * Return: + * * 0 - OK + * * negative - error + */ +int ice_tspll_lost_lock_e825c(struct ice_hw *hw, bool *lost_lock) +{ + u32 val; + int err; + + err =3D ice_read_cgu_reg(hw, ICE_CGU_RO_LOCK, &val); + if (err) + return err; + + *lost_lock =3D !FIELD_GET(ICE_CGU_RO_LOCK_TRUE_LOCK, val); + + return 0; +} + +/** + * ice_tspll_restart_e825c - trigger TSPLL restart + * @hw: Pointer to the HW struct + * + * Re-enable TSPLL for E825 device. + * + * Return: + * * 0 - OK + * * negative - error + */ +int ice_tspll_restart_e825c(struct ice_hw *hw) +{ + u32 val; + int err; + + /* Read the initial values of r23 and disable the PLL */ + err =3D ice_read_cgu_reg(hw, ICE_CGU_R23, &val); + if (err) + return err; + + val &=3D ~ICE_CGU_R23_R24_TSPLL_ENABLE; + err =3D ice_write_cgu_reg(hw, ICE_CGU_R23, val); + if (err) + return err; + + /* Wait at least 1 ms before reenabling PLL */ + usleep_range(USEC_PER_MSEC, 2 * USEC_PER_MSEC); + val |=3D ICE_CGU_R23_R24_TSPLL_ENABLE; + err =3D ice_write_cgu_reg(hw, ICE_CGU_R23, val); + + return err; +} + /** * ice_tspll_cfg - Configure the Clock Generation Unit TSPLL * @hw: Pointer to the HW struct @@ -577,6 +635,34 @@ static int ice_tspll_dis_sticky_bits(struct ice_hw *hw) } } =20 +/** + * ice_tspll_get_clk_src - get current TSPLL clock source + * @hw: board private hw structure + * @clk_src: pointer to store clk_src value + * + * Get current TSPLL clock source settings. + * + * Return: + * * 0 - OK + * * negative - error + */ +int ice_tspll_get_clk_src(struct ice_hw *hw, enum ice_clk_src *clk_src) +{ + u32 val; + int err; + + err =3D (hw->mac_type =3D=3D ICE_MAC_GENERIC_3K_E825) ? + ice_read_cgu_reg(hw, ICE_CGU_R23, &val) : + ice_read_cgu_reg(hw, ICE_CGU_R24, &val); + if (err) + return err; + + *clk_src =3D (enum ice_clk_src)FIELD_GET(ICE_CGU_R23_R24_TIME_REF_SEL, + val); + + return 0; +} + /** * ice_tspll_init - Initialize TSPLL with settings from firmware * @hw: Pointer to the HW structure diff --git a/drivers/net/ethernet/intel/ice/ice_tspll.h b/drivers/net/ether= net/intel/ice/ice_tspll.h index d650867004d1..05917ae51ded 100644 --- a/drivers/net/ethernet/intel/ice/ice_tspll.h +++ b/drivers/net/ethernet/intel/ice/ice_tspll.h @@ -32,6 +32,9 @@ struct ice_tspll_params_e82x { #define ICE_TSPLL_FBDIV_INTGR_E825 256 =20 int ice_tspll_cfg_pps_out_e825c(struct ice_hw *hw, bool enable); +int ice_tspll_lost_lock_e825c(struct ice_hw *hw, bool *lost_lock); +int ice_tspll_restart_e825c(struct ice_hw *hw); +int ice_tspll_get_clk_src(struct ice_hw *hw, enum ice_clk_src *clk_src); int ice_tspll_init(struct ice_hw *hw); int ice_tspll_bypass_mux_active_e825c(struct ice_hw *hw, u8 port, bool *ac= tive, enum ice_synce_clk output); @@ -39,4 +42,5 @@ int ice_tspll_cfg_bypass_mux_e825c(struct ice_hw *hw, boo= l ena, u32 port_num, enum ice_synce_clk output); int ice_tspll_cfg_synce_ethdiv_e825c(struct ice_hw *hw, enum ice_synce_clk output); +const char *ice_tspll_clk_src_str(enum ice_clk_src clk_src); #endif /* _ICE_TSPLL_H_ */ --=20 2.39.3 From nobody Fri Oct 2 08:28:23 2026 Received: from mgamail.intel.com (mgamail.intel.com [192.198.163.11]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id EFABA403AF8; Mon, 3 Aug 2026 11:41:20 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=192.198.163.11 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1785757285; cv=none; b=urEkzTkwv2581KtEyBRbAAqkvFsh6Y/iESff2M+EyvkUdX3Oy66COrgAlylJ/mXabRMOgGNUiDXzQ47pypqMyyCx3L7r1E8/08BYtv8zXmw4FpEBAepnn7wJQyYhZLC5+0Q8krBFbfFyDyUXA0e73kdypiciu7fessZSCtPQlpc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1785757285; c=relaxed/simple; bh=MSgV+SxPrEaSIXXDzO+Boj8AaTIZ82O/nWkt5aNElg0=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=kTP8SNZje30OsAFBwokLhTVmcL9LIjE86ZqQGFBYWkD0YIvmmb89ciXJ5HUVoy/R7PwxLFYuTSuE0s/sYoyPtkZZdjI4pf+HDZ5yTAo5rkXU7XB1tJxBN0uqUS2r7iZCg5aNLe7WM1oC5PGoEfgjtLoL+nWLw7cuFZvCyFBJqhE= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com; spf=pass smtp.mailfrom=intel.com; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b=LiiQhpfs; arc=none smtp.client-ip=192.198.163.11 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=intel.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b="LiiQhpfs" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1785757281; x=1817293281; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=MSgV+SxPrEaSIXXDzO+Boj8AaTIZ82O/nWkt5aNElg0=; b=LiiQhpfsl0NISrHrj4MkTlzYE28Q4s8AxDb94Fgz6aEwVvh+1CcaTUMP lR0V618vfT1juYmawa2aZklIHlIhDBN6b34fl9HOFzZOHKPFFCXh2zBCV dOzXhSJ3PJiaD5SnJj9XVRGP5wFdv9sJboZi91I5mI9DnbrpIZ1aQpxk1 EZJiPZRbwFx3OrzZX/pqHIxAX0xvjThcovhynN/gM8eXYlXZQqHxuVgDs Q7xLco8QxMYw1B0V4uWSdC8ZRtedSrN3TGjXC/ADEprMj0kUussBc/gBz VcAfkllrAuOU2g34RIXi8w10D0GNVFqmF6683DmoNhmuMko53yjT5BIUR g==; X-CSE-ConnectionGUID: AIwlEMSkTJuH2x45kDd1pQ== X-CSE-MsgGUID: DPbtG9IbRDaHv3zyxmCNMA== X-IronPort-AV: E=McAfee;i="6800,10657,11863"; a="96878845" X-IronPort-AV: E=Sophos;i="6.25,202,1779174000"; d="scan'208";a="96878845" Received: from fmviesa010.fm.intel.com ([10.60.135.150]) by fmvoesa105.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 03 Aug 2026 04:41:17 -0700 X-CSE-ConnectionGUID: FU8qsVn4QBqhsZemPIFx/w== X-CSE-MsgGUID: XZc6ZUUKQFehuALUptFPFw== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.25,202,1779174000"; d="scan'208";a="257299046" Received: from gklab-003-001.igk.intel.com ([10.91.173.48]) by fmviesa010.fm.intel.com with ESMTP; 03 Aug 2026 04:40:14 -0700 From: Grzegorz Nitka To: intel-wired-lan@lists.osuosl.org Cc: netdev@vger.kernel.org, linux-kernel@vger.kernel.org, richardcochran@gmail.com, andrew+netdev@lunn.ch, przemyslaw.kitszel@intel.com, anthony.l.nguyen@intel.com, arkadiusz.kubalewski@intel.com, pabeni@redhat.com, kuba@kernel.org, davem@davemloft.net, edumazet@google.com, Grzegorz Nitka , Przemyslaw Korba Subject: [PATCH v3 iwl-next 2/3] ice: add TSPLL DPLL device and TIME_REF pin for E825 Date: Mon, 3 Aug 2026 13:35:06 +0200 Message-Id: <20260803113507.1858083-3-grzegorz.nitka@intel.com> X-Mailer: git-send-email 2.39.3 In-Reply-To: <20260803113507.1858083-1-grzegorz.nitka@intel.com> References: <20260803113507.1858083-1-grzegorz.nitka@intel.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" This extends the E825 advanced sync-timing support introduced by the tx-clk series, which added the TXC DPLL device for TX reference clock control. The TSPLL, the source timer PLL, is now also exposed through the dpll subsystem so that its lock status and clock source selection are visible and controllable from userspace. On E825 devices the TSPLL is the source timer PLL, distinct from the EEC and PPS DPLLs used on E810. Register it as a DPLL_TYPE_GENERIC device for owner PFs. Add struct ice_dplls::tspll_in, a fwnode-backed input pin named "time_ref". The state_on_dpll_get callback queries ICE_CGU_R23 via ice_tspll_get_clk_src() and returns CONNECTED when TIME_REF is selected as clock source, DISCONNECTED otherwise. The state_on_dpll_set callback switches the source between TIME_REF and TCXO via the new ice_tspll_set_cfg() helper. Registration is deferred via the dpll notifier path if the pin is not yet visible in the subsystem at probe time. Initialize TSPLL DPLL state from direct clock-source/lock reads so the first published state reflects hardware and prev_dpll_state matches. During periodic polling, the DPLL worker consumes READ_ONCE(pf->ptp.tspll_locked), maintained and recovered by the PTP periodic worker. When the TSPLL clock source is TCXO (TIME_REF pin not selected), UNLOCKED is reported unconditionally to reflect the free-running state of the oscillator regardless of the raw lock bit. To avoid stale lock-status reads after synchronous source changes, the set callback now refreshes tspll.dpll_state immediately and emits a DPLL change notification when the cached state changed. If a TSPLL reconfiguration is applied but the PLL has not yet re-acquired lock, treat the internal -EAGAIN result as success so the PTP periodic worker can complete recovery, while real -EBUSY failures from reset/SBQ paths still propagate to userspace. The TSPLL userspace reconfiguration path (state_on_dpll_set) and the PTP periodic worker (ice_ptp_tspll_monitor) both read TSPLL HW state and write the pf->ptp.tspll_locked cache consumed by the DPLL worker. Serialize both with pf->dplls.lock across their HW-read -> cache-write sequence; otherwise a preempted monitor could overwrite an accurate cache update from the DPLL callback with stale HW state. The mutex lifetime is lifted to PF-features scope (initialized in ice_init_features() before ice_ptp_init(), destroyed in ice_deinit_features() after ice_ptp_release()) so the PTP monitor can take it unconditionally regardless of DPLL init state. Extend ice_dpll_deinit_txclk_pins() with a "flush" parameter so the E825 init error path for the TSPLL fwnode pin can tear down TXCLK pins without flushing pf->dplls.wq. If the flush ran here, notifier work items queued during earlier init steps would be blocked on pf->dplls.dpll_init, which is only completed at the unregister_pins label reached after this teardown. destroy_workqueue() at that label drains the queued items safely. Existing full-teardown callers pass flush=3Dtrue and keep current behavior. Reviewed-by: Przemyslaw Korba Signed-off-by: Grzegorz Nitka Tested-by: Alexander Nowlin --- drivers/net/ethernet/intel/ice/ice_dpll.c | 420 ++++++++++++++++++--- drivers/net/ethernet/intel/ice/ice_dpll.h | 4 + drivers/net/ethernet/intel/ice/ice_main.c | 9 + drivers/net/ethernet/intel/ice/ice_ptp.c | 12 + drivers/net/ethernet/intel/ice/ice_tspll.c | 34 +- drivers/net/ethernet/intel/ice/ice_tspll.h | 2 + 6 files changed, 426 insertions(+), 55 deletions(-) diff --git a/drivers/net/ethernet/intel/ice/ice_dpll.c b/drivers/net/ethern= et/intel/ice/ice_dpll.c index 1ca137f67dd4..60d24b796c10 100644 --- a/drivers/net/ethernet/intel/ice/ice_dpll.c +++ b/drivers/net/ethernet/intel/ice/ice_dpll.c @@ -24,6 +24,8 @@ =20 #define E825_RCLK_PARENT_0_PIN_IDX 0 #define E825_RCLK_PARENT_1_PIN_IDX 1 +#define E825_DPLL_TSPLL_BASE_IDX 0 +#define E825_DPLL_TXCLK_BASE_IDX 1 =20 #define ICE_DPLL_PIN_SW_INPUT_ABS(in_idx) \ (ICE_DPLL_SW_PIN_INPUT_BASE_SFP + (in_idx)) @@ -86,6 +88,7 @@ static const char * const ice_dpll_sw_pin_sma[] =3D { "SM= A1", "SMA2" }; static const char * const ice_dpll_sw_pin_ufl[] =3D { "U.FL1", "U.FL2" }; static const char * const ice_dpll_ext_eref_pin =3D "EXT_EREF0"; static const char * const ice_dpll_fwnode_ext_synce =3D "clk_ref_synce"; +static const char * const ice_dpll_fwnode_time_ref =3D "time_ref"; =20 static const struct dpll_pin_frequency ice_esync_range[] =3D { DPLL_PIN_FREQUENCY_RANGE(0, DPLL_PIN_FREQUENCY_1_HZ), @@ -2827,6 +2830,130 @@ static const struct dpll_pin_ops ice_dpll_txclk_ops= =3D { .direction_get =3D ice_dpll_input_direction, }; =20 +static enum dpll_lock_status ice_dpll_tspll_lock_status_get(struct ice_pf = *pf, + bool use_cached); + +/** + * ice_dpll_tspll_state_on_dpll_get - get TIME_REF pin state on TSPLL DPLL + * @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 dpll + * @extack: error reporting + * + * Dpll subsystem callback. Returns CONNECTED if the TSPLL is using + * TIME_REF as its clock source, DISCONNECTED otherwise. + * + * Return: + * * 0 - success + * * negative - failed to read clock source + */ +static int +ice_dpll_tspll_state_on_dpll_get(const struct dpll_pin *pin, void *pin_pri= v, + const struct dpll_device *dpll, + void *dpll_priv, + enum dpll_pin_state *state, + struct netlink_ext_ack *extack) +{ + struct ice_dpll_pin *p =3D pin_priv; + enum ice_clk_src clk_src; + int err; + + if (ice_dpll_is_reset(p->pf, extack)) + return -EBUSY; + + err =3D ice_tspll_get_clk_src(&p->pf->hw, &clk_src); + if (err) + return err; + + *state =3D clk_src =3D=3D ICE_CLK_SRC_TIME_REF ? DPLL_PIN_STATE_CONNECTED= : + DPLL_PIN_STATE_DISCONNECTED; + return 0; +} + +/** + * ice_dpll_tspll_state_on_dpll_set - set TIME_REF pin state on TSPLL DPLL + * @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: requested state of the pin + * @extack: error reporting + * + * Dpll subsystem callback. Enables (CONNECTED) or disables (DISCONNECTED) + * the TIME_REF signal as the TSPLL clock source. Selects TCXO when disabl= ed. + * + * Return: + * * 0 - success + * * negative - error + */ +static int +ice_dpll_tspll_state_on_dpll_set(const struct dpll_pin *pin, void *pin_pri= v, + const struct dpll_device *dpll, + void *dpll_priv, enum dpll_pin_state state, + struct netlink_ext_ack *extack) +{ + struct ice_dpll_pin *p =3D pin_priv; + struct ice_pf *pf =3D p->pf; + enum ice_clk_src clk_src; + bool changed =3D false; + struct ice_dpll *tp; + int ret; + + if (ice_dpll_is_reset(pf, extack)) + return -EBUSY; + + if (state !=3D DPLL_PIN_STATE_CONNECTED && + state !=3D DPLL_PIN_STATE_DISCONNECTED) { + NL_SET_ERR_MSG(extack, + "unsupported pin state for TSPLL reference clock"); + return -EINVAL; + } + + clk_src =3D (state =3D=3D DPLL_PIN_STATE_CONNECTED) ? ICE_CLK_SRC_TIME_RE= F : + ICE_CLK_SRC_TCXO; + tp =3D &pf->dplls.tspll; + /* Serialize multi-register TSPLL reconfiguration with restart path. */ + mutex_lock(&pf->dplls.lock); + ret =3D ice_tspll_set_cfg(&pf->hw, ICE_TSPLL_FREQ_156_250, clk_src); + if (!ret || ret =3D=3D -EAGAIN) { + enum dpll_lock_status new_state; + + new_state =3D ice_dpll_tspll_lock_status_get(pf, false); + + /* Sync the cached lock state to what we just observed from HW + * so ice_dpll_periodic_work_e825() (cached path) does not emit + * a spurious UNLOCKED notification between now and the next + * PTP monitor tick. + */ + WRITE_ONCE(pf->ptp.tspll_locked, + new_state =3D=3D DPLL_LOCK_STATUS_LOCKED); + + if (tp->prev_dpll_state !=3D new_state) { + tp->dpll_state =3D new_state; + tp->prev_dpll_state =3D new_state; + changed =3D true; + } + } + mutex_unlock(&pf->dplls.lock); + if (changed) + __dpll_device_change_ntf(tp->dpll); + /* TSPLL reconfiguration may complete before lock is reacquired. + * PTP periodic monitoring tracks and restores the final lock state. + */ + if (ret =3D=3D -EAGAIN) + ret =3D 0; + + return ret; +} + +static const struct dpll_pin_ops ice_dpll_tspll_in_ops =3D { + .state_on_dpll_set =3D ice_dpll_tspll_state_on_dpll_set, + .state_on_dpll_get =3D ice_dpll_tspll_state_on_dpll_get, + .direction_get =3D ice_dpll_input_direction, +}; + static const struct dpll_pin_ops ice_dpll_pin_sma_ops =3D { .state_on_dpll_set =3D ice_dpll_sma_pin_state_set, .state_on_dpll_get =3D ice_dpll_sw_pin_state_get, @@ -2928,6 +3055,37 @@ static u64 ice_generate_clock_id(struct ice_pf *pf) return pci_get_dsn(pf->pdev); } =20 +/** + * ice_dpll_tspll_lock_status_get - derive TSPLL state for dpll subsystem + * @pf: board private structure + * @use_cached: if true, read lock state from pf->ptp.tspll_locked (mainta= ined + * by the PTP periodic worker); if false, query hardware dire= ctly + * + * If TIME_REF is not selected, TSPLL is treated as unlocked from the dpll + * subsystem perspective, regardless of raw lock bit. + */ +static enum dpll_lock_status +ice_dpll_tspll_lock_status_get(struct ice_pf *pf, bool use_cached) +{ + enum ice_clk_src clk_src; + bool locked; + + if (ice_tspll_get_clk_src(&pf->hw, &clk_src) || + clk_src !=3D ICE_CLK_SRC_TIME_REF) + return DPLL_LOCK_STATUS_UNLOCKED; + + if (use_cached) { + locked =3D READ_ONCE(pf->ptp.tspll_locked); + } else { + bool lock_lost; + + if (ice_tspll_lost_lock_e825c(&pf->hw, &lock_lost)) + return DPLL_LOCK_STATUS_UNLOCKED; + locked =3D !lock_lost; + } + return locked ? DPLL_LOCK_STATUS_LOCKED : DPLL_LOCK_STATUS_UNLOCKED; +} + /** * ice_dpll_pin_ntf - notify pin change including any SW pin wrappers * @dplls: pointer to dplls struct @@ -3160,12 +3318,44 @@ ice_dpll_update_state(struct ice_pf *pf, struct ice= _dpll *d, bool init) return ret; } =20 +/** + * ice_dpll_periodic_work_e825 - TSPLL DPLL periodic update for E825 + * @pf: board private structure + * + * Publish TSPLL lock status to the dpll subsystem. The PTP periodic worker + * owns TSPLL lock polling and recovery; this function consumes the cached + * result for dpll notifications. + * + * Context: Must be called without pf->dplls.lock held. + */ +static void ice_dpll_periodic_work_e825(struct ice_pf *pf) +{ + struct ice_dpll *tp =3D &pf->dplls.tspll; + enum dpll_lock_status new_state; + bool changed =3D false; + + mutex_lock(&pf->dplls.lock); + new_state =3D ice_dpll_tspll_lock_status_get(pf, true); + + if (tp->prev_dpll_state !=3D new_state) { + tp->dpll_state =3D new_state; + tp->prev_dpll_state =3D new_state; + changed =3D true; + } + mutex_unlock(&pf->dplls.lock); + if (changed) + dpll_device_change_ntf(tp->dpll); +} + /** * ice_dpll_periodic_work - DPLLs periodic worker * @work: pointer to kthread_work structure * - * DPLLs periodic worker is responsible for polling state of dpll. - * Context: Holds pf->dplls.lock + * Periodic worker responsible for polling DPLL state. On E810 devices it + * polls the EEC and PPS DPLLs. On E825 devices, when this PF owns the + * source timer, it publishes TSPLL lock status to the dpll subsystem. + * + * Context: Acquires and releases pf->dplls.lock */ static void ice_dpll_periodic_work(struct kthread_work *work) { @@ -3173,38 +3363,43 @@ static void ice_dpll_periodic_work(struct kthread_w= ork *work) struct ice_pf *pf =3D container_of(d, struct ice_pf, dplls); struct ice_dpll *de =3D &pf->dplls.eec; struct ice_dpll *dp =3D &pf->dplls.pps; - u32 phase_offset_ntf =3D 0; + u32 ntf_mask =3D 0; int ret =3D 0; =20 if (ice_is_reset_in_progress(pf->state)) goto resched; - mutex_lock(&pf->dplls.lock); - d->periodic_counter++; - ret =3D ice_dpll_update_state(pf, de, false); - if (!ret) - ret =3D ice_dpll_update_state(pf, dp, false); - if (!ret && dp->phase_offset_monitor_period && - d->periodic_counter % dp->phase_offset_monitor_period =3D=3D 0) - ret =3D ice_dpll_pps_update_phase_offsets(pf, &phase_offset_ntf); - if (ret) { - /* EBUSY is expected during reset recovery */ - if (pf->hw.adminq.sq_last_status !=3D LIBIE_AQ_RC_EBUSY) - d->cgu_state_acq_err_num++; - /* stop rescheduling this worker */ - if (d->cgu_state_acq_err_num > - ICE_CGU_STATE_ACQ_ERR_THRESHOLD) { - dev_err(ice_pf_to_dev(pf), - "EEC/PPS DPLLs periodic work disabled\n"); - mutex_unlock(&pf->dplls.lock); - return; + + if (pf->hw.mac_type =3D=3D ICE_MAC_GENERIC_3K_E825) { + ice_dpll_periodic_work_e825(pf); + } else { + mutex_lock(&pf->dplls.lock); + d->periodic_counter++; + ret =3D ice_dpll_update_state(pf, de, false); + if (!ret) + ret =3D ice_dpll_update_state(pf, dp, false); + if (!ret && dp->phase_offset_monitor_period && + d->periodic_counter % dp->phase_offset_monitor_period =3D=3D 0) + ret =3D ice_dpll_pps_update_phase_offsets(pf, &ntf_mask); + if (ret) { + /* EBUSY is expected during reset recovery */ + if (pf->hw.adminq.sq_last_status !=3D LIBIE_AQ_RC_EBUSY) + d->cgu_state_acq_err_num++; + /* stop rescheduling this worker */ + if (d->cgu_state_acq_err_num > + ICE_CGU_STATE_ACQ_ERR_THRESHOLD) { + dev_err(ice_pf_to_dev(pf), + "EEC/PPS DPLLs periodic work disabled\n"); + mutex_unlock(&pf->dplls.lock); + return; + } } + mutex_unlock(&pf->dplls.lock); + ice_dpll_notify_changes(de); + ice_dpll_notify_changes(dp); + if (ntf_mask) + ice_dpll_pins_notify_mask(d, d->inputs, d->num_inputs, + ntf_mask); } - mutex_unlock(&pf->dplls.lock); - ice_dpll_notify_changes(de); - ice_dpll_notify_changes(dp); - if (phase_offset_ntf) - ice_dpll_pins_notify_mask(d, d->inputs, d->num_inputs, - phase_offset_ntf); =20 resched: /* Run twice a second or reschedule if update failed */ @@ -3568,6 +3763,7 @@ static void ice_dpll_pin_notify_work(struct work_stru= ct *work) work); struct ice_dpll_pin *pin, *parent =3D w->pin; bool is_tx_synce_parent =3D false; + bool is_tspll_time_ref =3D false; struct ice_pf *pf =3D parent->pf; bool is_rclk_parent =3D false; int ret; @@ -3588,7 +3784,11 @@ static void ice_dpll_pin_notify_work(struct work_str= uct *work) is_tx_synce_parent =3D ice_dpll_fwnode_eq(parent->fwnode, pf->dplls.txclks[E825_EXT_SYNCE_PIN_IDX].fwnode); - if (!is_rclk_parent && !is_tx_synce_parent) + + is_tspll_time_ref =3D + ice_dpll_fwnode_eq(parent->fwnode, pf->dplls.tspll_in.fwnode); + + if (!is_rclk_parent && !is_tx_synce_parent && !is_tspll_time_ref) goto out; =20 switch (w->action) { @@ -3629,6 +3829,18 @@ static void ice_dpll_pin_notify_work(struct work_str= uct *work) ERR_PTR(ret)); goto drop_parent_ref; } + } else if (is_tspll_time_ref) { + /* Register TIME_REF pin directly to TSPLL DPLL */ + ret =3D dpll_pin_register(pf->dplls.tspll.dpll, + parent->pin, + &ice_dpll_tspll_in_ops, + parent); + if (ret) { + dev_err(ice_pf_to_dev(pf), + "TSPLL TIME_REF pin register failed: %pe\n", + ERR_PTR(ret)); + goto drop_parent_ref; + } } break; case DPLL_PIN_DELETED: @@ -3647,6 +3859,11 @@ static void ice_dpll_pin_notify_work(struct work_str= uct *work) pin =3D &pf->dplls.txclks[E825_EXT_SYNCE_PIN_IDX]; dpll_pin_unregister(pf->dplls.txc.dpll, pin->pin, &ice_dpll_txclk_ops, pin); + } else if (is_tspll_time_ref) { + /* Unregister TIME_REF pin from TSPLL DPLL */ + dpll_pin_unregister(pf->dplls.tspll.dpll, + parent->pin, + &ice_dpll_tspll_in_ops, parent); } drop_parent_ref: /* Drop fwnode pin reference */ @@ -3818,12 +4035,12 @@ ice_dpll_deinit_fwnode_pins(struct ice_pf *pf, stru= ct ice_dpll_pin *pins, destroy_workqueue(pf->dplls.wq); } =20 -static int ice_dpll_deinit_txclk_pins(struct ice_pf *pf) +static int ice_dpll_deinit_txclk_pins(struct ice_pf *pf, bool flush) { struct ice_dpll_pin *synce_pin =3D &pf->dplls.txclks[E825_EXT_SYNCE_PIN_I= DX]; struct ice_dpll *dt =3D &pf->dplls.txc; =20 - ice_dpll_stop_fwnode_pin_activity(synce_pin, true); + ice_dpll_stop_fwnode_pin_activity(synce_pin, flush); ice_dpll_unregister_pins(dt->dpll, pf->dplls.txclks, &ice_dpll_txclk_ops, ARRAY_SIZE(pf->dplls.txclks)); @@ -3838,6 +4055,25 @@ static int ice_dpll_deinit_txclk_pins(struct ice_pf = *pf) return 0; } =20 +/** + * ice_dpll_deinit_tspll_pins - deinitialize TSPLL fwnode pin + * @pf: board private structure + * + * Stop notifier activity and release the TIME_REF fwnode pin, unregisteri= ng + * it from the TSPLL DPLL if it was registered. + */ +static void ice_dpll_deinit_tspll_pins(struct ice_pf *pf) +{ + struct ice_dpll_pin *time_ref =3D &pf->dplls.tspll_in; + struct ice_dpll *tp =3D &pf->dplls.tspll; + + ice_dpll_stop_fwnode_pin_activity(time_ref, true); + if (!IS_ERR_OR_NULL(time_ref->pin)) + dpll_pin_unregister(tp->dpll, time_ref->pin, + &ice_dpll_tspll_in_ops, time_ref); + ice_dpll_release_fwnode_pin(time_ref); +} + /** * ice_dpll_deinit_pins - deinitialize direct pins * @pf: board private structure @@ -3870,7 +4106,10 @@ static void ice_dpll_deinit_pins(struct ice_pf *pf, = bool cgu) =20 ice_dpll_deinit_rclk_pin(pf); if (pf->hw.mac_type =3D=3D ICE_MAC_GENERIC_3K_E825) { - ice_dpll_deinit_txclk_pins(pf); + if (ice_pf_src_tmr_owned(pf) && + test_bit(ICE_FLAG_PTP_SUPPORTED, pf->flags)) + ice_dpll_deinit_tspll_pins(pf); + ice_dpll_deinit_txclk_pins(pf, true); ice_dpll_deinit_fwnode_pins(pf, pf->dplls.inputs, 0); } if (cgu) { @@ -4066,6 +4305,45 @@ static int ice_dpll_init_txclk_pins(struct ice_pf *p= f, int start_idx) return ret; } =20 +/** + * ice_dpll_init_tspll_pins - init and register TSPLL TIME_REF fwnode pin + * @pf: board private structure + * + * Initialize the fwnode-based TIME_REF input pin for the TSPLL DPLL on E8= 25. + * If the pin is not yet available in the dpll subsystem, registration will + * be deferred via the notifier path. + * + * Return: + * * 0 - success + * * negative - initialization failure reason + */ +static int ice_dpll_init_tspll_pins(struct ice_pf *pf) +{ + struct ice_dpll_pin *time_ref =3D &pf->dplls.tspll_in; + struct ice_dpll *tp =3D &pf->dplls.tspll; + int ret; + + time_ref->pf =3D pf; + ret =3D ice_dpll_init_fwnode_pin(time_ref, ice_dpll_fwnode_time_ref); + if (ret) + return ret; + + if (IS_ERR_OR_NULL(time_ref->pin)) { + dev_dbg(ice_pf_to_dev(pf), + "TSPLL TIME_REF pin not registered yet\n"); + return 0; + } + + ret =3D dpll_pin_register(tp->dpll, time_ref->pin, + &ice_dpll_tspll_in_ops, time_ref); + if (ret) { + ice_dpll_stop_fwnode_pin_activity(time_ref, false); + ice_dpll_release_fwnode_pin(time_ref); + } + + return ret; +} + /** * ice_dpll_init_pins_e825 - init pins and register pins with a dplls * @pf: board private structure @@ -4093,8 +4371,25 @@ static int ice_dpll_init_pins_e825(struct ice_pf *pf) goto unregister_pins; =20 ret =3D ice_dpll_init_txclk_pins(pf, 0); - if (ret) + if (ret) { ice_dpll_deinit_rclk_pin(pf); + goto unregister_pins; + } + + if (ice_pf_src_tmr_owned(pf) && + test_bit(ICE_FLAG_PTP_SUPPORTED, pf->flags)) { + ret =3D ice_dpll_init_tspll_pins(pf); + if (ret) { + /* + * Avoid flushing pf->dplls.wq here: notifier work + * items block on pf->dplls.dpll_init which is + * completed by the unregister_pins path below. + * destroy_workqueue() there drains queued items. + */ + ice_dpll_deinit_txclk_pins(pf, false); + ice_dpll_deinit_rclk_pin(pf); + } + } =20 unregister_pins: if (ret) { @@ -4288,6 +4583,7 @@ static void ice_dpll_deinit_worker(struct ice_pf *pf) =20 kthread_cancel_delayed_work_sync(&d->work); kthread_destroy_worker(d->kworker); + d->kworker =3D NULL; } =20 /** @@ -4777,6 +5073,7 @@ 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 =3D &pf->dplls; + struct ice_dpll *tp =3D &d->tspll; struct ice_dpll *dt =3D &d->txc; int ret =3D 0; int i; @@ -4785,7 +5082,11 @@ static int ice_dpll_init_info_e825c(struct ice_pf *p= f) d->num_inputs =3D ICE_SYNCE_CLK_NUM; dt->dpll_state =3D ice_txclk_lock_status(pf->ptp.port.tx_clk); dt->mode =3D DPLL_MODE_MANUAL; - dt->dpll_idx =3D pf->ptp.port.port_num; + dt->dpll_idx =3D E825_DPLL_TXCLK_BASE_IDX + pf->ptp.port.port_num; + tp->dpll_state =3D ice_dpll_tspll_lock_status_get(pf, false); + tp->prev_dpll_state =3D tp->dpll_state; + tp->mode =3D DPLL_MODE_MANUAL; + tp->dpll_idx =3D E825_DPLL_TSPLL_BASE_IDX; =20 d->inputs =3D kzalloc_objs(*d->inputs, d->num_inputs); if (!d->inputs) @@ -5040,7 +5341,8 @@ void ice_dpll_lock_state_set_unmanaged(struct ice_pf = *pf, * resources and unregistering the dpll, pin and all resources used for * handling them. * - * Context: Destroys pf->dplls.lock mutex. Call only if ICE_FLAG_DPLL was = set. + * Context: Call only if ICE_FLAG_DPLL was set. pf->dplls.lock lifetime is + * managed by ice_init_features()/ice_deinit_features(). */ void ice_dpll_deinit(struct ice_pf *pf) { @@ -5064,7 +5366,7 @@ void ice_dpll_deinit(struct ice_pf *pf) up_write(&pf->dplls.txclk_notify_rwsem); } =20 - if (cgu) + if (pf->dplls.kworker) ice_dpll_deinit_worker(pf); =20 if (pf->hw.mac_type =3D=3D ICE_MAC_GENERIC_3K_E825) @@ -5078,9 +5380,10 @@ void ice_dpll_deinit(struct ice_pf *pf) 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); + if (!IS_ERR_OR_NULL(pf->dplls.tspll.dpll)) + ice_dpll_deinit_dpll(pf, &pf->dplls.tspll, false); =20 ice_dpll_deinit_info(pf); - mutex_destroy(&pf->dplls.lock); } =20 /** @@ -5089,17 +5392,13 @@ void ice_dpll_deinit(struct ice_pf *pf) * * Set up the device dplls for unmanaged mode, register them and pins conn= ected * within Linux dpll subsystem. Allow userspace to obtain state of DPLL. - * - * Context: Initializes pf->dplls.lock mutex. */ static void ice_dpll_init_unmanaged(struct ice_pf *pf) { - struct ice_dplls *d =3D &pf->dplls; int err; =20 if (!ice_pf_src_tmr_owned(pf)) return; - mutex_init(&d->lock); err =3D ice_dpll_init_info_unmanaged(pf); if (err) goto err_exit; @@ -5118,7 +5417,6 @@ static void ice_dpll_init_unmanaged(struct ice_pf *pf) deinit_info: ice_dpll_deinit_info(pf); err_exit: - mutex_destroy(&d->lock); dev_warn(ice_pf_to_dev(pf), "DPLLs init failure err:%d\n", err); } =20 @@ -5129,8 +5427,6 @@ static void ice_dpll_init_unmanaged(struct ice_pf *pf) * Set up the device dplls, register them and pins connected within Linux = dpll * subsystem. Allow userspace to obtain state of DPLL and handling of DPLL * configuration requests. - * - * Context: Initializes pf->dplls.lock mutex. */ static void ice_dpll_init_e825(struct ice_pf *pf) { @@ -5148,7 +5444,6 @@ static void ice_dpll_init_e825(struct ice_pf *pf) pf->ptp.port.port_num =3D pf->hw.lane_num; } =20 - 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. @@ -5160,9 +5455,30 @@ static void ice_dpll_init_e825(struct ice_pf *pf) err =3D ice_dpll_init_info_e825c(pf); if (err) goto err_exit; + if (ice_pf_src_tmr_owned(pf) && + test_bit(ICE_FLAG_PTP_SUPPORTED, pf->flags)) { + err =3D ice_dpll_init_dpll(pf, &pf->dplls.tspll, false, + DPLL_TYPE_GENERIC); + if (err) + goto deinit_info; + /* Start the periodic worker before registering fwnode pins. The + * fwnode notifier path (ice_dpll_pin_notify_work()) blocks on + * pf->dplls.dpll_init, which is only signalled on the success + * return below. Starting the worker before any fwnode notifier + * is registered means a subsequent init failure cannot leave + * notifier work items queued on pf->dplls.wq, so the deinit + * flush_workqueue() paths in ice_dpll_deinit_pins() cannot + * deadlock. The E825 periodic worker only touches + * pf->dplls.tspll (already registered above) and pf->ptp state, + * so it is safe to run before pins. + */ + err =3D ice_dpll_init_worker(pf); + if (err) + goto deinit_tspll; + } err =3D ice_dpll_init_dpll(pf, &pf->dplls.txc, false, DPLL_TYPE_GENERIC); if (err) - goto deinit_info; + goto deinit_worker; err =3D ice_dpll_init_pins_e825(pf); if (err) goto deinit_txclk; @@ -5173,10 +5489,15 @@ static void ice_dpll_init_e825(struct ice_pf *pf) =20 deinit_txclk: ice_dpll_deinit_dpll(pf, &pf->dplls.txc, false); +deinit_worker: + if (pf->dplls.kworker) + ice_dpll_deinit_worker(pf); +deinit_tspll: + if (!IS_ERR_OR_NULL(pf->dplls.tspll.dpll)) + ice_dpll_deinit_dpll(pf, &pf->dplls.tspll, false); deinit_info: ice_dpll_deinit_info(pf); err_exit: - mutex_destroy(&d->lock); dev_warn(ice_pf_to_dev(pf), "DPLLs init failure err:%d\n", err); } =20 @@ -5187,16 +5508,12 @@ static void ice_dpll_init_e825(struct ice_pf *pf) * Set up the device dplls, register them and pins connected within Linux = dpll * subsystem. Allow userspace to obtain state of DPLL and handling of DPLL * configuration requests. - * - * Context: Initializes pf->dplls.lock mutex. */ static void ice_dpll_init_e810(struct ice_pf *pf) { bool cgu =3D ice_is_feature_supported(pf, ICE_F_CGU); - struct ice_dplls *d =3D &pf->dplls; int err =3D 0; =20 - mutex_init(&d->lock); err =3D ice_dpll_init_info(pf, cgu); if (err) goto err_exit; @@ -5227,7 +5544,6 @@ static void ice_dpll_init_e810(struct ice_pf *pf) deinit_info: ice_dpll_deinit_info(pf); err_exit: - mutex_destroy(&d->lock); dev_warn(ice_pf_to_dev(pf), "DPLLs init failure err:%d\n", err); } =20 diff --git a/drivers/net/ethernet/intel/ice/ice_dpll.h b/drivers/net/ethern= et/intel/ice/ice_dpll.h index c59d746a8567..6bdb821bfb3a 100644 --- a/drivers/net/ethernet/intel/ice/ice_dpll.h +++ b/drivers/net/ethernet/intel/ice/ice_dpll.h @@ -135,10 +135,12 @@ struct ice_dpll { * @eec: pointer to EEC dpll dev * @pps: pointer to PPS dpll dev * @txc: pointer to TXC dpll dev + * @tspll: pointer to TSPLL dpll dev (E825 only) * @inputs: input pins pointer * @outputs: output pins pointer * @rclk: recovered pins pointer * @txclks: TX clock reference pins pointer + * @tspll_in: TSPLL TIME_REF fwnode input pin (E825 only) * @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 @@ -179,12 +181,14 @@ struct ice_dplls { struct ice_dpll eec; struct ice_dpll pps; struct ice_dpll txc; + struct ice_dpll tspll; 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]; + struct ice_dpll_pin tspll_in; u8 num_inputs; u8 num_outputs; u8 sma_data; diff --git a/drivers/net/ethernet/intel/ice/ice_main.c b/drivers/net/ethern= et/intel/ice/ice_main.c index 968a8e18d197..ab83b64b2270 100644 --- a/drivers/net/ethernet/intel/ice/ice_main.c +++ b/drivers/net/ethernet/intel/ice/ice_main.c @@ -4920,6 +4920,14 @@ static void ice_init_features(struct ice_pf *pf) if (ice_is_safe_mode(pf)) return; =20 + /* pf->dplls.lock guards TSPLL/CGU access shared between the DPLL + * subsystem callbacks and the PTP periodic worker's TSPLL monitor. + * Initialize it before ice_ptp_init() so the PTP kworker never sees + * an uninitialized mutex, and destroy it in ice_deinit_features() + * only after ice_ptp_release() has drained the kworker. + */ + mutex_init(&pf->dplls.lock); + /* initialize DDP driven features */ if (test_bit(ICE_FLAG_PTP_SUPPORTED, pf->flags)) ice_ptp_init(pf); @@ -4976,6 +4984,7 @@ static void ice_deinit_features(struct ice_pf *pf) ice_ptp_release(pf); if (test_bit(ICE_FLAG_DPLL, pf->flags)) ice_dpll_deinit(pf); + mutex_destroy(&pf->dplls.lock); if (pf->eswitch_mode =3D=3D DEVLINK_ESWITCH_MODE_SWITCHDEV) xa_destroy(&pf->eswitch.reprs); ice_hwmon_exit(pf); diff --git a/drivers/net/ethernet/intel/ice/ice_ptp.c b/drivers/net/etherne= t/intel/ice/ice_ptp.c index a997be5f7d8f..39538068a11e 100644 --- a/drivers/net/ethernet/intel/ice/ice_ptp.c +++ b/drivers/net/ethernet/intel/ice/ice_ptp.c @@ -2904,8 +2904,19 @@ static void ice_ptp_tspll_monitor(struct ice_pf *pf) !ice_pf_src_tmr_owned(pf)) return; =20 + /* Serialize the entire monitor tick against TSPLL userspace reconfig + * (ice_dpll_tspll_state_on_dpll_set()). Both paths read HW state and + * write pf->ptp.tspll_locked; without holding pf->dplls.lock across + * the HW read here, a preempted monitor could observe stale HW state + * and then overwrite an accurate cache update from the DPLL callback. + * pf->dplls.lock is initialized in ice_init_features() before the PTP + * kworker starts and destroyed in ice_deinit_features() only after + * ice_ptp_release() has drained the kworker, so it is always valid. + */ + mutex_lock(&pf->dplls.lock); err =3D ice_tspll_lost_lock_e825c(&pf->hw, &lock_lost); if (err) { + mutex_unlock(&pf->dplls.lock); dev_err_ratelimited(ice_pf_to_dev(pf), "Failed reading TimeSync PLL lock status (err: %d). Retrying.\n", err); @@ -2937,6 +2948,7 @@ static void ice_ptp_tspll_monitor(struct ice_pf *pf) WRITE_ONCE(pf->ptp.tspll_locked, true); pf->ptp.tspll_lock_retries =3D 0; } + mutex_unlock(&pf->dplls.lock); } =20 static void ice_ptp_periodic_work(struct kthread_work *work) diff --git a/drivers/net/ethernet/intel/ice/ice_tspll.c b/drivers/net/ether= net/intel/ice/ice_tspll.c index be8e2da21dd6..378eefe0f9af 100644 --- a/drivers/net/ethernet/intel/ice/ice_tspll.c +++ b/drivers/net/ethernet/intel/ice/ice_tspll.c @@ -334,8 +334,9 @@ static int ice_tspll_dis_sticky_bits_e82x(struct ice_hw= *hw) * Return: * * %0 - success * * %-EINVAL - input parameters are incorrect - * * %-EBUSY - failed to lock TSPLL - * * %other - CGU read/write failure + * * %-EAGAIN - TSPLL configuration succeeded but lock is not acquired yet + * * %-EBUSY - CGU access is busy (for example during reset) + * * %other - other CGU read/write failures */ static int ice_tspll_cfg_e825c(struct ice_hw *hw, enum ice_tspll_freq clk_= freq, enum ice_clk_src clk_src) @@ -467,7 +468,7 @@ static int ice_tspll_cfg_e825c(struct ice_hw *hw, enum = ice_tspll_freq clk_freq, =20 if (!(val & ICE_CGU_RO_LOCK_TRUE_LOCK)) { dev_warn(ice_hw_to_dev(hw), "CGU PLL failed to lock\n"); - return -EBUSY; + return -EAGAIN; } =20 err =3D ice_read_cgu_reg(hw, ICE_CGU_R9, &r9); @@ -663,6 +664,33 @@ int ice_tspll_get_clk_src(struct ice_hw *hw, enum ice_= clk_src *clk_src) return 0; } =20 +/** + * ice_tspll_set_cfg - configure TS PLL with new settings + * @hw: board private hw structure + * @clk_freq: clock frequency to program + * @clk_src: clock source to select (TIME_REF, or TCXO) + * + * Configure CGU with new clock source and clock frequency settings. + * + * Return: + * * 0 - OK + * * negative - error + */ +int ice_tspll_set_cfg(struct ice_hw *hw, enum ice_tspll_freq clk_freq, + enum ice_clk_src clk_src) +{ + int ret; + + if (!ice_tspll_check_params(hw, clk_freq, clk_src)) + return -EINVAL; + + ret =3D ice_tspll_dis_sticky_bits(hw); + if (ret) + return ret; + + return ice_tspll_cfg(hw, clk_freq, clk_src); +} + /** * ice_tspll_init - Initialize TSPLL with settings from firmware * @hw: Pointer to the HW structure diff --git a/drivers/net/ethernet/intel/ice/ice_tspll.h b/drivers/net/ether= net/intel/ice/ice_tspll.h index 05917ae51ded..832c049e686c 100644 --- a/drivers/net/ethernet/intel/ice/ice_tspll.h +++ b/drivers/net/ethernet/intel/ice/ice_tspll.h @@ -35,6 +35,8 @@ int ice_tspll_cfg_pps_out_e825c(struct ice_hw *hw, bool e= nable); int ice_tspll_lost_lock_e825c(struct ice_hw *hw, bool *lost_lock); int ice_tspll_restart_e825c(struct ice_hw *hw); int ice_tspll_get_clk_src(struct ice_hw *hw, enum ice_clk_src *clk_src); +int ice_tspll_set_cfg(struct ice_hw *hw, enum ice_tspll_freq clk_freq, + enum ice_clk_src clk_src); int ice_tspll_init(struct ice_hw *hw); int ice_tspll_bypass_mux_active_e825c(struct ice_hw *hw, u8 port, bool *ac= tive, enum ice_synce_clk output); --=20 2.39.3 From nobody Fri Oct 2 08:28:23 2026 Received: from mgamail.intel.com (mgamail.intel.com [192.198.163.11]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 8EFC7403E9D; Mon, 3 Aug 2026 11:41:21 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=192.198.163.11 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1785757284; cv=none; b=JQXcVmQOlH9sk5HhCFToyzbzkVdd8P5GRzj2RnkvgIeh6rvA+6ekkAHMNH0YA9VSRsMU4quQXxv5X6lMXuWdYiEJhzqf0m1VPhPRiQLTOEDWDnTlcSi5mQq8jdHod+0Wrtpj3SKGNYhm0padh72jmhayj75S5EGUfwTP0fbJf1s= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1785757284; c=relaxed/simple; bh=Onz7CdZbRWZ4/sUp0LhAYEpZ8q+IcNlKx2NN1kFBa+w=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=EGIoqaNPei46LMReEUljCVnpI7zJ4z6B62Iw53/NhQwYEA1FEORmicYVT8idly3A8ZhKe4LEdceco5Uoe1SqXq5wdNpTy4eqEiWI1GQZiz/QGkRJ7eB+eBESzTDg4RKvrecEdAadn5QZaD/e9VG8YqV4zFrw8gL3Ka4VlAEz2dM= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com; spf=pass smtp.mailfrom=intel.com; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b=ffpMeJ2c; arc=none smtp.client-ip=192.198.163.11 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=intel.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b="ffpMeJ2c" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1785757282; x=1817293282; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=Onz7CdZbRWZ4/sUp0LhAYEpZ8q+IcNlKx2NN1kFBa+w=; b=ffpMeJ2ctT9to7IA2Wrxzj1CtKoMheHyUCUUZp1CvO/aWYmlVgNXSIus 0VYeHJoXpq+Tiw905ANaxN/bB7eMs3lkk4WOLYEHyeLuqSx3M3PVJGY+X QIOVnzPab9+tvjdVMbZRHW9gViYKTfRPGcBcurh+embTjY8ZDTXELBfTG oB+Js6B1xSG09ixkFvs/aE1YS27nS6ESscCm4dNbGxLm22GzobeV53Uf3 RckIQQYMChMwYt/2kfxNdeYtcNk18oCL6XrMUo/y8O5dnYEADvXkYP/Is InZ++n/7+uj/Hx4y87cGLTkYwd7nBtsgGR6lz8ky8dymrjwiohbuQMmLt Q==; X-CSE-ConnectionGUID: AxuMfgtwTiSnAyDEOJo1+Q== X-CSE-MsgGUID: 0aPiqDVyT8SnsD+VmII3iw== X-IronPort-AV: E=McAfee;i="6800,10657,11863"; a="96878857" X-IronPort-AV: E=Sophos;i="6.25,202,1779174000"; d="scan'208";a="96878857" Received: from fmviesa010.fm.intel.com ([10.60.135.150]) by fmvoesa105.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 03 Aug 2026 04:41:17 -0700 X-CSE-ConnectionGUID: nh/o6NbySkmPZsODBrLtQQ== X-CSE-MsgGUID: 5uJGVq50Rj+n/6WLOWHs5g== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.25,202,1779174000"; d="scan'208";a="257299048" Received: from gklab-003-001.igk.intel.com ([10.91.173.48]) by fmviesa010.fm.intel.com with ESMTP; 03 Aug 2026 04:40:20 -0700 From: Grzegorz Nitka To: intel-wired-lan@lists.osuosl.org Cc: netdev@vger.kernel.org, linux-kernel@vger.kernel.org, richardcochran@gmail.com, andrew+netdev@lunn.ch, przemyslaw.kitszel@intel.com, anthony.l.nguyen@intel.com, arkadiusz.kubalewski@intel.com, pabeni@redhat.com, kuba@kernel.org, davem@davemloft.net, edumazet@google.com, Grzegorz Nitka , Przemyslaw Korba Subject: [PATCH v3 iwl-next 3/3] ice: use per-interface clock_id for E825 generic DPLLs Date: Mon, 3 Aug 2026 13:35:07 +0200 Message-Id: <20260803113507.1858083-4-grzegorz.nitka@intel.com> X-Mailer: git-send-email 2.39.3 In-Reply-To: <20260803113507.1858083-1-grzegorz.nitka@intel.com> References: <20260803113507.1858083-1-grzegorz.nitka@intel.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" On E825, the TX-CLK and TSPLL DPLL devices are registered as DPLL_TYPE_GENERIC. Their clock_id was derived from the board-level PCIe DSN, which is identical for all interfaces sharing the same NAC/quad. As a result, userspace (e.g. 'dpll device show') reports several DPLL devices with the same clock_id and no board or signal label, making it impossible to unambiguously map a DPLL device to the interface it belongs to. Since these DPLLs are per-interface, use the permanent port MAC as the clock_id basis for E825 generic DPLLs: * TX-CLK uses the plain MAC-derived value. * TSPLL on the source-timer owner PF uses the same MAC-derived value with a dedicated tag bit, so it stays distinct from TX-CLK while remaining stable per interface. Other DPLL objects (EEC/PPS and non-E825 paths) keep the board DSN-derived clock_id. When the permanent MAC is not yet valid, fall back to the existing board-level clock_id to preserve init behavior. Reviewed-by: Przemyslaw Korba Signed-off-by: Grzegorz Nitka Tested-by: Alexander Nowlin --- drivers/net/ethernet/intel/ice/ice_dpll.c | 81 +++++++++++++++++++++-- 1 file changed, 76 insertions(+), 5 deletions(-) diff --git a/drivers/net/ethernet/intel/ice/ice_dpll.c b/drivers/net/ethern= et/intel/ice/ice_dpll.c index 60d24b796c10..ea3153237f90 100644 --- a/drivers/net/ethernet/intel/ice/ice_dpll.c +++ b/drivers/net/ethernet/intel/ice/ice_dpll.c @@ -3055,6 +3055,68 @@ static u64 ice_generate_clock_id(struct ice_pf *pf) return pci_get_dsn(pf->pdev); } =20 +/** + * ice_generate_dpll_clock_id - generate clock_id for a specific dpll devi= ce + * @pf: board private structure + * @d: dpll device context + * @type: dpll type being registered + * + * For E825 generic DPLLs, use per-interface permanent MAC as the clock_id + * basis so userspace can unambiguously map DPLL devices to interfaces. + * TX-CLK keeps plain MAC-derived ID, while TSPLL uses the same basis with + * a dedicated tag bit to remain distinct on source-timer owner PFs. + * Other DPLL objects keep board-level DSN-derived clock_id. + * + * Return: generated clock id for a dpll device + */ +static u64 ice_generate_dpll_clock_id(struct ice_pf *pf, struct ice_dpll *= d, + enum dpll_type type) +{ + struct ice_hw *hw =3D &pf->hw; + u64 mac_clock_id; + + if (hw->mac_type =3D=3D ICE_MAC_GENERIC_3K_E825 && + type =3D=3D DPLL_TYPE_GENERIC && + hw->port_info && + is_valid_ether_addr(hw->port_info->mac.perm_addr)) { + mac_clock_id =3D ether_addr_to_u64(hw->port_info->mac.perm_addr); + + if (d->dpll_idx >=3D E825_DPLL_TXCLK_BASE_IDX) + return mac_clock_id; + + if (d->dpll_idx =3D=3D E825_DPLL_TSPLL_BASE_IDX) + return mac_clock_id | BIT_ULL(63); + } + + return pf->dplls.clock_id; +} + +/** + * ice_dpll_is_own_dpll_clock_id - check if clock_id belongs to this pf's = DPLLs + * @pf: board private structure + * @clock_id: clock_id from a DPLL notification + * + * Match info->src_clock_id from a DPLL pin notification against any DPLL + * device this PF has registered. Used to suppress self-notifications + * generated as a side effect of our own dpll_pin_register() and + * dpll_pin_unregister() calls on the fwnode-backed SYNCE and TIME_REF pin= s, + * whose DPLLs (TXC and TSPLL) use MAC-derived clock_ids on E825. + * + * Return: true if clock_id matches one of this PF's registered DPLL devic= es. + */ +static bool ice_dpll_is_own_dpll_clock_id(struct ice_pf *pf, u64 clock_id) +{ + if (clock_id =3D=3D pf->dplls.clock_id) + return true; + if (pf->hw.mac_type !=3D ICE_MAC_GENERIC_3K_E825) + return false; + if (clock_id =3D=3D ice_generate_dpll_clock_id(pf, &pf->dplls.txc, + DPLL_TYPE_GENERIC)) + return true; + return clock_id =3D=3D ice_generate_dpll_clock_id(pf, &pf->dplls.tspll, + DPLL_TYPE_GENERIC); +} + /** * ice_dpll_tspll_lock_status_get - derive TSPLL state for dpll subsystem * @pf: board private structure @@ -3891,10 +3953,12 @@ static int ice_dpll_pin_notify(struct notifier_bloc= k *nb, unsigned long action, if (pin->fwnode !=3D info->fwnode) return NOTIFY_DONE; /* Not this pin */ =20 - /* Ignore notification which are the outcome of internal pin - * registration/unregistration calls - synce pin case. + /* Ignore notifications that are a side effect of internal pin + * registration/unregistration calls. E825 uses per-device + * MAC-derived clock_ids for the TXC and TSPLL generic DPLLs, so + * info->src_clock_id may not equal pf->dplls.clock_id. */ - if (info->src_clock_id =3D=3D pin->pf->dplls.clock_id) + if (ice_dpll_is_own_dpll_clock_id(pin->pf, info->src_clock_id)) return NOTIFY_DONE; =20 work =3D kzalloc_obj(*work); @@ -4251,10 +4315,17 @@ static int ice_dpll_init_txclk_pins(struct ice_pf *= pf, int start_idx) { struct ice_dpll_pin *ref_pin =3D pf->dplls.txclks; struct ice_dpll *txc =3D &pf->dplls.txc; + u64 clock_id; int ret; =20 + /* + * EXT_EREF0 is a non-fwnode pin; its clock_id must match the TX-CLK + * DPLL device clock_id (see dpll_pin_register()). + */ + clock_id =3D ice_generate_dpll_clock_id(pf, txc, DPLL_TYPE_GENERIC); + /* Configure EXT_EREF0 pin */ - ret =3D ice_dpll_get_pins(pf, ref_pin, start_idx, 1, pf->dplls.clock_id); + ret =3D ice_dpll_get_pins(pf, ref_pin, start_idx, 1, clock_id); if (ret) return ret; ret =3D dpll_pin_register(txc->dpll, ref_pin->pin, &ice_dpll_txclk_ops, @@ -4539,7 +4610,7 @@ static int ice_dpll_init_dpll(struct ice_pf *pf, struct ice_dpll *d, bool cgu, enum dpll_type type) { - u64 clock_id =3D pf->dplls.clock_id; + u64 clock_id =3D ice_generate_dpll_clock_id(pf, d, type); int ret; =20 d->dpll =3D dpll_device_get(clock_id, d->dpll_idx, THIS_MODULE, --=20 2.39.3