Merge a patch series to improve dc2114x support

This patch series by Hanyuan Zhao <hanyuan-z@qq.com> provides a number of
improvements to the dc2114x driver.

Link: https://lore.kernel.org/r/tencent_BD4B002FC63A5F77969D9BD1FFF125371C08@qq.com
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index 94a1c98..576cd2d 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -762,6 +762,38 @@
 	help
 	  This driver supports DEC DC2114x Fast ethernet chips.
 
+config TULIP_SUPPORT_NON_PCI
+	bool "No PCI controller"
+	depends on TULIP
+	default n
+	help
+	  Say Y to this and you can run this driver on platforms that do not
+	  have PCI controllers.
+
+config TULIP_IGNORE_TX_NO_CARRIER
+	bool "Ignore tx no carrier error"
+	depends on TULIP
+	default n
+	help
+	  Some IP cores of dc2114x or its variants do not comply so well with
+	  the behaviors described by the official document. A packet could be
+	  sent successfully but reported with No Carrier error. Latest drivers
+	  of this IP core do not detect this error anymore. Say Y to this could
+	  disable handling of this error.
+
+config TULIP_MULTIPLE_TX_DESC
+	bool "Use multiple tx descriptors"
+	depends on TULIP
+	default n
+	help
+	  Some IP cores of dc2114x or its variants do not comply so well with
+	  the behaviors described by the official document. Originally this
+	  driver uses only one tx descriptor and organizes it as a ring buffer,
+	  which would lead to a problem that one packet would be sent twice.
+	  Say Y to this could prevent this bug if you are using IP cores with
+	  this issue, by using multiple tx descriptors and organizing them as
+	  a real well-defined ring buffer.
+
 config XILINX_AXIEMAC
 	select PHYLIB
 	select MII
diff --git a/drivers/net/dc2114x.c b/drivers/net/dc2114x.c
index ce028f4..7c0665f 100644
--- a/drivers/net/dc2114x.c
+++ b/drivers/net/dc2114x.c
@@ -1,6 +1,7 @@
 // SPDX-License-Identifier: GPL-2.0+
 
 #include <asm/io.h>
+#include <cpu_func.h>
 #include <dm.h>
 #include <malloc.h>
 #include <net.h>
@@ -72,10 +73,20 @@
 
 #define POLL_DEMAND	1
 
+#if CONFIG_IS_ENABLED(TULIP_SUPPORT_NON_PCI)
+#define phys_to_bus(dev, a)	virt_to_phys((volatile const void *)(a))
+#else
 #define phys_to_bus(dev, a)	dm_pci_phys_to_mem((dev), (a))
+#endif
+
+/* Number of TX descriptors   */
+#if CONFIG_IS_ENABLED(TULIP_MULTIPLE_TX_DESC)
+#define NUM_TX_DESC 4
+#else
+#define NUM_TX_DESC 1
+#endif
 
 #define NUM_RX_DESC PKTBUFSRX
-#define NUM_TX_DESC 1			/* Number of TX descriptors   */
 #define RX_BUFF_SZ  PKTSIZE_ALIGN
 
 #define TOUT_LOOP   1000000
@@ -89,9 +100,17 @@
 	u32 next;
 };
 
