When an X710 ethernet port with an active ptp daemon (like the ptp4l and
phc2sys combo) suddenly loses its link and regains it after a while, the
ptp daemon has a hard time to recover synchronization and sometimes
entirely fails to do so.

The issue seems to be related to a wrongly configured increment while the
link is down. This could not be observed with the Intel reference driver.
We identified the fix to appear in Intels official ethernet-linux-i40e
release version 2.17.4.

Include the relevant changes in the kernel version of this driver.

Fixes: beb0dff1251d ("i40e: enable PTP")
Cc: [email protected]
Signed-off-by: Markus Blöchl <[email protected]>
---
Tested with an X710 at 10G link speed and kernel version 6.12.42.
---
Changes in v2:
- Fix kdoc and code formatting
- Rebase onto net tree
- Link to v1: 
https://lore.kernel.org/r/[email protected]
---
 drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h |  9 +++
 drivers/net/ethernet/intel/i40e/i40e_ptp.c        | 69 +++++++++++++++++++++--
 drivers/net/ethernet/intel/i40e/i40e_register.h   |  9 +++
 drivers/net/ethernet/intel/i40e/i40e_type.h       |  8 +++
 4 files changed, 90 insertions(+), 5 deletions(-)

diff --git a/drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h 
b/drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h
index cc02a85ad42b..ec176e9569ad 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h
+++ b/drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h
@@ -1488,6 +1488,15 @@ enum i40e_aq_link_speed {
        I40E_LINK_SPEED_25GB    = BIT(I40E_LINK_SPEED_25GB_SHIFT),
 };
 
+enum i40e_prt_mac_pcs_link_speed {
+       I40E_PRT_MAC_PCS_LINK_SPEED_UNKNOWN = 0,
+       I40E_PRT_MAC_PCS_LINK_SPEED_100MB,
+       I40E_PRT_MAC_PCS_LINK_SPEED_1GB,
+       I40E_PRT_MAC_PCS_LINK_SPEED_10GB,
+       I40E_PRT_MAC_PCS_LINK_SPEED_40GB,
+       I40E_PRT_MAC_PCS_LINK_SPEED_20GB
+};
+
 struct i40e_aqc_module_desc {
        u8 oui[3];
        u8 reserved1;
diff --git a/drivers/net/ethernet/intel/i40e/i40e_ptp.c 
b/drivers/net/ethernet/intel/i40e/i40e_ptp.c
index 33535418178b..89abe2f22216 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_ptp.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_ptp.c
@@ -847,6 +847,66 @@ void i40e_ptp_rx_hwtstamp(struct i40e_pf *pf, struct 
sk_buff *skb, u8 index)
        i40e_ptp_convert_to_hwtstamp(skb_hwtstamps(skb), ns);
 }
 
