diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index 8ed6ff2fe3d..27c6a61de39 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -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 diff --git a/drivers/net/airoha_eth.c b/drivers/net/airoha_eth.c index fef803ca167..d5b9da6bc18 100644 --- a/drivers/net/airoha_eth.c +++ b/drivers/net/airoha_eth.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -20,11 +21,14 @@ #include #include #include +#include #include #include #include #include +#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(ð->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 = ð->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);