Commit 8a00946e authored by David S. Miller's avatar David S. Miller
Browse files

Merge branch 'enetc-fixes'



Vladimir Oltean says:

====================
Fixes for NXP ENETC driver

This contains an assorted set of fixes collected over the past 2 weeks
on the enetc driver. Some are related to VLAN processing, some to
physical link settings, some are fixups of previous hardware workarounds,
and some are simply zero-day data path bugs that for some reason were
never caught or at least identified.
====================

Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 8bd2a055 3a5d12c9
Loading
Loading
Loading
Loading
+44 −43
Original line number Diff line number Diff line
@@ -281,6 +281,8 @@ static int enetc_poll(struct napi_struct *napi, int budget)
	int work_done;
	int i;

	enetc_lock_mdio();

	for (i = 0; i < v->count_tx_rings; i++)
		if (!enetc_clean_tx_ring(&v->tx_ring[i], budget))
			complete = false;
@@ -291,8 +293,10 @@ static int enetc_poll(struct napi_struct *napi, int budget)
	if (work_done)
		v->rx_napi_work = true;

	if (!complete)
	if (!complete) {
		enetc_unlock_mdio();
		return budget;
	}

	napi_complete_done(napi, work_done);

@@ -301,8 +305,6 @@ static int enetc_poll(struct napi_struct *napi, int budget)

	v->rx_napi_work = false;

	enetc_lock_mdio();

	/* enable interrupts */
	enetc_wr_reg_hot(v->rbier, ENETC_RBIER_RXTIE);