+/* Assigned for network card's ring buffer:
+ * Some CPU might treat these memories as cached, and changes to these memories
+ * won't immediately be visible to each other. It is necessary to ensure that
+ * these memories between the CPU and the network card are marked as uncached.
+ */
+static struct de4x5_desc rx_ring[NUM_RX_DESC] __aligned(32);
+static struct de4x5_desc tx_ring[NUM_TX_DESC] __aligned(32);
+
 struct dc2114x_priv {
-	struct de4x5_desc rx_ring[NUM_RX_DESC] __aligned(32);
-	struct de4x5_desc tx_ring[NUM_TX_DESC] __aligned(32);
+	struct de4x5_desc *rx_ring; /* Must be uncached to CPU */
+	struct de4x5_desc *tx_ring; /* Must be uncached to CPU */
 	int rx_new;	/* RX descriptor ring pointer */
 	int tx_new;	/* TX descriptor ring pointer */
 	char rx_ring_size;
@@ -271,7 +290,12 @@
 
 static void send_setup_frame(struct dc2114x_priv *priv)
 {
-	char setup_frame[SETUP_FRAME_LEN];
+	/* We are writing setup frame and these changes should be visible to the
+	 * network card immediately. So let's directly read/write through the
+	 * uncached window.
+	 */
+	char __setup_frame[SETUP_FRAME_LEN] __aligned(32);
+	char *setup_frame = (char *)map_physmem((phys_addr_t)virt_to_phys(__setup_frame), 0, MAP_NOCACHE);
 	char *pa = &setup_frame[0];
 	int i;
 
@@ -292,8 +316,13 @@
 	}
 
 	priv->tx_ring[priv->tx_new].buf = cpu_to_le32(phys_to_bus(priv->devno,
-						      (u32)&setup_frame[0]));
+						      (phys_addr_t)&setup_frame[0]));
+#if CONFIG_IS_ENABLED(TULIP_MULTIPLE_TX_DESC)
+	priv->tx_ring[priv->tx_new].des1 = cpu_to_le32(TD_SET | SETUP_FRAME_LEN);
+	priv->tx_ring[priv->tx_ring_size - 1].des1 |= cpu_to_le32(TD_TER);
+#else
 	priv->tx_ring[priv->tx_new].des1 = cpu_to_le32(TD_TER | TD_SET | SETUP_FRAME_LEN);
+#endif
 	priv->tx_ring[priv->tx_new].status = cpu_to_le32(T_OWN);
 
 	dc2114x_outl(priv, POLL_DEMAND, DE4X5_TPD);
@@ -307,7 +336,7 @@
 	}
 
 	if (le32_to_cpu(priv->tx_ring[priv->tx_new].status) != 0x7FFFFFFF) {
-		printf("TX error status2 = 0x%08X\n",
+		debug("TX error status2 = 0x%08X\n",
 		       le32_to_cpu(priv->tx_ring[priv->tx_new].status));
 	}
 
@@ -332,9 +361,17 @@
 		goto done;
 	}
 
+	/* Packet should be visible to the network card */
+	flush_dcache_range((phys_addr_t)packet, (phys_addr_t)(packet + RX_BUFF_SZ));
+
 	priv->tx_ring[priv->tx_new].buf = cpu_to_le32(phys_to_bus(priv->devno,
-						      (u32)packet));
+						      (phys_addr_t)packet));
+#if CONFIG_IS_ENABLED(TULIP_MULTIPLE_TX_DESC)
+	priv->tx_ring[priv->tx_new].des1 = cpu_to_le32(TD_LS | TD_FS | length);
+	priv->tx_ring[priv->tx_ring_size - 1].des1 |= cpu_to_le32(TD_TER);
+#else
 	priv->tx_ring[priv->tx_new].des1 = cpu_to_le32(TD_TER | TD_LS | TD_FS | length);
+#endif
 	priv->tx_ring[priv->tx_new].status = cpu_to_le32(T_OWN);
 
 	dc2114x_outl(priv, POLL_DEMAND, DE4X5_TPD);
@@ -349,7 +386,9 @@
 
 	if (le32_to_cpu(priv->tx_ring[priv->tx_new].status) & TD_ES) {
 		priv->tx_ring[priv->tx_new].status = 0x0;
+#if !CONFIG_IS_ENABLED(TULIP_IGNORE_TX_NO_CARRIER)
 		goto done;
+#endif
 	}
 
 	status = length;
@@ -398,13 +437,22 @@
 		return -1;
 	}
 