+/**
+ * i40e_ptp_get_link_speed_hw - get the link speed
+ * @pf: Board private structure
+ *
+ * Calculate link speed depending on the link status.
+ *
+ * Return: current link speed.
+ **/
+static enum i40e_aq_link_speed i40e_ptp_get_link_speed_hw(struct i40e_pf *pf)
+{
+       bool link_up = pf->hw.phy.link_info.link_info & I40E_AQ_LINK_UP;
+       enum i40e_aq_link_speed link_speed = I40E_LINK_SPEED_UNKNOWN;
+       struct i40e_hw *hw = &pf->hw;
+
+       if (link_up) {
+               struct i40e_link_status *hw_link_info = &hw->phy.link_info;
+
+               i40e_aq_get_link_info(hw, true, NULL, NULL);
+               link_speed = hw_link_info->link_speed;
+       } else {
+               enum i40e_prt_mac_link_speed prtmac_linksta;
+               u64 prtmac_pcs_linksta;
+
+               prtmac_linksta = (rd32(hw, I40E_PRTMAC_LINKSTA(0)) &
+                                 I40E_PRTMAC_LINKSTA_MAC_LINK_SPEED_MASK) >>
+                                 I40E_PRTMAC_LINKSTA_MAC_LINK_SPEED_SHIFT;
+               if (prtmac_linksta == I40E_PRT_MAC_LINK_SPEED_40GB) {
+                       link_speed = I40E_LINK_SPEED_40GB;
+               } else {
+                       i40e_aq_debug_read_register(hw,
+                                                   
I40E_PRTMAC_PCS_LINK_STATUS1(0),
+                                                   &prtmac_pcs_linksta,
+                                                   NULL);
+
+                       prtmac_pcs_linksta = (prtmac_pcs_linksta &
+                                             
I40E_PRTMAC_PCS_LINK_STATUS1_LINK_SPEED_MASK) >>
+                                             
I40E_PRTMAC_PCS_LINK_STATUS1_LINK_SPEED_SHIFT;
+
+                       switch (prtmac_pcs_linksta) {
+                       case I40E_PRT_MAC_PCS_LINK_SPEED_100MB:
+                               link_speed = I40E_LINK_SPEED_100MB;
+                               break;
+                       case I40E_PRT_MAC_PCS_LINK_SPEED_1GB:
+                               link_speed = I40E_LINK_SPEED_1GB;
+                               break;
+                       case I40E_PRT_MAC_PCS_LINK_SPEED_10GB:
+                               link_speed = I40E_LINK_SPEED_10GB;
+                               break;
+                       case I40E_PRT_MAC_PCS_LINK_SPEED_20GB:
+                               link_speed = I40E_LINK_SPEED_20GB;
+                               break;
+                       default:
+                               link_speed = I40E_LINK_SPEED_UNKNOWN;
+                       }
+               }
+       }
+
+       return link_speed;
+}
+
 /**
  * i40e_ptp_set_increment - Utility function to update clock increment rate
  * @pf: Board private structure
@@ -857,16 +917,14 @@ void i40e_ptp_rx_hwtstamp(struct i40e_pf *pf, struct 
sk_buff *skb, u8 index)
  **/
 void i40e_ptp_set_increment(struct i40e_pf *pf)
 {
-       struct i40e_link_status *hw_link_info;
+       enum i40e_aq_link_speed link_speed;
        struct i40e_hw *hw = &pf->hw;
        u64 incval;
        u32 mult;
 
-       hw_link_info = &hw->phy.link_info;
+       link_speed = i40e_ptp_get_link_speed_hw(pf);
 
-       i40e_aq_get_link_info(&pf->hw, true, NULL, NULL);
-
-       switch (hw_link_info->link_speed) {
+       switch (link_speed) {
        case I40E_LINK_SPEED_10GB:
                mult = I40E_PTP_10GB_INCVAL_MULT;
                break;
@@ -909,6 +967,7 @@ void i40e_ptp_set_increment(struct i40e_pf *pf)
        /* Update the base adjustement value. */
        WRITE_ONCE(pf->ptp_adj_mult, mult);
        smp_mb(); /* Force the above update. */
+       i40e_ptp_set_1pps_signal_hw(pf);
 }
 
 /**
diff --git a/drivers/net/ethernet/intel/i40e/i40e_register.h 
b/drivers/net/ethernet/intel/i40e/i40e_register.h
index 432afbb64201..c4051dbcc297 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_register.h
+++ b/drivers/net/ethernet/intel/i40e/i40e_register.h
@@ -530,6 +530,15 @@
 #define I40E_PRTMAC_HSEC_CTL_TX_PAUSE_REFRESH_TIMER_SHIFT 0
 #define I40E_PRTMAC_HSEC_CTL_TX_PAUSE_REFRESH_TIMER_MASK I40E_MASK(0xFFFF, \
        I40E_PRTMAC_HSEC_CTL_TX_PAUSE_REFRESH_TIMER_SHIFT)
+/* _i=0...3 */ /* Reset: GLOBR */
+#define I40E_PRTMAC_PCS_LINK_STATUS1(_i) (0x0008C200 + ((_i) * 4))
+#define I40E_PRTMAC_PCS_LINK_STATUS1_LINK_SPEED_SHIFT 24
+#define I40E_PRTMAC_PCS_LINK_STATUS1_LINK_SPEED_MASK I40E_MASK(0x7, 
I40E_PRTMAC_PCS_LINK_STATUS1_LINK_SPEED_SHIFT)
+#define I40E_PRTMAC_PCS_LINK_STATUS2 0x0008C220
+/* _i=0...3 */ /* Reset: GLOBR */
+#define I40E_PRTMAC_LINKSTA(_i) (0x001E2420 + ((_i) * 4))
+#define I40E_PRTMAC_LINKSTA_MAC_LINK_SPEED_SHIFT 27
+#define I40E_PRTMAC_LINKSTA_MAC_LINK_SPEED_MASK I40E_MASK(0x7, 
I40E_PRTMAC_LINKSTA_MAC_LINK_SPEED_SHIFT)
 #define I40E_GLNVM_FLA 0x000B6108 /* Reset: POR */
 #define I40E_GLNVM_FLA_LOCKED_SHIFT 6
 #define I40E_GLNVM_FLA_LOCKED_MASK I40E_MASK(0x1, I40E_GLNVM_FLA_LOCKED_SHIFT)
diff --git a/drivers/net/ethernet/intel/i40e/i40e_type.h 
b/drivers/net/ethernet/intel/i40e/i40e_type.h
index ed8bbdb586da..98c8c5709e5f 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_type.h
+++ b/drivers/net/ethernet/intel/i40e/i40e_type.h
@@ -115,6 +115,14 @@ enum i40e_queue_type {
        I40E_QUEUE_TYPE_UNKNOWN
 };
 
+enum i40e_prt_mac_link_speed {
+       I40E_PRT_MAC_LINK_SPEED_100MB = 0,
+       I40E_PRT_MAC_LINK_SPEED_1GB,
+       I40E_PRT_MAC_LINK_SPEED_10GB,
+       I40E_PRT_MAC_LINK_SPEED_40GB,
+       I40E_PRT_MAC_LINK_SPEED_20GB
+};
+
 struct i40e_link_status {
        enum i40e_aq_phy_type phy_type;
        enum i40e_aq_link_speed link_speed;

---
base-commit: e5235eb6cfe02a51256013a78f7b28779a7740d5
change-id: 20251119-i40e_ptp_link_down-47934f9e155d

Best regards,
-- 
Markus Blöchl <[email protected]>


-- 

Reply via email to