[PATCH net-next 2/2] net: fec: support receive flushing via fsl,rx-flush-queues

"A. Sverdlin" <[email protected]>
Newsgroups org.kernel.vger.netdev,dev.linux.lists.imx,org.kernel.vger.linux-devicetree,org.kernel.vger.linux-kernel
Message-ID <[email protected]>
From: Alexander Sverdlin <[email protected]>

The FEC/ENET controller can flush frames stuck at the head of the RX FIFO
when their destination ring has no empty buffer descriptor, instead of
letting them block the FIFO. This is controlled per RX queue through the
RX_FLUSHn bits of the QOS Scheme register (FEC_QOS_SCHEME).

Parse the new fsl,rx-flush-queues property, build the RX flush mask and
program it in fec_enet_enable_ring().

Erratum ERR050395 (e.g. i.MX8QXP) can cause an RX path lock-up when
flushing is enabled on more than one queue at a time. Rather than encoding
that limitation in the binding, enforce it in the driver: reject a
configuration that enables flushing on multiple queues unless the
controller advertises FEC_QUIRK_HAS_MULTI_RX_FLUSH, which future parts with
the erratum fixed can set.

Signed-off-by: Alexander Sverdlin <[email protected]>
---
 drivers/net/ethernet/freescale/fec.h      | 14 ++++++++++
 drivers/net/ethernet/freescale/fec_main.c | 34 +++++++++++++++++++++++
 2 files changed, 48 insertions(+)

diff --git a/drivers/net/ethernet/freescale/fec.h b/drivers/net/ethernet/freescale/fec.h
index 7176803146f3d..4af3ae286895f 100644
--- a/drivers/net/ethernet/freescale/fec.h
+++ b/drivers/net/ethernet/freescale/fec.h
@@ -186,6 +186,7 @@
 #define FEC_RCMR_2		0xfff
 #define FEC_DMA_CFG_1		0xfff
 #define FEC_DMA_CFG_2		0xfff
+#define FEC_QOS_SCHEME		0xfff
 #define FEC_TXIC0		0xfff
 #define FEC_TXIC1		0xfff
 #define FEC_TXIC2		0xfff
@@ -322,6 +323,10 @@ struct bufdesc_ex {
 #define RCMR_CMP(X)		(((X) == 1) ? RCMR_CMP_1 : RCMR_CMP_2)
 #define FEC_TX_BD_FTYPE(X)	(((X) & 0xf) << 20)
 
+/* FEC_QOS_SCHEME bits */
+#define QOS_RX_FLUSH(X)		(1 << (3 + (X)))	/* RX_FLUSHn, n = 0, 1, 2 */
+#define QOS_RX_FLUSH_MASK	(QOS_RX_FLUSH(0) | QOS_RX_FLUSH(1) | QOS_RX_FLUSH(2))
+
 /* The number of Tx and Rx buffers.  These are allocated from the page
  * pool.  The code may assume these are power of two, so it is best
  * to keep them that size.
@@ -499,6 +504,12 @@ struct bufdesc_ex {
 /* Jumbo Frame support */
 #define FEC_QUIRK_JUMBO_FRAME		BIT(25)
 
+/* Receive flushing (QOS Scheme register RX_FLUSHn) may be enabled on more than
+ * one RX queue at a time. Parts without this quirk are subject to erratum
+ * ERR050395 and must limit RX flushing to a single queue.
+ */
+#define FEC_QUIRK_HAS_MULTI_RX_FLUSH	BIT(24)
+
 struct bufdesc_prop {
 	int qid;
 	/* Address of Rx and Tx buffers */
@@ -604,6 +615,9 @@ struct fec_enet_private {
 	unsigned int num_tx_queues;
 	unsigned int num_rx_queues;
 
+	/* Bitmask of RX queues with receive flushing enabled */
+	u32 rx_flush_mask;
+
 	struct fec_enet_priv_tx_q *tx_queue[FEC_ENET_MAX_TX_QS];
 	struct fec_enet_priv_rx_q *rx_queue[FEC_ENET_MAX_RX_QS];
 
diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c
index ced4dbf8cd90f..a4fe8423630b5 100644
--- a/drivers/net/ethernet/freescale/fec_main.c
+++ b/drivers/net/ethernet/freescale/fec_main.c
@@ -1090,6 +1090,15 @@ static void fec_enet_enable_ring(struct net_device *ndev)
 			       fep->hwp + FEC_RCMR(i));
 	}
 
+	/* Enable receive flushing for the selected queues */
+	if (fep->rx_flush_mask) {
+		u32 val = readl(fep->hwp + FEC_QOS_SCHEME);
+
+		val &= ~QOS_RX_FLUSH_MASK;
+		val |= fep->rx_flush_mask;
+		writel(val, fep->hwp + FEC_QOS_SCHEME);
+	}
+
 	for (i = 0; i < fep->num_tx_queues; i++) {
 		txq = fep->tx_queue[i];
 		writel(txq->bd.dma, fep->hwp + FEC_X_DES_START(i));
@@ -5239,6 +5248,31 @@ fec_probe(struct platform_device *pdev)
 	fep->num_rx_queues = num_rx_qs;
 	fep->num_tx_queues = num_tx_qs;
 
+	/* Enable receive flushing on the requested queues. Erratum ERR050395
+	 * restricts flushing to a single queue; only accept more than one queue
+	 * on controllers known to have the erratum fixed.
+	 */
+	for (i = 0; i < of_property_count_u32_elems(np, "fsl,rx-flush-queues"); i++) {
+		u32 q;
+
+		if (of_property_read_u32_index(np, "fsl,rx-flush-queues", i, &q))
+			break;
+		if (q >= num_rx_qs) {
+			dev_warn(&pdev->dev,
+				 "fsl,rx-flush-queues: queue %u exceeds num-rx-queues, ignoring\n",
+				 q);
+			continue;
+		}
+		fep->rx_flush_mask |= QOS_RX_FLUSH(q);
+	}
+	if (hweight32(fep->rx_flush_mask) > 1 &&
+	    !(fep->quirks & FEC_QUIRK_HAS_MULTI_RX_FLUSH)) {
+		dev_err(&pdev->dev,
+			"fsl,rx-flush-queues: RX flush on multiple queues not supported\n");
+		ret = -EINVAL;
+		goto failed_ioremap;
+	}
+
 	/* default enable pause frame auto negotiation */
 	if (fep->quirks & FEC_QUIRK_HAS_GBIT)
 		fep->pause_flag |= FEC_PAUSE_FLAG_AUTONEG;
-- 
2.55.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.