]> git.ipfire.org Git - thirdparty/u-boot.git/commitdiff
net: airoha: fill in support for PCS/PHY in Airoha Ethernet driver
authorChristian Marangi <ansuelsmth@gmail.com>
Wed, 11 Feb 2026 18:22:28 +0000 (21:22 +0300)
committerJerome Forissier <jerome.forissier@arm.com>
Thu, 12 Mar 2026 14:53:41 +0000 (15:53 +0100)
Add required changes to call PCS function to configure the Serdes Port.
The Ethernet driver is adapted following Upstream Kernel node structure.

Function calling order is the same of Phylink upstream kernel.

With the PCS support, also add support for attaching PHY. With
"in-band-status" set in DT for the managed property, a rudimental
support for SFP module is present.

Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
drivers/net/Kconfig
drivers/net/airoha_eth.c

index 8ed6ff2fe3d89b83b176ed4452b2940deca49af2..27c6a61de39bceda5a3bd9064dcc722ecf5f1eed 100644 (file)
@@ -128,6 +128,7 @@ config AIROHA_ETH
        depends on ARCH_AIROHA
        select PHYLIB
        select DEVRES
+       select DM_ETH_PHY
        select DM_RESET
        select MDIO_MT7531_MMIO
        help
index fef803ca1670ccd42000686ca27d899b28b95e88..d5b9da6bc1899b55bac6636686f8d71a68bf67d6 100644 (file)
@@ -13,6 +13,7 @@
 #include <dm/devres.h>
 #include <dm/lists.h>
 #include <mapmem.h>
+#include <miiphy.h>
 #include <net.h>
 #include <regmap.h>
 #include <reset.h>
 #include <linux/bitfield.h>
 #include <linux/delay.h>
 #include <linux/dma-mapping.h>
+#include <linux/ethtool.h>
 #include <linux/io.h>
 #include <linux/iopoll.h>
 #include <linux/time.h>
 #include <asm/arch/scu-regmap.h>
 
+#include "airoha/pcs-airoha.h"
+
 #define AIROHA_MAX_NUM_GDM_PORTS       4
 #define AIROHA_MAX_NUM_QDMA            1
 #define AIROHA_MAX_NUM_RSTS            3
@@ -319,6 +323,12 @@ struct airoha_qdma {
 struct airoha_gdm_port {
        struct airoha_qdma *qdma;
        int id;
+
+       struct udevice *pcs_dev;
+       phy_interface_t mode;
+       bool neg_mode;
+
+       struct phy_device *phydev;
 };
 
 struct airoha_eth {
@@ -694,6 +704,34 @@ static int airoha_qdma_init(struct udevice *dev,
        return airoha_qdma_hw_init(qdma);
 }
 
+static int airoha_pcs_init(struct udevice *dev)
+{
+       struct airoha_gdm_port *port = dev_get_priv(dev);
+       struct udevice *pcs_dev;
+       const char *managed;
+       int ret;
+
+       ret = uclass_get_device_by_phandle(UCLASS_MISC, dev, "pcs",
+                                          &pcs_dev);
+       if (ret || !pcs_dev)
+               return ret;
+
+       port->pcs_dev = pcs_dev;
+       port->mode = dev_read_phy_mode(dev);
+       managed = dev_read_string(dev, "managed");
+       port->neg_mode = !strncmp(managed, "in-band-status",
+                                 sizeof("in-band-status"));
+
+       airoha_pcs_pre_config(pcs_dev, port->mode);
+
+       ret = airoha_pcs_post_config(pcs_dev, port->mode);
+       if (ret)
+               return ret;
+
+       return airoha_pcs_config(pcs_dev, port->neg_mode,
+                                port->mode, NULL, true);
+}
+
 static int airoha_hw_init(struct udevice *dev,
                          struct airoha_eth *eth)
 {
@@ -714,6 +752,10 @@ static int airoha_hw_init(struct udevice *dev,
        if (ret)
                return ret;
 
+       ret = reset_deassert_bulk(&eth->xsi_rsts);
+       if (ret)
+               return ret;
+
        mdelay(20);
 
        for (i = 0; i < ARRAY_SIZE(eth->qdma); i++) {
@@ -884,6 +926,7 @@ 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);
+       int ret;
 
        port->qdma = &eth->qdma[0];
 
@@ -891,6 +934,14 @@ static int airoha_eth_port_probe(struct udevice *dev)
        if (ret)
                return ret;
 
+       if (port->id > 1) {
+               ret = airoha_pcs_init(dev);
+               if (ret)
+                       return ret;
+
+               port->phydev = dm_eth_phy_connect(dev);
+       }
+
        return 0;
 }
 
@@ -910,6 +961,47 @@ static int airoha_eth_init(struct udevice *dev)
                        GLOBAL_CFG_TX_DMA_EN_MASK |
                        GLOBAL_CFG_RX_DMA_EN_MASK);
 
+       if (port->id > 1) {
+               struct phy_device *phydev = port->phydev;
+               int speed, duplex;
+               int ret;
+
+               if (phydev) {
+                       ret = phy_config(phydev);
+                       if (ret)
+                               return ret;
+
+                       ret = phy_startup(phydev);
+                       if (ret)
+                               return ret;
+
+                       speed = phydev->speed;
+                       duplex = phydev->duplex;
+               } else {
+                       duplex = DUPLEX_FULL;
+
+                       /* Hardcode speed for linkup */
+                       switch (port->mode) {
+                       case PHY_INTERFACE_MODE_USXGMII:
+                       case PHY_INTERFACE_MODE_10GBASER:
+                               speed = SPEED_10000;
+                               break;
+                       case PHY_INTERFACE_MODE_2500BASEX:
+                               speed = SPEED_2500;
+                               break;
+                       case PHY_INTERFACE_MODE_SGMII:
+                       case PHY_INTERFACE_MODE_1000BASEX:
+                               speed = SPEED_1000;
+                               break;
+                       default:
+                               return -EINVAL;
+                       }
+               }
+
+               airoha_pcs_link_up(port->pcs_dev, port->neg_mode, port->mode,
+                                  speed, duplex);
+       }
+
        return 0;
 }
 
@@ -918,6 +1010,13 @@ static void airoha_eth_stop(struct udevice *dev)
        struct airoha_gdm_port *port = dev_get_priv(dev);
        struct airoha_qdma *qdma = port->qdma;
 
+       if (port->id > 1) {
+               if (port->phydev)
+                       phy_shutdown(port->phydev);
+
+               airoha_pcs_link_down(port->pcs_dev);
+       }
+
        airoha_qdma_clear(qdma, REG_QDMA_GLOBAL_CFG,
                          GLOBAL_CFG_TX_DMA_EN_MASK |
                          GLOBAL_CFG_RX_DMA_EN_MASK);