-	dc2114x_outl(priv, OMR_SDP | OMR_PS | OMR_PM, DE4X5_OMR);
+	/* 2024-07:
+	 * Remove the OMR_PM flag and choose 16 perfect filtering mode since in
+	 * modern networks there're plenty of multicasts and set ORM_PM flag will
+	 * increase the dc2114x's workload and ask the U-Boot to handle packets
+	 * not related to itself. And most of the time, U-Boot does not need this
+	 * feature.
+	 *
+	 * A better way: let user to decide whether to have this flag.
+	 */
+	dc2114x_outl(priv, OMR_SDP | OMR_PS, DE4X5_OMR);
 
 	for (i = 0; i < NUM_RX_DESC; i++) {
 		priv->rx_ring[i].status = cpu_to_le32(R_OWN);
 		priv->rx_ring[i].des1 = cpu_to_le32(RX_BUFF_SZ);
 		priv->rx_ring[i].buf = cpu_to_le32(phys_to_bus(priv->devno,
-					     (u32)net_rx_packets[i]));
+					     (phys_addr_t)net_rx_packets[i]));
 		priv->rx_ring[i].next = 0;
 	}
 
@@ -423,9 +471,9 @@
 	priv->tx_ring[priv->tx_ring_size - 1].des1 |= cpu_to_le32(TD_TER);
 
 	/* Tell the adapter where the TX/RX rings are located. */
-	dc2114x_outl(priv, phys_to_bus(priv->devno, (u32)&priv->rx_ring),
+	dc2114x_outl(priv, phys_to_bus(priv->devno, (phys_addr_t)priv->rx_ring),
 		     DE4X5_RRBA);
-	dc2114x_outl(priv, phys_to_bus(priv->devno, (u32)&priv->tx_ring),
+	dc2114x_outl(priv, phys_to_bus(priv->devno, (phys_addr_t)priv->tx_ring),
 		     DE4X5_TRBA);
 
 	start_de4x5(priv);
@@ -461,21 +509,32 @@
 	}
 }
 
+#if !CONFIG_IS_ENABLED(TULIP_SUPPORT_NON_PCI)
 static struct pci_device_id supported[] = {
 	{ PCI_DEVICE(PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_TULIP_FAST) },
 	{ PCI_DEVICE(PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_21142) },
 	{ }
 };
+#endif
 
 static int dc2114x_start(struct udevice *dev)
 {
-	struct eth_pdata *plat = dev_get_plat(dev);
 	struct dc2114x_priv *priv = dev_get_priv(dev);
+	int rval;
 
-	memcpy(priv->enetaddr, plat->enetaddr, sizeof(plat->enetaddr));
+	if (!priv->enetaddr) {
+		rval = eth_env_get_enetaddr("ethaddr", priv->enetaddr);
 
+		if (!rval) {
+			printf("dc2114x: Err: please set a valid MAC address\n");
+			return -EINVAL;
+		}
+	}
+
+#if !CONFIG_IS_ENABLED(TULIP_SUPPORT_NON_PCI)
 	/* Ensure we're not sleeping. */
 	dm_pci_write_config8(dev, PCI_CFDA_PSM, WAKEUP);
+#endif
 
 	return dc21x4x_init_common(priv);
 }
@@ -485,8 +544,9 @@
 	struct dc2114x_priv *priv = dev_get_priv(dev);
 
 	dc21x4x_halt_common(priv);
-
+#if !CONFIG_IS_ENABLED(TULIP_SUPPORT_NON_PCI)
 	dm_pci_write_config8(dev, PCI_CFDA_PSM, SLEEP);
+#endif
 }
 
 static int dc2114x_send(struct udevice *dev, void *packet, int length)
@@ -515,7 +575,8 @@
 	if (!ret)
 		return 0;
 
