From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: John Doe Date: Sat, 15 Mar 2025 00:57:55 -0300 Subject: [PATCH] Add Maxio MAE0621A PHY driver Signed-off-by: John Doe --- drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 5 + drivers/net/phy/Kconfig | 5 + drivers/net/phy/Makefile | 1 + drivers/net/phy/maxio.c | 254 ++++++++++ drivers/net/phy/phy_device.c | 9 + 5 files changed, 274 insertions(+) diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index d04543e5697b..c8cf84fa4091 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -7826,10 +7826,11 @@ int stmmac_suspend(struct device *dev) priv->speed = SPEED_UNKNOWN; return 0; } EXPORT_SYMBOL_GPL(stmmac_suspend); +#define MAXIO_PHY_MAE0621A_ID 0x7b744411 static void stmmac_reset_rx_queue(struct stmmac_priv *priv, u32 queue) { struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[queue]; @@ -7922,10 +7923,14 @@ int stmmac_resume(struct device *dev) stmmac_reset_queues_param(priv); stmmac_free_tx_skbufs(priv); stmmac_clear_descriptors(priv, &priv->dma_conf); + if (ndev->phydev->drv->config_init) { + if (ndev->phydev->phy_id == MAXIO_PHY_MAE0621A_ID) + ndev->phydev->drv->config_init(ndev->phydev); + } stmmac_hw_setup(ndev, false); stmmac_init_coalesce(priv); stmmac_set_rx_mode(ndev); diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig index 41c15a2c2037..6a5de0ce5723 100644 --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig @@ -360,10 +360,15 @@ config RENESAS_PHY config ROCKCHIP_PHY tristate "Rockchip Ethernet PHYs" help Currently supports the integrated Ethernet PHY. +config MAXIO_PHY + tristate "Maxio PHYs" + help + Supports the Maxio MAExxxx PHY. + config SMSC_PHY tristate "SMSC PHYs" select CRC16 help Currently supports the LAN83C185, LAN8187 and LAN8700 PHYs diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile index c8dac6e92278..2bdde53a3885 100644 --- a/drivers/net/phy/Makefile +++ b/drivers/net/phy/Makefile @@ -94,10 +94,11 @@ obj-$(CONFIG_NXP_C45_TJA11XX_PHY) += nxp-c45-tja.o obj-$(CONFIG_NXP_CBTX_PHY) += nxp-cbtx.o obj-$(CONFIG_NXP_TJA11XX_PHY) += nxp-tja11xx.o obj-y += qcom/ obj-$(CONFIG_QSEMI_PHY) += qsemi.o obj-$(CONFIG_REALTEK_PHY) += realtek/ +obj-$(CONFIG_MAXIO_PHY) += maxio.o obj-$(CONFIG_RENESAS_PHY) += uPD60620.o obj-$(CONFIG_ROCKCHIP_PHY) += rockchip.o obj-$(CONFIG_SMSC_PHY) += smsc.o obj-$(CONFIG_STE10XP) += ste10Xp.o obj-$(CONFIG_TERANETICS_PHY) += teranetics.o diff --git a/drivers/net/phy/maxio.c b/drivers/net/phy/maxio.c new file mode 100644 index 000000000000..fd227e86794f --- /dev/null +++ b/drivers/net/phy/maxio.c @@ -0,0 +1,254 @@ +/* + * drivers/net/phy/maxio.c + * + * Driver for maxio PHYs + * + * Author: zhao yang + * + * Copyright (c) 2004 maxio technology, Inc. + */ +#include +#include +#include +#include +#include +#include +#include + +#define MAXIO_PAGE_SELECT 0x1f +#define MAXIO_MAE0621A_INER 0x12 +#define MAXIO_MAE0621A_INER_LINK_STATUS BIT(4) +#define MAXIO_MAE0621A_INSR 0x1d +#define MAXIO_MAE0621A_TX_DELAY (BIT(6)|BIT(7)) +#define MAXIO_MAE0621A_RX_DELAY (BIT(4)|BIT(5)) +#define MAXIO_MAE0621A_CLK_MODE_REG 0x02 +#define MAXIO_MAE0621A_WORK_STATUS_REG 0x1d + + +static int maxio_read_paged(struct phy_device *phydev, int page, u32 regnum) +{ + int ret = 0, oldpage; + + oldpage = phy_read(phydev, MAXIO_PAGE_SELECT); + if (oldpage >= 0) { + phy_write(phydev, MAXIO_PAGE_SELECT, page); + ret = phy_read(phydev, regnum); + } + phy_write(phydev, MAXIO_PAGE_SELECT, oldpage); + + return ret; +} + +static int maxio_write_paged(struct phy_device *phydev, int page, u32 regnum, u16 val) +{ + int ret = 0, oldpage; + + oldpage = phy_read(phydev, MAXIO_PAGE_SELECT); + if (oldpage >= 0) { + phy_write(phydev, MAXIO_PAGE_SELECT, page); + ret = phy_write(phydev, regnum, val); + } + phy_write(phydev, MAXIO_PAGE_SELECT, oldpage); + + return ret; +} + +static int maxio_mae0621a_clk_init(struct phy_device *phydev) +{ + u32 workmode,clkmode,oldpage; + + oldpage = phy_read(phydev, MAXIO_PAGE_SELECT); + if (oldpage == 0xFFFF) { + oldpage = phy_read(phydev, MAXIO_PAGE_SELECT); + } + + //soft reset + phy_write(phydev, MAXIO_PAGE_SELECT, 0x0); + phy_write(phydev, MII_BMCR, BMCR_RESET | phy_read(phydev, MII_BMCR)); + + //get workmode + phy_write(phydev, MAXIO_PAGE_SELECT, 0xa43); + workmode = phy_read(phydev, MAXIO_MAE0621A_WORK_STATUS_REG); + + //get clkmode + phy_write( phydev, MAXIO_PAGE_SELECT, 0xd92 ); + clkmode = phy_read( phydev, MAXIO_MAE0621A_CLK_MODE_REG ); + + //abnormal + if (0 == (workmode&BIT(5))) { + if (0 == (clkmode&BIT(8))) { + //oscillator + phy_write(phydev, 0x02, clkmode | BIT(8)); + printk("****maxio_mae0621a_clk_init**clkmode**0x210a: 0x%x\n", phydev->phy_id); + } else { + //crystal + printk("****maxio_mae0621a_clk_init**clkmode**0x200a: 0x%x\n", phydev->phy_id); + phy_write(phydev, 0x02, clkmode &(~ BIT(8))); + } + } + phy_write(phydev, MAXIO_PAGE_SELECT, 0x0); + + phy_write(phydev, MAXIO_PAGE_SELECT, oldpage); + + return 0; +} + +static int maxio_read_mmd(struct phy_device *phydev, int devnum, u16 regnum) +{ + int ret = 0, oldpage; + oldpage = phy_read(phydev, MAXIO_PAGE_SELECT); + + if (devnum == MDIO_MMD_AN && regnum == MDIO_AN_EEE_ADV) {// eee info + phy_write(phydev, MAXIO_PAGE_SELECT ,0); + phy_write(phydev, 0xd, MDIO_MMD_AN); + phy_write(phydev, 0xe, MDIO_AN_EEE_ADV); + phy_write(phydev, 0xd, 0x4000 | MDIO_MMD_AN); + ret = phy_read(phydev, 0x0e); + } else { + ret = -EOPNOTSUPP; + } + phy_write(phydev, MAXIO_PAGE_SELECT, oldpage); + + return ret; +} + +static int maxio_write_mmd(struct phy_device *phydev, int devnum, u16 regnum, u16 val) +{ + int ret = 0, oldpage; + oldpage = phy_read(phydev, MAXIO_PAGE_SELECT); + + if (devnum == MDIO_MMD_AN && regnum == MDIO_AN_EEE_ADV) { // eee info + phy_write(phydev, MAXIO_PAGE_SELECT ,0); + ret |= phy_write(phydev, 0xd, MDIO_MMD_AN); + ret |= phy_write(phydev, 0xe, MDIO_AN_EEE_ADV); + ret |= phy_write(phydev, 0xd, 0x4000 | MDIO_MMD_AN); + ret |= phy_write(phydev, 0xe, val); + msleep(100); + ret |= genphy_restart_aneg(phydev); + } else { + ret = -EOPNOTSUPP; + } + phy_write(phydev, MAXIO_PAGE_SELECT, oldpage); + + return ret; +} + +static int maxio_mae0621a_config_aneg(struct phy_device *phydev) +{ + return genphy_config_aneg(phydev); +} + + +static int maxio_mae0621a_config_init(struct phy_device *phydev) +{ + struct device *dev = &phydev->mdio.dev; + u16 val; + int ret; + u32 broken = 0; + + maxio_mae0621a_clk_init(phydev); + + //disable eee + printk("eee value: 0x%x \n",maxio_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_ADV)); + maxio_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_ADV, 0); + printk("eee value: 0x%x \n",maxio_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_ADV)); + broken |= MDIO_EEE_100TX; + broken |= MDIO_EEE_1000T; + memcpy(phydev->eee_broken_modes, broken, sizeof(phydev->eee_broken_modes)); + + //enable auto_speed_down + ret = maxio_write_paged(phydev, 0xd8f, 0x0, 0x300 ); + + //adjust TX/RX delay + switch (phydev->interface) { + case PHY_INTERFACE_MODE_RGMII: + val = 0x0; + break; + case PHY_INTERFACE_MODE_RGMII_ID: + val = MAXIO_MAE0621A_TX_DELAY | MAXIO_MAE0621A_RX_DELAY; + break; + case PHY_INTERFACE_MODE_RGMII_RXID: + val = MAXIO_MAE0621A_RX_DELAY; + break; + case PHY_INTERFACE_MODE_RGMII_TXID: + val = MAXIO_MAE0621A_TX_DELAY; + break; + default: /* the rest of the modes imply leaving delays as is. */ + goto delay_skip; + } + + ret = maxio_read_paged(phydev, 0xd96, 0x0); + if (ret < 0) { + dev_err(dev, "Failed to update the TX delay register\n"); + return ret; + } + + ret =maxio_write_paged(phydev, 0xd96, 0x0, val|ret ); + if (ret < 0) { + dev_err(dev, "Failed to update the TX delay register\n"); + return ret; + } else if (ret == 0) { + dev_dbg(dev, + "2ns delay was already %s (by pin-strapping RXD1 or bootloader configuration)\n", + val ? "enabled" : "disabled"); + } +delay_skip: + + phy_write(phydev, MII_BMCR, BMCR_RESET | phy_read(phydev, MII_BMCR)); + msleep(1); + phy_write(phydev, MAXIO_PAGE_SELECT, 0x0); + + return 0; +} + + +static int maxio_mae0621a_resume(struct phy_device *phydev) +{ + int ret = genphy_resume(phydev); + + ret |= phy_write(phydev, MII_BMCR, BMCR_RESET | phy_read(phydev, MII_BMCR)); + + msleep(20); + + return ret; +} + +int maxio_mae0621a_suspend(struct phy_device *phydev) +{ + genphy_suspend(phydev); + phy_write(phydev, MAXIO_PAGE_SELECT ,0); + + return 0; +} + +static int maxio_mae0621a_status(struct phy_device *phydev) +{ + return genphy_read_status(phydev); +} + +static int maxio_mae0621a_probe(struct phy_device *phydev) +{ + int ret = maxio_mae0621a_clk_init(phydev); + mdelay(100); + return ret; +} + +static struct phy_driver maxio_nc_drvs[] = { + { + .phy_id = 0x7b744411, + .phy_id_mask = 0x7fffffff, + .name = "MAE0621A Gigabit Ethernet", + .features = PHY_GBIT_FEATURES, + .probe = maxio_mae0621a_probe, + .config_init = maxio_mae0621a_config_init, + .config_aneg = maxio_mae0621a_config_aneg, + .read_status = maxio_mae0621a_status, + .suspend = maxio_mae0621a_suspend, + .resume = maxio_mae0621a_resume, + }, +}; +module_phy_driver(maxio_nc_drvs); + +MODULE_DESCRIPTION("Maxio PHY driver"); +MODULE_AUTHOR("Zhao Yang"); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index 46713d27412b..1a9514360673 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -858,10 +858,19 @@ static int get_phy_c45_ids(struct mii_bus *bus, int addr, */ static int get_phy_c22_id(struct mii_bus *bus, int addr, u32 *phy_id) { int phy_reg; +#ifdef CONFIG_MAXIO_PHY + /* + *An MDIO connects to multiple PHYs requiring write before read. + *This operation does not affect one MDIO connected to a single PHY + *MII_PHYSID2 is a read-only register and writine to it has no effect + */ + mdiobus_write(bus,addr,MII_PHYSID2,0); +#endif + /* Grab the bits from PHYIR1, and put them in the upper half */ phy_reg = mdiobus_read(bus, addr, MII_PHYSID1); if (phy_reg < 0) { /* returning -ENODEV doesn't stop bus scanning */ return (phy_reg == -EIO || phy_reg == -ENODEV) ? -ENODEV : -EIO; -- Created with Armbian build tools https://github.com/armbian/build