[PATCH 02/19] net: airoha: add initial support for multiple GDM port

Mikhail Kshevetskiy mikhail.kshevetskiy at iopsys.eu
Thu Dec 4 05:14:16 CET 2025


From: Christian Marangi <ansuelsmth at gmail.com>

Rework the driver to support multiple GDM port. The driver is split to
main driver as a MISC driver with forced probe (by using the
DM_FLAG_PROBE_AFTER_BIND) and each GDM port register a ETH driver.

This permit a 1:1 implementation with the linux kernel driver and permit
to use the same exact DT nodes.

Signed-off-by: Christian Marangi <ansuelsmth at gmail.com>
---
 drivers/net/airoha_eth.c | 156 ++++++++++++++++++++++++++++++++++-----
 1 file changed, 136 insertions(+), 20 deletions(-)

diff --git a/drivers/net/airoha_eth.c b/drivers/net/airoha_eth.c
index 3234d875887..31937137d59 100644
--- a/drivers/net/airoha_eth.c
+++ b/drivers/net/airoha_eth.c
@@ -9,6 +9,7 @@
  */
 
 #include <dm.h>
+#include <dm/device-internal.h>
 #include <dm/devres.h>
 #include <mapmem.h>
 #include <net.h>
@@ -23,7 +24,7 @@
 #include <linux/time.h>
 #include <asm/arch/scu-regmap.h>
 
-#define AIROHA_MAX_NUM_GDM_PORTS	1
+#define AIROHA_MAX_NUM_GDM_PORTS	4
 #define AIROHA_MAX_NUM_QDMA		1
 #define AIROHA_MAX_NUM_RSTS		3
 #define AIROHA_MAX_NUM_XSI_RSTS		4
@@ -37,6 +38,8 @@
 #define TX_DSCP_NUM			16
 #define RX_DSCP_NUM			PKTBUFSRX
 
+#define AIROHA_GDM_PORT_STRING_LEN	sizeof("airoha-gdmX")
+
 /* SCU */
 #define SCU_SHARE_FEMEM_SEL		0x958
 
@@ -245,6 +248,21 @@
 #define QDMA_ETH_RXMSG_CRSN_MASK	GENMASK(20, 16)
 #define QDMA_ETH_RXMSG_PPE_ENTRY_MASK	GENMASK(15, 0)
 