-	*packetp = net_rx_packets[priv->rx_new];
+	invalidate_dcache_range((phys_addr_t)net_rx_packets[priv->rx_new], (phys_addr_t)(net_rx_packets[priv->rx_new] + RX_BUFF_SZ));
+	*packetp = (uchar *)net_rx_packets[priv->rx_new];
 
 	return ret - 4;
 }
@@ -543,7 +604,7 @@
 
 static int dc2114x_bind(struct udevice *dev)
 {
-	static int card_number;
+	static int card_number = 0;
 	char name[16];
 
 	sprintf(name, "dc2114x#%u", card_number++);
@@ -555,6 +616,8 @@
 {
 	struct eth_pdata *plat = dev_get_plat(dev);
 	struct dc2114x_priv *priv = dev_get_priv(dev);
+
+#if !CONFIG_IS_ENABLED(TULIP_SUPPORT_NON_PCI)
 	u16 command, status;
 	u32 iobase;
 
@@ -562,9 +625,6 @@
 	iobase &= ~0xf;
 
 	debug("dc2114x: DEC 2114x PCI Device @0x%x\n", iobase);
-
-	priv->devno = dev;
-	priv->enetaddr = plat->enetaddr;
 	priv->iobase = (void __iomem *)dm_pci_mem_to_phys(dev, iobase);
 
 	command = PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER;
@@ -576,10 +636,29 @@
 	}
 
 	dm_pci_write_config8(dev, PCI_LATENCY_TIMER, 0x60);
+#endif
+
+	priv->devno = dev;
+	priv->enetaddr = plat->enetaddr;
+	priv->rx_ring = (struct de4x5_desc *)map_physmem((phys_addr_t)virt_to_phys(rx_ring), 0, MAP_NOCACHE);
+	priv->tx_ring = (struct de4x5_desc *)map_physmem((phys_addr_t)virt_to_phys(tx_ring), 0, MAP_NOCACHE);
 
 	return 0;
 }
 
+#if CONFIG_IS_ENABLED(TULIP_SUPPORT_NON_PCI)
+static int dc2114x_of_to_plat(struct udevice *dev)
+{
+	struct eth_pdata *plat = dev_get_plat(dev);
+	struct dc2114x_priv *priv = dev_get_priv(dev);
+
+	plat->iobase = (phys_addr_t)map_physmem((phys_addr_t)devfdt_get_addr(dev), 0, MAP_NOCACHE);
+	priv->iobase = (void *)plat->iobase;
+
+	return 0;
+}
+#endif
+
 static const struct eth_ops dc2114x_ops = {
 	.start		= dc2114x_start,
 	.send		= dc2114x_send,
@@ -589,9 +668,23 @@
 	.read_rom_hwaddr = dc2114x_read_rom_hwaddr,
 };
 
+#if CONFIG_IS_ENABLED(TULIP_SUPPORT_NON_PCI)
+static const struct udevice_id dc2114x_eth_ids[] = {
+	{ .compatible = "dec,dmfe" },
+	{ .compatible = "tulip,dmfe" },
+	{ .compatible = "dec,dc2114x" },
+	{ .compatible = "tulip,dc2114x" },
+	{ }
+};
+#endif
+
 U_BOOT_DRIVER(eth_dc2114x) = {
 	.name	= "eth_dc2114x",
 	.id	= UCLASS_ETH,
+#if CONFIG_IS_ENABLED(TULIP_SUPPORT_NON_PCI)
+	.of_match	= dc2114x_eth_ids,
+	.of_to_plat	= dc2114x_of_to_plat,
+#endif
 	.bind	= dc2114x_bind,
 	.probe	= dc2114x_probe,
 	.ops	= &dc2114x_ops,
@@ -599,4 +692,6 @@
 	.plat_auto	= sizeof(struct eth_pdata),
 };
 
+#if !CONFIG_IS_ENABLED(TULIP_SUPPORT_NON_PCI)
 U_BOOT_PCI_DEVICE(eth_dc2114x, supported);
+#endif