diff options
Diffstat (limited to 'drivers/net/ethernet/freescale/enetc/enetc_ethtool.c')
| -rw-r--r-- | drivers/net/ethernet/freescale/enetc/enetc_ethtool.c | 121 | 
1 files changed, 110 insertions, 11 deletions
diff --git a/drivers/net/ethernet/freescale/enetc/enetc_ethtool.c b/drivers/net/ethernet/freescale/enetc/enetc_ethtool.c index bca68edfbe9c..e993ed04ab57 100644 --- a/drivers/net/ethernet/freescale/enetc/enetc_ethtool.c +++ b/drivers/net/ethernet/freescale/enetc/enetc_ethtool.c @@ -32,6 +32,12 @@ static const u32 enetc_port_regs[] = {  	ENETC_PM0_CMD_CFG, ENETC_PM0_MAXFRM, ENETC_PM0_IF_MODE  }; +static const u32 enetc_port_mm_regs[] = { +	ENETC_MMCSR, ENETC_PFPMR, ENETC_PTCFPR(0), ENETC_PTCFPR(1), +	ENETC_PTCFPR(2), ENETC_PTCFPR(3), ENETC_PTCFPR(4), ENETC_PTCFPR(5), +	ENETC_PTCFPR(6), ENETC_PTCFPR(7), +}; +  static int enetc_get_reglen(struct net_device *ndev)  {  	struct enetc_ndev_priv *priv = netdev_priv(ndev); @@ -45,6 +51,9 @@ static int enetc_get_reglen(struct net_device *ndev)  	if (hw->port)  		len += ARRAY_SIZE(enetc_port_regs); +	if (hw->port && !!(priv->si->hw_features & ENETC_SI_F_QBU)) +		len += ARRAY_SIZE(enetc_port_mm_regs); +  	len *= sizeof(u32) * 2; /* store 2 entries per reg: addr and value */  	return len; @@ -90,6 +99,14 @@ static void enetc_get_regs(struct net_device *ndev, struct ethtool_regs *regs,  		*buf++ = addr;  		*buf++ = enetc_rd(hw, addr);  	} + +	if (priv->si->hw_features & ENETC_SI_F_QBU) { +		for (i = 0; i < ARRAY_SIZE(enetc_port_mm_regs); i++) { +			addr = ENETC_PORT_BASE + enetc_port_mm_regs[i]; +			*buf++ = addr; +			*buf++ = enetc_rd(hw, addr); +		} +	}  }  static const struct { @@ -370,8 +387,7 @@ static const struct ethtool_rmon_hist_range enetc_rmon_ranges[] = {  };  static void enetc_rmon_stats(struct enetc_hw *hw, int mac, -			     struct ethtool_rmon_stats *s, -			     const struct ethtool_rmon_hist_range **ranges) +			     struct ethtool_rmon_stats *s)  {  	s->undersize_pkts = enetc_port_rd(hw, ENETC_PM_RUND(mac));  	s->oversize_pkts = enetc_port_rd(hw, ENETC_PM_ROVR(mac)); @@ -393,8 +409,6 @@ static void enetc_rmon_stats(struct enetc_hw *hw, int mac,  	s->hist_tx[4] = enetc_port_rd(hw, ENETC_PM_T1023(mac));  	s->hist_tx[5] = enetc_port_rd(hw, ENETC_PM_T1522(mac));  	s->hist_tx[6] = enetc_port_rd(hw, ENETC_PM_T1523X(mac)); - -	*ranges = enetc_rmon_ranges;  }  static void enetc_get_eth_mac_stats(struct net_device *ndev, @@ -447,13 +461,15 @@ static void enetc_get_rmon_stats(struct net_device *ndev,  	struct enetc_hw *hw = &priv->si->hw;  	struct enetc_si *si = priv->si; +	*ranges = enetc_rmon_ranges; +  	switch (rmon_stats->src) {  	case ETHTOOL_MAC_STATS_SRC_EMAC: -		enetc_rmon_stats(hw, 0, rmon_stats, ranges); +		enetc_rmon_stats(hw, 0, rmon_stats);  		break;  	case ETHTOOL_MAC_STATS_SRC_PMAC:  		if (si->hw_features & ENETC_SI_F_QBU) -			enetc_rmon_stats(hw, 1, rmon_stats, ranges); +			enetc_rmon_stats(hw, 1, rmon_stats);  		break;  	case ETHTOOL_MAC_STATS_SRC_AGGREGATE:  		ethtool_aggregate_rmon_stats(ndev, rmon_stats); @@ -977,7 +993,9 @@ static int enetc_get_mm(struct net_device *ndev, struct ethtool_mm_state *state)  	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->tx_active = state->tx_enabled && +			   (state->verify_status == ETHTOOL_MM_VERIFY_STATUS_SUCCEEDED || +			    state->verify_status == ETHTOOL_MM_VERIFY_STATUS_DISABLED);  	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 @@ -990,6 +1008,78 @@ static int enetc_get_mm(struct net_device *ndev, struct ethtool_mm_state *state)  	return 0;  } +static int enetc_mm_wait_tx_active(struct enetc_hw *hw, int verify_time) +{ +	int timeout = verify_time * USEC_PER_MSEC * ENETC_MM_VERIFY_RETRIES; +	u32 val; + +	/* This will time out after the standard value of 3 verification +	 * attempts. To not sleep forever, it relies on a non-zero verify_time, +	 * guarantee which is provided by the ethtool nlattr policy. +	 */ +	return read_poll_timeout(enetc_port_rd, val, +				 ENETC_MMCSR_GET_VSTS(val) == 3, +				 ENETC_MM_VERIFY_SLEEP_US, timeout, +				 true, hw, ENETC_MMCSR); +} + +static void enetc_set_ptcfpr(struct enetc_hw *hw, u8 preemptible_tcs) +{ +	u32 val; +	int tc; + +	for (tc = 0; tc < 8; tc++) { +		val = enetc_port_rd(hw, ENETC_PTCFPR(tc)); + +		if (preemptible_tcs & BIT(tc)) +			val |= ENETC_PTCFPR_FPE; +		else +			val &= ~ENETC_PTCFPR_FPE; + +		enetc_port_wr(hw, ENETC_PTCFPR(tc), val); +	} +} + +/* ENETC does not have an IRQ to notify changes to the MAC Merge TX status + * (active/inactive), but the preemptible traffic classes should only be + * committed to hardware once TX is active. Resort to polling. + */ +void enetc_mm_commit_preemptible_tcs(struct enetc_ndev_priv *priv) +{ +	struct enetc_hw *hw = &priv->si->hw; +	u8 preemptible_tcs = 0; +	u32 val; +	int err; + +	val = enetc_port_rd(hw, ENETC_MMCSR); +	if (!(val & ENETC_MMCSR_ME)) +		goto out; + +	if (!(val & ENETC_MMCSR_VDIS)) { +		err = enetc_mm_wait_tx_active(hw, ENETC_MMCSR_GET_VT(val)); +		if (err) +			goto out; +	} + +	preemptible_tcs = priv->preemptible_tcs; +out: +	enetc_set_ptcfpr(hw, preemptible_tcs); +} + +/* FIXME: Workaround for the link partner's verification failing if ENETC + * priorly received too much express traffic. The documentation doesn't + * suggest this is needed. + */ +static void enetc_restart_emac_rx(struct enetc_si *si) +{ +	u32 val = enetc_port_rd(&si->hw, ENETC_PM0_CMD_CFG); + +	enetc_port_wr(&si->hw, ENETC_PM0_CMD_CFG, val & ~ENETC_PM0_RX_EN); + +	if (val & ENETC_PM0_RX_EN) +		enetc_port_wr(&si->hw, ENETC_PM0_CMD_CFG, val); +} +  static int enetc_set_mm(struct net_device *ndev, struct ethtool_mm_cfg *cfg,  			struct netlink_ext_ack *extack)  { @@ -1028,10 +1118,13 @@ static int enetc_set_mm(struct net_device *ndev, struct ethtool_mm_cfg *cfg,  	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; +	/* If link is up, enable/disable MAC Merge right away */ +	if (!(val & ENETC_MMCSR_LINK_FAIL)) { +		if (!!(priv->active_offloads & ENETC_F_QBU)) +			val |= ENETC_MMCSR_ME; +		else +			val &= ~ENETC_MMCSR_ME; +	}  	val &= ~ENETC_MMCSR_VT_MASK;  	val |= ENETC_MMCSR_VT(cfg->verify_time); @@ -1041,6 +1134,10 @@ static int enetc_set_mm(struct net_device *ndev, struct ethtool_mm_cfg *cfg,  	enetc_port_wr(hw, ENETC_MMCSR, val); +	enetc_restart_emac_rx(priv->si); + +	enetc_mm_commit_preemptible_tcs(priv); +  	mutex_unlock(&priv->mm_lock);  	return 0; @@ -1074,6 +1171,8 @@ void enetc_mm_link_state_update(struct enetc_ndev_priv *priv, bool link)  	enetc_port_wr(hw, ENETC_MMCSR, val); +	enetc_mm_commit_preemptible_tcs(priv); +  	mutex_unlock(&priv->mm_lock);  }  EXPORT_SYMBOL_GPL(enetc_mm_link_state_update);  |