+enum {
+	FE_PSE_PORT_CDM1,
+	FE_PSE_PORT_GDM1,
+	FE_PSE_PORT_GDM2,
+	FE_PSE_PORT_GDM3,
+	FE_PSE_PORT_PPE1,
+	FE_PSE_PORT_CDM2,
+	FE_PSE_PORT_CDM3,
+	FE_PSE_PORT_CDM4,
+	FE_PSE_PORT_PPE2,
+	FE_PSE_PORT_GDM4,
+	FE_PSE_PORT_CDM5,
+	FE_PSE_PORT_DROP = 0xf,
+};
+
 struct airoha_qdma_desc {
 	__le32 rsv;
 	__le32 ctrl;
@@ -309,11 +327,14 @@ struct airoha_eth {
 	struct reset_ctl_bulk rsts;
 	struct reset_ctl_bulk xsi_rsts;
 
+	struct airoha_eth_soc_data *soc;
+
 	struct airoha_qdma qdma[AIROHA_MAX_NUM_QDMA];
-	struct airoha_gdm_port *ports[AIROHA_MAX_NUM_GDM_PORTS];
+	char gdm_port_str[AIROHA_MAX_NUM_GDM_PORTS][AIROHA_GDM_PORT_STRING_LEN];
 };
 
 struct airoha_eth_soc_data {
+	u32 version;
 	int num_xsi_rsts;
 	const char * const *xsi_rsts_names;
 	const char *switch_compatible;
@@ -375,6 +396,8 @@ static u32 airoha_rmw(void __iomem *base, u32 offset, u32 mask, u32 val)
 #define airoha_switch_wr(eth, offset, val)			\
 	airoha_wr((eth)->switch_regs, (offset), (val))
 
+static struct driver airoha_eth_port;
+
 static inline dma_addr_t dma_map_unaligned(void *vaddr, size_t len,
 					   enum dma_data_direction dir)
 {
@@ -396,11 +419,26 @@ static inline void dma_unmap_unaligned(dma_addr_t addr, size_t len,
 	dma_unmap_single(start, end - start, dir);
 }
 
+static int airoha_get_fe_port(struct airoha_gdm_port *port)
+{
+	struct airoha_qdma *qdma = port->qdma;
+	struct airoha_eth *eth = qdma->eth;
+
+	switch (eth->soc->version) {
+	case 0x7523:
+		/* FIXME: GDM1 is the only supported port */
+		return FE_PSE_PORT_GDM1;
+	case 0x7581:
+	default:
+		return port->id == 4 ? FE_PSE_PORT_GDM4 : port->id;
+	}
+}
+
 static void airoha_fe_maccr_init(struct airoha_eth *eth)
 {
 	int p;
 
-	for (p = 1; p <= ARRAY_SIZE(eth->ports); p++) {
+	for (p = 1; p <= AIROHA_MAX_NUM_GDM_PORTS; p++) {
 		/*
 		 * Disable any kind of CRC drop or offload.
 		 * Enable padding of short TX packets to 60 bytes.
@@ -738,11 +776,35 @@ static int airoha_switch_init(struct udevice *dev, struct airoha_eth *eth)
 	return 0;
 }
 
+static int airoha_alloc_gdm_port(struct udevice *dev, ofnode node)
+{
+	struct airoha_eth *eth = dev_get_priv(dev);
+	struct udevice *gdm_dev;
+	char *str;
+	int ret;
+	u32 id;
+
+	ret = ofnode_read_u32(node, "reg", &id);
+	if (ret)
+		return ret;
+
+	if (id > AIROHA_MAX_NUM_GDM_PORTS)
+		return -EINVAL;
+
+	str = eth->gdm_port_str[id];
+	snprintf(str, AIROHA_GDM_PORT_STRING_LEN,
+		 "airoha-gdm%d", id);
+
+	return device_bind_with_driver_data(dev, &airoha_eth_port, str,
+					    (ulong)eth, node, &gdm_dev);
+}
+
 static int airoha_eth_probe(struct udevice *dev)
 {
 	struct airoha_eth_soc_data *data = (void *)dev_get_driver_data(dev);
 	struct airoha_eth *eth = dev_get_priv(dev);
 	struct regmap *scu_regmap;
+	ofnode node;
 	int i, ret;
 
 	scu_regmap = airoha_get_scu_regmap();
@@ -755,6 +817,8 @@ static int airoha_eth_probe(struct udevice *dev)
 	 */
 	regmap_write(scu_regmap, SCU_SHARE_FEMEM_SEL, 0x0);
 
+	eth->soc = data;
+
 	eth->fe_regs = dev_remap_addr_name(dev, "fe");
 	if (!eth->fe_regs)
 		return -ENOMEM;
@@ -794,13 +858,42 @@ static int airoha_eth_probe(struct udevice *dev)
 	if (ret)
 		return ret;
 
+	ofnode_for_each_subnode(node, dev_ofnode(dev)) {
+		if (!ofnode_device_is_compatible(node, "airoha,eth-mac"))
+			continue;
+
+		if (!ofnode_is_enabled(node))
+			continue;
+
+		ret = airoha_alloc_gdm_port(dev, node);
+		if (ret)
+			return ret;
+	}
+
 	return airoha_switch_init(dev, eth);
 }
 
+static int airoha_eth_port_of_to_plat(struct udevice *dev)
+{
+	struct airoha_gdm_port *port = dev_get_priv(dev);
+
+	return dev_read_u32(dev, "reg", &port->id);
+}
+
+static int airoha_eth_port_probe(struct udevice *dev)
+{
+	struct airoha_eth *eth = (void *)dev_get_driver_data(dev);
+	struct airoha_gdm_port *port = dev_get_priv(dev);
+
+	port->qdma = &eth->qdma[0];
+
+	return 0;
+}
+
 static int airoha_eth_init(struct udevice *dev)
 {
-	struct airoha_eth *eth = dev_get_priv(dev);
-	struct airoha_qdma *qdma = &eth->qdma[0];
+	struct airoha_gdm_port *port = dev_get_priv(dev);
+	struct airoha_qdma *qdma = port->qdma;
 	struct airoha_queue *q;
 	int qid;
 
@@ -818,8 +911,8 @@ static int airoha_eth_init(struct udevice *dev)
 
 static void airoha_eth_stop(struct udevice *dev)
 {
-	struct airoha_eth *eth = dev_get_priv(dev);
-	struct airoha_qdma *qdma = &eth->qdma[0];
+	struct airoha_gdm_port *port = dev_get_priv(dev);
+	struct airoha_qdma *qdma = port->qdma;
 
 	airoha_qdma_clear(qdma, REG_QDMA_GLOBAL_CFG,
 			  GLOBAL_CFG_TX_DMA_EN_MASK |
@@ -828,8 +921,8 @@ static void airoha_eth_stop(struct udevice *dev)
 
 static int airoha_eth_send(struct udevice *dev, void *packet, int length)
 {
-	struct airoha_eth *eth = dev_get_priv(dev);
-	struct airoha_qdma *qdma = &eth->qdma[0];
+	struct airoha_gdm_port *port = dev_get_priv(dev);
+	struct airoha_qdma *qdma = port->qdma;
 	struct airoha_qdma_desc *desc;
 	struct airoha_queue *q;
 	dma_addr_t dma_addr;
@@ -851,7 +944,7 @@ static int airoha_eth_send(struct udevice *dev, void *packet, int length)
 	desc = &q->desc[q->head];
 	index = (q->head + 1) % q->ndesc;
 
-	fport = 1;
+	fport = airoha_get_fe_port(port);
 
 	msg0 = 0;
 	msg1 = FIELD_PREP(QDMA_ETH_TXMSG_FPORT_MASK, fport) |
@@ -893,8 +986,8 @@ static int airoha_eth_send(struct udevice *dev, void *packet, int length)
 
 static int airoha_eth_recv(struct udevice *dev, int flags, uchar **packetp)
 {
-	struct airoha_eth *eth = dev_get_priv(dev);
-	struct airoha_qdma *qdma = &eth->qdma[0];
+	struct airoha_gdm_port *port = dev_get_priv(dev);
+	struct airoha_qdma *qdma = port->qdma;
 	struct airoha_qdma_desc *desc;
 	struct airoha_queue *q;
 	u16 length;
@@ -921,8 +1014,8 @@ static int airoha_eth_recv(struct udevice *dev, int flags, uchar **packetp)
 
 static int arht_eth_free_pkt(struct udevice *dev, uchar *packet, int length)
 {
-	struct airoha_eth *eth = dev_get_priv(dev);
-	struct airoha_qdma *qdma = &eth->qdma[0];
+	struct airoha_gdm_port *port = dev_get_priv(dev);
+	struct airoha_qdma *qdma = port->qdma;
 	struct airoha_queue *q;
 	int qid;
 
@@ -963,8 +1056,9 @@ static int arht_eth_free_pkt(struct udevice *dev, uchar *packet, int length)
 
 static int arht_eth_write_hwaddr(struct udevice *dev)
 {
+	struct airoha_gdm_port *port = dev_get_priv(dev);
 	struct eth_pdata *pdata = dev_get_plat(dev);
-	struct airoha_eth *eth = dev_get_priv(dev);
+	struct airoha_qdma *qdma = port->qdma;
 	unsigned char *mac = pdata->enetaddr;
 	u32 macaddr_lsb, macaddr_msb;
 
@@ -976,19 +1070,32 @@ static int arht_eth_write_hwaddr(struct udevice *dev)
 		      FIELD_PREP(SMACCR1_MAC0, mac[0]);
 
 	/* Set MAC for Switch */
-	airoha_switch_wr(eth, SWITCH_SMACCR0, macaddr_lsb);
-	airoha_switch_wr(eth, SWITCH_SMACCR1, macaddr_msb);
+	airoha_switch_wr(qdma->eth, SWITCH_SMACCR0, macaddr_lsb);
+	airoha_switch_wr(qdma->eth, SWITCH_SMACCR1, macaddr_msb);
+
+	return 0;
+}
+
+static int airoha_eth_bind(struct udevice *dev)
+{
+	/*
+	 * Force Probe as we set the Main ETH driver as misc
+	 * to register multiple eth port for each GDM
+	 */
+	dev_or_flags(dev, DM_FLAG_PROBE_AFTER_BIND);
 
 	return 0;
 }
 
 static const struct airoha_eth_soc_data en7523_data = {
+	.version = 0x7523,
 	.xsi_rsts_names = en7523_xsi_rsts_names,
 	.num_xsi_rsts = ARRAY_SIZE(en7523_xsi_rsts_names),
 	.switch_compatible = "airoha,en7523-switch",
 };
 
 static const struct airoha_eth_soc_data en7581_data = {
+	.version = 0x7581,
 	.xsi_rsts_names = en7581_xsi_rsts_names,
 	.num_xsi_rsts = ARRAY_SIZE(en7581_xsi_rsts_names),
 	.switch_compatible = "airoha,en7581-switch",
@@ -1013,12 +1120,21 @@ static const struct eth_ops airoha_eth_ops = {
 	.write_hwaddr = arht_eth_write_hwaddr,
 };
 
+static struct driver airoha_eth_port = {
+	.name = "airoha-eth-port",
+	.id = UCLASS_ETH,
+	.of_to_plat = airoha_eth_port_of_to_plat,
+	.probe = airoha_eth_port_probe,
+	.ops = &airoha_eth_ops,
+	.priv_auto = sizeof(struct airoha_gdm_port),
+	.plat_auto = sizeof(struct eth_pdata),
+};
+
 U_BOOT_DRIVER(airoha_eth) = {
 	.name = "airoha-eth",
-	.id = UCLASS_ETH,
+	.id = UCLASS_MISC,
 	.of_match = airoha_eth_ids,
 	.probe = airoha_eth_probe,
-	.ops = &airoha_eth_ops,
+	.bind = airoha_eth_bind,
 	.priv_auto = sizeof(struct airoha_eth),
-	.plat_auto = sizeof(struct eth_pdata),
 };
-- 
2.51.0



More information about the U-Boot mailing list