net: enetc: add support for MAC Merge layer
authorVladimir Oltean <vladimir.oltean@nxp.com>
Mon, 6 Feb 2023 09:45:30 +0000 (11:45 +0200)
committerJakub Kicinski <kuba@kernel.org>
Wed, 8 Feb 2023 04:13:55 +0000 (20:13 -0800)
Add PF driver support for viewing and changing the MAC Merge sublayer
parameters, and seeing the verification state machine's current state.
The verification handshake with the link partner is driven by hardware.

Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Link: https://lore.kernel.org/r/20230206094531.444988-1-vladimir.oltean@nxp.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
drivers/net/ethernet/freescale/enetc/enetc.h
drivers/net/ethernet/freescale/enetc/enetc_ethtool.c
drivers/net/ethernet/freescale/enetc/enetc_hw.h
drivers/net/ethernet/freescale/enetc/enetc_pf.c

index e21d096c5a9098424a4df07d2d6d4efcbc99180a..8010f31cd10daed72a97d1463dffcf710d8a6297 100644 (file)
@@ -314,7 +314,6 @@ struct psfp_cap {
 };
 
 #define ENETC_F_TX_TSTAMP_MASK 0xff
-/* TODO: more hardware offloads */
 enum enetc_active_offloads {
        /* 8 bits reserved for TX timestamp types (hwtstamp_tx_types) */
        ENETC_F_TX_TSTAMP               = BIT(0),
@@ -323,6 +322,7 @@ enum enetc_active_offloads {
        ENETC_F_RX_TSTAMP               = BIT(8),
        ENETC_F_QBV                     = BIT(9),
        ENETC_F_QCI                     = BIT(10),
+       ENETC_F_QBU                     = BIT(11),
 };
 
 enum enetc_flags_bit {
@@ -382,6 +382,11 @@ struct enetc_ndev_priv {
 
        struct work_struct      tx_onestep_tstamp;
        struct sk_buff_head     tx_skbs;
+
+       /* Serialize access to MAC Merge state between ethtool requests
+        * and link state updates
+        */
+       struct mutex            mm_lock;
 };
 
 /* Messaging */
@@ -427,6 +432,7 @@ int enetc_xdp_xmit(struct net_device *ndev, int num_frames,
 
 /* ethtool */
 void enetc_set_ethtool_ops(struct net_device *ndev);
+void enetc_mm_link_state_update(struct enetc_ndev_priv *priv, bool link);
 
 /* control buffer descriptor ring (CBDR) */
 int enetc_setup_cbdr(struct device *dev, struct enetc_hw *hw, int bd_count,
index 05e2bad609c605220d0461cad50871954cca820d..0782594668339abda727cd7679e85462c95c566c 100644 (file)
@@ -863,6 +863,148 @@ static int enetc_set_link_ksettings(struct net_device *dev,
        return phylink_ethtool_ksettings_set(priv->phylink, cmd);
 }
 
+static int enetc_get_mm(struct net_device *ndev, struct ethtool_mm_state *state)
+{
+       struct enetc_ndev_priv *priv = netdev_priv(ndev);
+       struct enetc_si *si = priv->si;
+       struct enetc_hw *hw = &si->hw;
+       u32 lafs, rafs, val;
+
+       if (!(si->hw_features & ENETC_SI_F_QBU))
+               return -EOPNOTSUPP;
+
+       mutex_lock(&priv->mm_lock);
+
+       val = enetc_port_rd(hw, ENETC_PFPMR);
+       state->pmac_enabled = !!(val & ENETC_PFPMR_PMACE);
+
+       val = enetc_port_rd(hw, ENETC_MMCSR);
+
+       switch (ENETC_MMCSR_GET_VSTS(val)) {
+       case 0:
+               state->verify_status = ETHTOOL_MM_VERIFY_STATUS_DISABLED;
+               break;
+       case 2:
+               state->verify_status = ETHTOOL_MM_VERIFY_STATUS_VERIFYING;
+               break;
+       case 3:
+               state->verify_status = ETHTOOL_MM_VERIFY_STATUS_SUCCEEDED;
+               break;
+       case 4:
+               state->verify_status = ETHTOOL_MM_VERIFY_STATUS_FAILED;
+               break;
+       case 5:
+       default:
+               state->verify_status = ETHTOOL_MM_VERIFY_STATUS_UNKNOWN;
+               break;
+       }
+
+       rafs = ENETC_MMCSR_GET_RAFS(val);
+       state->tx_min_frag_size = ethtool_mm_frag_size_add_to_min(rafs);
+       lafs = ENETC_MMCSR_GET_LAFS(val);
+       state->rx_min_frag_size = ethtool_mm_frag_size_add_to_min(lafs);
+       state->tx_enabled = !!(val & ENETC_MMCSR_LPE); /* mirror of MMCSR_ME */
+       state->tx_active = !!(val & ENETC_MMCSR_LPA);
+       state->verify_enabled = !(val & ENETC_MMCSR_VDIS);
+       state->verify_time = ENETC_MMCSR_GET_VT(val);
+       /* A verifyTime of 128 ms would exceed the 7 bit width
+        * of the ENETC_MMCSR_VT field
+        */
+       state->max_verify_time = 127;
+
+       mutex_unlock(&priv->mm_lock);
+
+       return 0;
+}
+
+static int enetc_set_mm(struct net_device *ndev, struct ethtool_mm_cfg *cfg,
+                       struct netlink_ext_ack *extack)
+{
+       struct enetc_ndev_priv *priv = netdev_priv(ndev);
+       struct enetc_hw *hw = &priv->si->hw;
+       struct enetc_si *si = priv->si;
+       u32 val, add_frag_size;
+       int err;
+
+       if (!(si->hw_features & ENETC_SI_F_QBU))
+               return -EOPNOTSUPP;
+
+       err = ethtool_mm_frag_size_min_to_add(cfg->tx_min_frag_size,
+                                             &add_frag_size, extack);
+       if (err)
+               return err;
+
+       mutex_lock(&priv->mm_lock);
+
+       val = enetc_port_rd(hw, ENETC_PFPMR);
+       if (cfg->pmac_enabled)
+               val |= ENETC_PFPMR_PMACE;
+       else
+               val &= ~ENETC_PFPMR_PMACE;
+       enetc_port_wr(hw, ENETC_PFPMR, val);
+
+       val = enetc_port_rd(hw, ENETC_MMCSR);
+
+       if (cfg->verify_enabled)
+               val &= ~ENETC_MMCSR_VDIS;
+       else
+               val |= ENETC_MMCSR_VDIS;
+
+       if (cfg->tx_enabled)
+               priv->active_offloads |= ENETC_F_QBU;
+       else
+               priv->active_offloads &= ~ENETC_F_QBU;
+
+       /* If link is up, enable MAC Merge right away */
+       if (!!(priv->active_offloads & ENETC_F_QBU) &&
+           !(val & ENETC_MMCSR_LINK_FAIL))
+               val |= ENETC_MMCSR_ME;
+
+       val &= ~ENETC_MMCSR_VT_MASK;
+       val |= ENETC_MMCSR_VT(cfg->verify_time);
+
+       val &= ~ENETC_MMCSR_RAFS_MASK;
+       val |= ENETC_MMCSR_RAFS(add_frag_size);
+
+       enetc_port_wr(hw, ENETC_MMCSR, val);
+
+       mutex_unlock(&priv->mm_lock);
+
+       return 0;
+}
+
+/* When the link is lost, the verification state machine goes to the FAILED
+ * state and doesn't restart on its own after a new link up event.
+ * According to 802.3 Figure 99-8 - Verify state diagram, the LINK_FAIL bit
+ * should have been sufficient to re-trigger verification, but for ENETC it
+ * doesn't. As a workaround, we need to toggle the Merge Enable bit to
+ * re-trigger verification when link comes up.
+ */
+void enetc_mm_link_state_update(struct enetc_ndev_priv *priv, bool link)
+{
+       struct enetc_hw *hw = &priv->si->hw;
+       u32 val;
+
+       mutex_lock(&priv->mm_lock);
+
+       val = enetc_port_rd(hw, ENETC_MMCSR);
+
+       if (link) {
+               val &= ~ENETC_MMCSR_LINK_FAIL;
+               if (priv->active_offloads & ENETC_F_QBU)
+                       val |= ENETC_MMCSR_ME;
+       } else {
+               val |= ENETC_MMCSR_LINK_FAIL;
+               if (priv->active_offloads & ENETC_F_QBU)
+                       val &= ~ENETC_MMCSR_ME;
+       }
+
+       enetc_port_wr(hw, ENETC_MMCSR, val);
+
+       mutex_unlock(&priv->mm_lock);
+}
+EXPORT_SYMBOL_GPL(enetc_mm_link_state_update);
+
 static const struct ethtool_ops enetc_pf_ethtool_ops = {
        .supported_coalesce_params = ETHTOOL_COALESCE_USECS |
                                     ETHTOOL_COALESCE_MAX_FRAMES |
@@ -893,6 +1035,8 @@ static const struct ethtool_ops enetc_pf_ethtool_ops = {
        .set_wol = enetc_set_wol,
        .get_pauseparam = enetc_get_pauseparam,
        .set_pauseparam = enetc_set_pauseparam,
+       .get_mm = enetc_get_mm,
+       .set_mm = enetc_set_mm,
 };
 
 static const struct ethtool_ops enetc_vf_ethtool_ops = {
index 041df7d0ae81913e05bd6c1c822513c71786eca2..7b93d09436c47230560ecd92bbc1932ecc37228b 100644 (file)
@@ -222,7 +222,24 @@ enum enetc_bdr_type {TX, RX};
 #define ENETC_PSIVHFR0(n)      (0x1e00 + (n) * 8) /* n = SI index */
 #define ENETC_PSIVHFR1(n)      (0x1e04 + (n) * 8) /* n = SI index */
 #define ENETC_MMCSR            0x1f00
-#define ENETC_MMCSR_ME         BIT(16)
+#define ENETC_MMCSR_LINK_FAIL  BIT(31)
+#define ENETC_MMCSR_VT_MASK    GENMASK(29, 23) /* Verify Time */
+#define ENETC_MMCSR_VT(x)      (((x) << 23) & ENETC_MMCSR_VT_MASK)
+#define ENETC_MMCSR_GET_VT(x)  (((x) & ENETC_MMCSR_VT_MASK) >> 23)
+#define ENETC_MMCSR_TXSTS_MASK GENMASK(22, 21) /* Merge Status */
+#define ENETC_MMCSR_GET_TXSTS(x) (((x) & ENETC_MMCSR_TXSTS_MASK) >> 21)
+#define ENETC_MMCSR_VSTS_MASK  GENMASK(20, 18) /* Verify Status */
+#define ENETC_MMCSR_GET_VSTS(x) (((x) & ENETC_MMCSR_VSTS_MASK) >> 18)
+#define ENETC_MMCSR_VDIS       BIT(17) /* Verify Disabled */
+#define ENETC_MMCSR_ME         BIT(16) /* Merge Enabled */
+#define ENETC_MMCSR_RAFS_MASK  GENMASK(9, 8) /* Remote Additional Fragment Size */
+#define ENETC_MMCSR_RAFS(x)    (((x) << 8) & ENETC_MMCSR_RAFS_MASK)
+#define ENETC_MMCSR_GET_RAFS(x)        (((x) & ENETC_MMCSR_RAFS_MASK) >> 8)
+#define ENETC_MMCSR_LAFS_MASK  GENMASK(4, 3) /* Local Additional Fragment Size */
+#define ENETC_MMCSR_GET_LAFS(x)        (((x) & ENETC_MMCSR_LAFS_MASK) >> 3)
+#define ENETC_MMCSR_LPA                BIT(2) /* Local Preemption Active */
+#define ENETC_MMCSR_LPE                BIT(1) /* Local Preemption Enabled */
+#define ENETC_MMCSR_LPS                BIT(0) /* Local Preemption Supported */
 #define ENETC_PTCMSDUR(n)      (0x2020 + (n) * 4) /* n = TC index [0..7] */
 
 #define ENETC_PMAC_OFFSET      0x1000
index 7facc7d5261e1c1f9080298d25d71a74760d2267..97056dc3496d6de5d6ba8522dc60276ef54e5c3d 100644 (file)
@@ -1083,6 +1083,9 @@ static void enetc_pl_mac_link_up(struct phylink_config *config,
        enetc_port_mac_wr(si, ENETC_PM0_CMD_CFG, cmd_cfg);
 
        enetc_mac_enable(si, true);
+
+       if (si->hw_features & ENETC_SI_F_QBU)
+               enetc_mm_link_state_update(priv, true);
 }
 
 static void enetc_pl_mac_link_down(struct phylink_config *config,
@@ -1090,8 +1093,15 @@ static void enetc_pl_mac_link_down(struct phylink_config *config,
                                   phy_interface_t interface)
 {
        struct enetc_pf *pf = phylink_to_enetc_pf(config);
+       struct enetc_si *si = pf->si;
+       struct enetc_ndev_priv *priv;
 
-       enetc_mac_enable(pf->si, false);
+       priv = netdev_priv(si->ndev);
+
+       if (si->hw_features & ENETC_SI_F_QBU)
+               enetc_mm_link_state_update(priv, false);
+
+       enetc_mac_enable(si, false);
 }
 
 static const struct phylink_mac_ops enetc_mac_phylink_ops = {
@@ -1284,6 +1294,8 @@ static int enetc_pf_probe(struct pci_dev *pdev,
 
        priv = netdev_priv(ndev);
 
+       mutex_init(&priv->mm_lock);
+
        enetc_init_si_rings_params(priv);
 
        err = enetc_alloc_si_resources(priv);