Created
December 9, 2024 15:42
-
-
Save warpme/2705f38fc713f130247fc257e4b3b55a to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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