@@ -327,8 +329,8 @@ static void enetc_get_tx_tstamp(struct enetc_hw *hw, union enetc_tx_bd *txbd,
{
	u32 lo, hi, tstamp_lo;

	lo = enetc_rd(hw, ENETC_SICTR0);
	hi = enetc_rd(hw, ENETC_SICTR1);
	lo = enetc_rd_hot(hw, ENETC_SICTR0);
	hi = enetc_rd_hot(hw, ENETC_SICTR1);
	tstamp_lo = le32_to_cpu(txbd->wb.tstamp);
	if (lo <= tstamp_lo)
		hi -= 1;
@@ -358,9 +360,7 @@ static bool enetc_clean_tx_ring(struct enetc_bdr *tx_ring, int napi_budget)
	i = tx_ring->next_to_clean;
	tx_swbd = &tx_ring->tx_swbd[i];

	enetc_lock_mdio();
	bds_to_clean = enetc_bd_ready_count(tx_ring, i);
	enetc_unlock_mdio();

	do_tstamp = false;

@@ -403,8 +403,6 @@ static bool enetc_clean_tx_ring(struct enetc_bdr *tx_ring, int napi_budget)
			tx_swbd = tx_ring->tx_swbd;
		}

		enetc_lock_mdio();

		/* BD iteration loop end */
		if (is_eof) {
			tx_frm_cnt++;
@@ -415,8 +413,6 @@ static bool enetc_clean_tx_ring(struct enetc_bdr *tx_ring, int napi_budget)

		if (unlikely(!bds_to_clean))
			bds_to_clean = enetc_bd_ready_count(tx_ring, i);

		enetc_unlock_mdio();
	}

	tx_ring->next_to_clean = i;
@@ -527,9 +523,8 @@ static void enetc_get_rx_tstamp(struct net_device *ndev,
static void enetc_get_offloads(struct enetc_bdr *rx_ring,
			       union enetc_rx_bd *rxbd, struct sk_buff *skb)
{
#ifdef CONFIG_FSL_ENETC_PTP_CLOCK
	struct enetc_ndev_priv *priv = netdev_priv(rx_ring->ndev);
#endif

	/* TODO: hashing */
	if (rx_ring->ndev->features & NETIF_F_RXCSUM) {
		u16 inet_csum = le16_to_cpu(rxbd->r.inet_csum);
@@ -538,12 +533,31 @@ static void enetc_get_offloads(struct enetc_bdr *rx_ring,
		skb->ip_summed = CHECKSUM_COMPLETE;
	}

	/* copy VLAN to skb, if one is extracted, for now we assume it's a
	 * standard TPID, but HW also supports custom values
	 */
	if (le16_to_cpu(rxbd->r.flags) & ENETC_RXBD_FLAG_VLAN)
		__vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q),
				       le16_to_cpu(rxbd->r.vlan_opt));
	if (le16_to_cpu(rxbd->r.flags) & ENETC_RXBD_FLAG_VLAN) {
		__be16 tpid = 0;

		switch (le16_to_cpu(rxbd->r.flags) & ENETC_RXBD_FLAG_TPID) {
		case 0:
			tpid = htons(ETH_P_8021Q);
			break;
		case 1:
			tpid = htons(ETH_P_8021AD);
			break;
		case 2:
			tpid = htons(enetc_port_rd(&priv->si->hw,
						   ENETC_PCVLANR1));
			break;
		case 3:
			tpid = htons(enetc_port_rd(&priv->si->hw,
						   ENETC_PCVLANR2));
			break;
		default:
			break;
		}

		__vlan_hwaccel_put_tag(skb, tpid, le16_to_cpu(rxbd->r.vlan_opt));
	}

#ifdef CONFIG_FSL_ENETC_PTP_CLOCK
	if (priv->active_offloads & ENETC_F_RX_TSTAMP)
		enetc_get_rx_tstamp(rx_ring->ndev, rxbd, skb);
@@ -660,8 +674,6 @@ static int enetc_clean_rx_ring(struct enetc_bdr *rx_ring,
		u32 bd_status;
		u16 size;

		enetc_lock_mdio();

		if (cleaned_cnt >= ENETC_RXBD_BUNDLE) {
			int count = enetc_refill_rx_ring(rx_ring, cleaned_cnt);

@@ -672,19 +684,15 @@ static int enetc_clean_rx_ring(struct enetc_bdr *rx_ring,

		rxbd = enetc_rxbd(rx_ring, i);
		bd_status = le32_to_cpu(rxbd->r.lstatus);
		if (!bd_status) {
			enetc_unlock_mdio();
		if (!bd_status)
			break;
		}

		enetc_wr_reg_hot(rx_ring->idr, BIT(rx_ring->index));
		dma_rmb(); /* for reading other rxbd fields */
		size = le16_to_cpu(rxbd->r.buf_len);
		skb = enetc_map_rx_buff_to_skb(rx_ring, i, size);
		if (!skb) {
			enetc_unlock_mdio();
		if (!skb)
			break;
		}

		enetc_get_offloads(rx_ring, rxbd, skb);

@@ -696,7 +704,6 @@ static int enetc_clean_rx_ring(struct enetc_bdr *rx_ring,

		if (unlikely(bd_status &
			     ENETC_RXBD_LSTATUS(ENETC_RXBD_ERR_MASK))) {
			enetc_unlock_mdio();
			dev_kfree_skb(skb);
			while (!(bd_status & ENETC_RXBD_LSTATUS_F)) {
				dma_rmb();
@@ -736,8 +743,6 @@ static int enetc_clean_rx_ring(struct enetc_bdr *rx_ring,

		enetc_process_skb(rx_ring, skb);

		enetc_unlock_mdio();

		napi_gro_receive(napi, skb);

		rx_frm_cnt++;
@@ -984,7 +989,7 @@ static void enetc_free_rxtx_rings(struct enetc_ndev_priv *priv)
		enetc_free_tx_ring(priv->tx_ring[i]);
}

static int enetc_alloc_cbdr(struct device *dev, struct enetc_cbdr *cbdr)
int enetc_alloc_cbdr(struct device *dev, struct enetc_cbdr *cbdr)
{
	int size = cbdr->bd_count * sizeof(struct enetc_cbd);

@@ -1005,7 +1010,7 @@ static int enetc_alloc_cbdr(struct device *dev, struct enetc_cbdr *cbdr)
	return 0;
}

static void enetc_free_cbdr(struct device *dev, struct enetc_cbdr *cbdr)
void enetc_free_cbdr(struct device *dev, struct enetc_cbdr *cbdr)
{
	int size = cbdr->bd_count * sizeof(struct enetc_cbd);

@@ -1013,7 +1018,7 @@ static void enetc_free_cbdr(struct device *dev, struct enetc_cbdr *cbdr)
	cbdr->bd_base = NULL;
}

static void enetc_setup_cbdr(struct enetc_hw *hw, struct enetc_cbdr *cbdr)
void enetc_setup_cbdr(struct enetc_hw *hw, struct enetc_cbdr *cbdr)
{
	/* set CBDR cache attributes */
	enetc_wr(hw, ENETC_SICAR2,
@@ -1033,7 +1038,7 @@ static void enetc_setup_cbdr(struct enetc_hw *hw, struct enetc_cbdr *cbdr)
	cbdr->cir = hw->reg + ENETC_SICBDRCIR;
}

static void enetc_clear_cbdr(struct enetc_hw *hw)
void enetc_clear_cbdr(struct enetc_hw *hw)
{
	enetc_wr(hw, ENETC_SICBDRMR, 0);
}
@@ -1058,13 +1063,12 @@ static int enetc_setup_default_rss_table(struct enetc_si *si, int num_groups)
	return 0;
}

static int enetc_configure_si(struct enetc_ndev_priv *priv)
int enetc_configure_si(struct enetc_ndev_priv *priv)
{
	struct enetc_si *si = priv->si;
	struct enetc_hw *hw = &si->hw;
	int err;

	enetc_setup_cbdr(hw, &si->cbd_ring);
	/* set SI cache attributes */
	enetc_wr(hw, ENETC_SICAR0,
		 ENETC_SICAR_RD_COHERENT | ENETC_SICAR_WR_COHERENT);
@@ -1112,6 +1116,8 @@ int enetc_alloc_si_resources(struct enetc_ndev_priv *priv)
	if (err)
		return err;

	enetc_setup_cbdr(&si->hw, &si->cbd_ring);

	priv->cls_rules = kcalloc(si->num_fs_entries, sizeof(*priv->cls_rules),
				  GFP_KERNEL);
	if (!priv->cls_rules) {
@@ -1119,14 +1125,8 @@ int enetc_alloc_si_resources(struct enetc_ndev_priv *priv)
		goto err_alloc_cls;
	}

	err = enetc_configure_si(priv);
	if (err)
		goto err_config_si;

	return 0;

err_config_si:
	kfree(priv->cls_rules);
err_alloc_cls:
	enetc_clear_cbdr(&si->hw);
	enetc_free_cbdr(priv->dev, &si->cbd_ring);
@@ -1212,7 +1212,8 @@ static void enetc_setup_rxbdr(struct enetc_hw *hw, struct enetc_bdr *rx_ring)
	rx_ring->idr = hw->reg + ENETC_SIRXIDR;

	enetc_refill_rx_ring(rx_ring, enetc_bd_unused(rx_ring));
	enetc_wr(hw, ENETC_SIRXIDR, rx_ring->next_to_use);
	/* update ENETC's consumer index */
	enetc_rxbdr_wr(hw, idx, ENETC_RBCIR, rx_ring->next_to_use);

	/* enable ring */
	enetc_rxbdr_wr(hw, idx, ENETC_RBMR, rbmr);
+5 −0
Original line number Diff line number Diff line
@@ -292,6 +292,7 @@ void enetc_get_si_caps(struct enetc_si *si);
void enetc_init_si_rings_params(struct enetc_ndev_priv *priv);
int enetc_alloc_si_resources(struct enetc_ndev_priv *priv);
void enetc_free_si_resources(struct enetc_ndev_priv *priv);
int enetc_configure_si(struct enetc_ndev_priv *priv);

int enetc_open(struct net_device *ndev);
int enetc_close(struct net_device *ndev);
@@ -309,6 +310,10 @@ int enetc_setup_tc(struct net_device *ndev, enum tc_setup_type type,
void enetc_set_ethtool_ops(struct net_device *ndev);

/* control buffer descriptor ring (CBDR) */
int enetc_alloc_cbdr(struct device *dev, struct enetc_cbdr *cbdr);
void enetc_free_cbdr(struct device *dev, struct enetc_cbdr *cbdr);
void enetc_setup_cbdr(struct enetc_hw *hw, struct enetc_cbdr *cbdr);
void enetc_clear_cbdr(struct enetc_hw *hw);
int enetc_set_mac_flt_entry(struct enetc_si *si, int index,
			    char *mac_addr, int si_map);
int enetc_clear_mac_flt_entry(struct enetc_si *si, int index);
+15 −3
Original line number Diff line number Diff line
@@ -172,6 +172,8 @@ enum enetc_bdr_type {TX, RX};
#define ENETC_PSIPMAR0(n)	(0x0100 + (n) * 0x8) /* n = SI index */
#define ENETC_PSIPMAR1(n)	(0x0104 + (n) * 0x8)
#define ENETC_PVCLCTR		0x0208
#define ENETC_PCVLANR1		0x0210
#define ENETC_PCVLANR2		0x0214
#define ENETC_VLAN_TYPE_C	BIT(0)
#define ENETC_VLAN_TYPE_S	BIT(1)
#define ENETC_PVCLCTR_OVTPIDL(bmp)	((bmp) & 0xff) /* VLAN_TYPE */
@@ -236,10 +238,17 @@ enum enetc_bdr_type {TX, RX};
#define ENETC_PM_IMDIO_BASE	0x8030

#define ENETC_PM0_IF_MODE	0x8300
#define ENETC_PMO_IFM_RG	BIT(2)
#define ENETC_PM0_IFM_RG	BIT(2)
#define ENETC_PM0_IFM_RLP	(BIT(5) | BIT(11))
#define ENETC_PM0_IFM_RGAUTO	(BIT(15) | ENETC_PMO_IFM_RG | BIT(1))
#define ENETC_PM0_IFM_XGMII	BIT(12)
#define ENETC_PM0_IFM_EN_AUTO	BIT(15)
#define ENETC_PM0_IFM_SSP_MASK	GENMASK(14, 13)
#define ENETC_PM0_IFM_SSP_1000	(2 << 13)
#define ENETC_PM0_IFM_SSP_100	(0 << 13)
#define ENETC_PM0_IFM_SSP_10	(1 << 13)
#define ENETC_PM0_IFM_FULL_DPX	BIT(12)
#define ENETC_PM0_IFM_IFMODE_MASK GENMASK(1, 0)
#define ENETC_PM0_IFM_IFMODE_XGMII 0
#define ENETC_PM0_IFM_IFMODE_GMII 2
#define ENETC_PSIDCAPR		0x1b08
#define ENETC_PSIDCAPR_MSK	GENMASK(15, 0)
#define ENETC_PSFCAPR		0x1b18
@@ -453,6 +462,8 @@ static inline u64 _enetc_rd_reg64_wa(void __iomem *reg)
#define enetc_wr_reg(reg, val)		_enetc_wr_reg_wa((reg), (val))
#define enetc_rd(hw, off)		enetc_rd_reg((hw)->reg + (off))
#define enetc_wr(hw, off, val)		enetc_wr_reg((hw)->reg + (off), val)
#define enetc_rd_hot(hw, off)		enetc_rd_reg_hot((hw)->reg + (off))
#define enetc_wr_hot(hw, off, val)	enetc_wr_reg_hot((hw)->reg + (off), val)
#define enetc_rd64(hw, off)		_enetc_rd_reg64_wa((hw)->reg + (off))
/* port register accessors - PF only */
#define enetc_port_rd(hw, off)		enetc_rd_reg((hw)->port + (off))
@@ -568,6 +579,7 @@ union enetc_rx_bd {
#define ENETC_RXBD_LSTATUS(flags)	((flags) << 16)
#define ENETC_RXBD_FLAG_VLAN	BIT(9)
#define ENETC_RXBD_FLAG_TSTMP	BIT(10)
#define ENETC_RXBD_FLAG_TPID	GENMASK(1, 0)

#define ENETC_MAC_ADDR_FILT_CNT	8 /* # of supported entries per port */
#define EMETC_MAC_ADDR_FILT_RES	3 /* # of reserved entries at the beginning */
+81 −17
Original line number Diff line number Diff line
@@ -190,7 +190,6 @@ static void enetc_pf_set_rx_mode(struct net_device *ndev)
{
	struct enetc_ndev_priv *priv = netdev_priv(ndev);
	struct enetc_pf *pf = enetc_si_priv(priv->si);
	char vlan_promisc_simap = pf->vlan_promisc_simap;
	struct enetc_hw *hw = &priv->si->hw;
	bool uprom = false, mprom = false;
	struct enetc_mac_filter *filter;
@@ -203,16 +202,12 @@ static void enetc_pf_set_rx_mode(struct net_device *ndev)
		psipmr = ENETC_PSIPMR_SET_UP(0) | ENETC_PSIPMR_SET_MP(0);
		uprom = true;
		mprom = true;
		/* Enable VLAN promiscuous mode for SI0 (PF) */
		vlan_promisc_simap |= BIT(0);
	} else if (ndev->flags & IFF_ALLMULTI) {
		/* enable multi cast promisc mode for SI0 (PF) */
		psipmr = ENETC_PSIPMR_SET_MP(0);
		mprom = true;
	}

	enetc_set_vlan_promisc(&pf->si->hw, vlan_promisc_simap);

	/* first 2 filter entries belong to PF */
	if (!uprom) {
		/* Update unicast filters */
@@ -320,7 +315,7 @@ static void enetc_set_loopback(struct net_device *ndev, bool en)
	u32 reg;

	reg = enetc_port_rd(hw, ENETC_PM0_IF_MODE);
	if (reg & ENETC_PMO_IFM_RG) {
	if (reg & ENETC_PM0_IFM_RG) {
		/* RGMII mode */
		reg = (reg & ~ENETC_PM0_IFM_RLP) |
		      (en ? ENETC_PM0_IFM_RLP : 0);
@@ -499,13 +494,20 @@ static void enetc_configure_port_mac(struct enetc_hw *hw)

static void enetc_mac_config(struct enetc_hw *hw, phy_interface_t phy_mode)
{
	/* set auto-speed for RGMII */
	if (enetc_port_rd(hw, ENETC_PM0_IF_MODE) & ENETC_PMO_IFM_RG ||
	    phy_interface_mode_is_rgmii(phy_mode))
		enetc_port_wr(hw, ENETC_PM0_IF_MODE, ENETC_PM0_IFM_RGAUTO);
	u32 val;

	if (phy_mode == PHY_INTERFACE_MODE_USXGMII)
		enetc_port_wr(hw, ENETC_PM0_IF_MODE, ENETC_PM0_IFM_XGMII);
	if (phy_interface_mode_is_rgmii(phy_mode)) {
		val = enetc_port_rd(hw, ENETC_PM0_IF_MODE);
		val &= ~ENETC_PM0_IFM_EN_AUTO;
		val &= ENETC_PM0_IFM_IFMODE_MASK;
		val |= ENETC_PM0_IFM_IFMODE_GMII | ENETC_PM0_IFM_RG;
		enetc_port_wr(hw, ENETC_PM0_IF_MODE, val);
	}

	if (phy_mode == PHY_INTERFACE_MODE_USXGMII) {
		val = ENETC_PM0_IFM_FULL_DPX | ENETC_PM0_IFM_IFMODE_XGMII;
		enetc_port_wr(hw, ENETC_PM0_IF_MODE, val);
	}
}

static void enetc_mac_enable(struct enetc_hw *hw, bool en)
@@ -937,6 +939,34 @@ static void enetc_pl_mac_config(struct phylink_config *config,
		phylink_set_pcs(priv->phylink, &pf->pcs->pcs);
}

static void enetc_force_rgmii_mac(struct enetc_hw *hw, int speed, int duplex)
{
	u32 old_val, val;

	old_val = val = enetc_port_rd(hw, ENETC_PM0_IF_MODE);

	if (speed == SPEED_1000) {
		val &= ~ENETC_PM0_IFM_SSP_MASK;
		val |= ENETC_PM0_IFM_SSP_1000;
	} else if (speed == SPEED_100) {
		val &= ~ENETC_PM0_IFM_SSP_MASK;
		val |= ENETC_PM0_IFM_SSP_100;
	} else if (speed == SPEED_10) {
		val &= ~ENETC_PM0_IFM_SSP_MASK;
		val |= ENETC_PM0_IFM_SSP_10;
	}

	if (duplex == DUPLEX_FULL)
		val |= ENETC_PM0_IFM_FULL_DPX;
	else
		val &= ~ENETC_PM0_IFM_FULL_DPX;

	if (val == old_val)
		return;

	enetc_port_wr(hw, ENETC_PM0_IF_MODE, val);
}

static void enetc_pl_mac_link_up(struct phylink_config *config,
				 struct phy_device *phy, unsigned int mode,
				 phy_interface_t interface, int speed,
@@ -949,6 +979,10 @@ static void enetc_pl_mac_link_up(struct phylink_config *config,
	if (priv->active_offloads & ENETC_F_QBV)
		enetc_sched_speed_set(priv, speed);

	if (!phylink_autoneg_inband(mode) &&
	    phy_interface_mode_is_rgmii(interface))
		enetc_force_rgmii_mac(&pf->si->hw, speed, duplex);

	enetc_mac_enable(&pf->si->hw, true);
}

@@ -1041,6 +1075,26 @@ static int enetc_init_port_rss_memory(struct enetc_si *si)
	return err;
}

static void enetc_init_unused_port(struct enetc_si *si)
{
	struct device *dev = &si->pdev->dev;
	struct enetc_hw *hw = &si->hw;
	int err;

	si->cbd_ring.bd_count = ENETC_CBDR_DEFAULT_SIZE;
	err = enetc_alloc_cbdr(dev, &si->cbd_ring);
	if (err)
		return;

	enetc_setup_cbdr(hw, &si->cbd_ring);

	enetc_init_port_rfs_memory(si);
	enetc_init_port_rss_memory(si);

	enetc_clear_cbdr(hw);
	enetc_free_cbdr(dev, &si->cbd_ring);
}

static int enetc_pf_probe(struct pci_dev *pdev,
			  const struct pci_device_id *ent)
{
@@ -1051,11 +1105,6 @@ static int enetc_pf_probe(struct pci_dev *pdev,
	struct enetc_pf *pf;
	int err;

	if (node && !of_device_is_available(node)) {
		dev_info(&pdev->dev, "device is disabled, skipping\n");
		return -ENODEV;
	}

	err = enetc_pci_probe(pdev, KBUILD_MODNAME, sizeof(*pf));
	if (err) {
		dev_err(&pdev->dev, "PCI probing failed\n");
@@ -1069,6 +1118,13 @@ static int enetc_pf_probe(struct pci_dev *pdev,
		goto err_map_pf_space;
	}

	if (node && !of_device_is_available(node)) {
		enetc_init_unused_port(si);
		dev_info(&pdev->dev, "device is disabled, skipping\n");
		err = -ENODEV;
		goto err_device_disabled;
	}

	pf = enetc_si_priv(si);
	pf->si = si;
	pf->total_vfs = pci_sriov_get_totalvfs(pdev);
@@ -1108,6 +1164,12 @@ static int enetc_pf_probe(struct pci_dev *pdev,
		goto err_init_port_rss;
	}

	err = enetc_configure_si(priv);
	if (err) {
		dev_err(&pdev->dev, "Failed to configure SI\n");
		goto err_config_si;
	}

	err = enetc_alloc_msix(priv);
	if (err) {
		dev_err(&pdev->dev, "MSIX alloc failed\n");
@@ -1136,6 +1198,7 @@ static int enetc_pf_probe(struct pci_dev *pdev,
	enetc_mdiobus_destroy(pf);
err_mdiobus_create:
	enetc_free_msix(priv);
err_config_si:
err_init_port_rss:
err_init_port_rfs:
err_alloc_msix:
@@ -1144,6 +1207,7 @@ static int enetc_pf_probe(struct pci_dev *pdev,
	si->ndev = NULL;
	free_netdev(ndev);
err_alloc_netdev:
err_device_disabled:
err_map_pf_space:
	enetc_pci_remove(pdev);

+7 −0
Original line number Diff line number Diff line
@@ -171,6 +171,12 @@ static int enetc_vf_probe(struct pci_dev *pdev,
		goto err_alloc_si_res;
	}

	err = enetc_configure_si(priv);
	if (err) {
		dev_err(&pdev->dev, "Failed to configure SI\n");
		goto err_config_si;
	}

	err = enetc_alloc_msix(priv);
	if (err) {
		dev_err(&pdev->dev, "MSIX alloc failed\n");
@@ -187,6 +193,7 @@ static int enetc_vf_probe(struct pci_dev *pdev,

err_reg_netdev:
	enetc_free_msix(priv);
err_config_si:
err_alloc_msix:
	enetc_free_si_resources(priv);
err_alloc_si_res: