Skip to content

Instantly share code, notes, and snippets.

@warpme
Created December 9, 2024 15:42
Show Gist options
  • Save warpme/2705f38fc713f130247fc257e4b3b55a to your computer and use it in GitHub Desktop.
Save warpme/2705f38fc713f130247fc257e4b3b55a to your computer and use it in GitHub Desktop.
From fc33284a82b0c9401c57ae301a72d36766e1c47f Mon Sep 17 00:00:00 2001
From: smp79 <sergey79@gmail.com>
Date: Thu, 16 Mar 2023 03:24:51 +0300
Subject: [PATCH] Add Maxio MAE0621A PHY driver
---
.../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(+)
create mode 100755 drivers/net/phy/maxio.c
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
index 485eec4d4e96f..1a424563e272a 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
@@ -7453,6 +7453,7 @@ int stmmac_suspend(struct device *dev)
return 0;
}
EXPORT_SYMBOL_GPL(stmmac_suspend);
+#define MAXIO_PHY_MAE0621A_ID 0x7b744411
/**
* stmmac_reset_queues_param - reset queue parameters
@@ -7540,6 +7541,10 @@ int stmmac_resume(struct device *dev)
stmmac_free_tx_skbufs(priv);
stmmac_clear_descriptors(priv);
+ 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);
diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig
index 902495afcb381..da92eee5a7fe4 100644
--- a/drivers/net/phy/Kconfig
+++ b/drivers/net/phy/Kconfig
@@ -292,6 +292,11 @@ config ROCKCHIP_PHY
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"
help
diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile
index b2728d00fc9a1..01a08c8f95eb5 100644
--- a/drivers/net/phy/Makefile
+++ b/drivers/net/phy/Makefile
@@ -78,6 +78,7 @@ obj-$(CONFIG_NXP_C45_TJA11XX_PHY) += nxp-c45-tja11xx.o
obj-$(CONFIG_NXP_TJA11XX_PHY) += nxp-tja11xx.o
obj-$(CONFIG_QSEMI_PHY) += qsemi.o
obj-$(CONFIG_REALTEK_PHY) += realtek.o
+obj-$(CONFIG_MAXIO_PHY) += maxio.o
obj-$(CONFIG_RENESAS_PHY) += uPD60620.o
obj-$(CONFIG_ROCKCHIP_PHY) += rockchip.o
obj-$(CONFIG_SMSC_PHY) += smsc.o
diff --git a/drivers/net/phy/maxio.c b/drivers/net/phy/maxio.c
new file mode 100755
index 0000000000000..fd227e86794f5
--- /dev/null
+++ b/drivers/net/phy/maxio.c
@@ -0,0 +1,254 @@
+/*
+ * drivers/net/phy/maxio.c
+ *
+ * Driver for maxio PHYs
+ *
+ * Author: zhao yang <yang_zhao@maxio-tech.com>
+ *
+ * Copyright (c) 2004 maxio technology, Inc.
+ */
+#include <linux/bitops.h>
+#include <linux/phy.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/timer.h>
+#include <linux/netdevice.h>
+
+#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
+
+
+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;
+}
+
+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;
+ phydev->eee_broken_modes = broken;
+
+ //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 e4752050a40e7..3523cedc45056 100644
--- a/drivers/net/phy/phy_device.c
+++ b/drivers/net/phy/phy_device.c
@@ -818,6 +818,15 @@ 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) {
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment