diff options
Diffstat (limited to 'drivers/net/ethernet')
-rw-r--r-- | drivers/net/ethernet/freescale/enetc/enetc.h | 8 | ||||
-rw-r--r-- | drivers/net/ethernet/freescale/enetc/enetc_ethtool.c | 144 | ||||
-rw-r--r-- | drivers/net/ethernet/freescale/enetc/enetc_hw.h | 19 | ||||
-rw-r--r-- | drivers/net/ethernet/freescale/enetc/enetc_pf.c | 14 |
4 files changed, 182 insertions, 3 deletions
diff --git a/drivers/net/ethernet/freescale/enetc/enetc.h b/drivers/net/ethernet/freescale/enetc/enetc.h index e21d096c5a90..8010f31cd10d 100644 --- a/drivers/net/ethernet/freescale/enetc/enetc.h +++ b/drivers/net/ethernet/freescale/enetc/enetc.h @@ -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, diff --git a/drivers/net/ethernet/freescale/enetc/enetc_ethtool.c b/drivers/net/ethernet/freescale/enetc/enetc_ethtool.c index 05e2bad609c6..078259466833 100644 --- a/drivers/net/ethernet/freescale/enetc/enetc_ethtool.c +++ b/drivers/net/ethernet/freescale/enetc/enetc_ethtool.c @@ -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 = { diff --git a/drivers/net/ethernet/freescale/enetc/enetc_hw.h b/drivers/net/ethernet/freescale/enetc/enetc_hw.h index 041df7d0ae81..7b93d09436c4 100644 --- a/drivers/net/ethernet/freescale/enetc/enetc_hw.h +++ b/drivers/net/ethernet/freescale/enetc/enetc_hw.h @@ -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 diff --git a/drivers/net/ethernet/freescale/enetc/enetc_pf.c b/drivers/net/ethernet/freescale/enetc/enetc_pf.c index 7facc7d5261e..97056dc3496d 100644 --- a/drivers/net/ethernet/freescale/enetc/enetc_pf.c +++ b/drivers/net/ethernet/freescale/enetc/enetc_pf.c @@ -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); |