From: alice.guo@oss.nxp.com
To: u-boot@lists.denx.de, u-boot@lists.u-boot-project.org
Cc: Fabio Estevam <festevam@gmail.com>, Tom Rini <trini@konsulko.com>,
Jerome Forissier <jerome.forissier@arm.com>,
Quentin Schulz <quentin.schulz@cherry.de>,
Peter Robinson <pbrobinson@gmail.com>,
"Lucien.Jheng" <lucienzx159@gmail.com>,
Weijie Gao <weijie.gao@mediatek.com>,
Beiyan Yun <root@infi.wang>, Johan Jonker <jbx6244@gmail.com>,
Siddharth Vadapalli <s-vadapalli@ti.com>,
Sky Huang <SkyLake.Huang@mediatek.com>,
Marek Vasut <marek.vasut+renesas@mailbox.org>,
Alice Guo <alice.guo@nxp.com>
Subject: [PATCH v1] net: phy: Add MaxLinear GPY215C Ethernet PHY driver
Date: Fri, 10 Jul 2026 16:12:34 +0800 [thread overview]
Message-ID: <20260710-phy-v1-1-87f37559b2ed@nxp.com> (raw)
From: Alice Guo <alice.guo@nxp.com>
Add a driver for the MaxLinear GPY215C Ethernet PHY.
Signed-off-by: Alice Guo <alice.guo@nxp.com>
---
CI: https://github.com/u-boot/u-boot/pull/1009/checks
---
drivers/net/phy/Kconfig | 6 +
drivers/net/phy/Makefile | 1 +
drivers/net/phy/mxl_gpy.c | 342 ++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 349 insertions(+)
diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig
index 3f7953d693c..f76636a897a 100644
--- a/drivers/net/phy/Kconfig
+++ b/drivers/net/phy/Kconfig
@@ -242,6 +242,12 @@ config PHY_MSCC
depends on !COMPILE_TEST
bool "Microsemi Corp Ethernet PHYs support"
+config PHY_MXL_GPY
+ bool "MaxLinear GPY215C PHY support"
+ help
+ This adds support for the MaxLinear GPY215C Ethernet PHY,
+ operating in SGMII with auto-negotiation.
+
config PHY_NATSEMI
bool "National Semiconductor Ethernet PHYs support"
diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile
index 83520de7f1f..0e79c03df7e 100644
--- a/drivers/net/phy/Makefile
+++ b/drivers/net/phy/Makefile
@@ -27,6 +27,7 @@ obj-$(CONFIG_PHY_MICREL_KSZ8XXX) += micrel_ksz8xxx.o
obj-$(CONFIG_PHY_MICREL_KSZ90X1) += micrel_ksz90x1.o
obj-$(CONFIG_PHY_MESON_GXL) += meson-gxl.o
obj-$(CONFIG_PHY_MOTORCOMM) += motorcomm.o
+obj-$(CONFIG_PHY_MXL_GPY) += mxl_gpy.o
obj-$(CONFIG_PHY_NATSEMI) += natsemi.o
obj-$(CONFIG_PHY_NXP_C45_TJA11XX) += nxp-c45-tja11xx.o
obj-$(CONFIG_PHY_NXP_TJA11XX) += nxp-tja11xx.o
diff --git a/drivers/net/phy/mxl_gpy.c b/drivers/net/phy/mxl_gpy.c
new file mode 100644
index 00000000000..7445ec5f051
--- /dev/null
+++ b/drivers/net/phy/mxl_gpy.c
@@ -0,0 +1,342 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * MaxLinear GPY215 2.5G Ethernet PHY driver for U-Boot
+ *
+ * Based on Ethernet Network Connection GPY215 (GPY215B1VI, GPY215C0VI) Data
+ * Sheet Revision 1.4
+ * Reference ID 617800
+ *
+ * Copyright 2026 NXP
+ */
+
+#include <phy.h>
+#include <linux/bitops.h>
+#include <linux/delay.h>
+
+/* -----------------------------------------------------------------------
+ * Standard MDIO registers (Clause 22, Device 0)
+ * -----------------------------------------------------------------------
+ */
+#define GPY215_STD_CTRL 0x00
+#define STD_CTRL_RST BIT(15) /* Self-clearing reset */
+#define STD_CTRL_LB BIT(14) /* Loopback */
+#define STD_CTRL_SSL BIT(13) /* Speed select LSB */
+#define STD_CTRL_ANEN BIT(12) /* Auto-negotiation enable */
+#define STD_CTRL_PD BIT(11) /* Power down (SLEEP) */
+#define STD_CTRL_ANRS BIT(9) /* Restart auto-negotiation */
+#define STD_CTRL_DPLX BIT(8) /* Duplex (1=full) */
+#define STD_CTRL_SSM BIT(6) /* Speed select MSB */
+
+#define GPY215_STD_STAT 0x01
+#define STD_STAT_LS BIT(2) /* Link status (latching low) */
+#define STD_STAT_ANEG_DONE BIT(5) /* Auto-neg complete */
+
+#define GPY215_STD_PHYID1 0x02 /* OUI bits [3:18] = 0x67C9 */
+#define GPY215_STD_PHYID2 0x03 /* OUI bits [19:24] + model + rev */
+
+/* PHY ID: OUI = 0x67C9 (bits 3:18) | 0xDC (bits 19:24)
+ * PHYID1 = 0x67C9, PHYID2 = 0xDCxx (model/rev in lower 10 bits)
+ */
+#define GPY215_PHY_ID1 0x67C9
+#define GPY215_PHY_ID2_MASK 0xFC00 /* OUI[19:24] only */
+#define GPY215_PHY_ID2_VAL 0xDC00
+
+/* -----------------------------------------------------------------------
+ * GPY-specific registers (Clause 22, Device 0)
+ * -----------------------------------------------------------------------
+ */
+#define GPY215_PHY_MIISTAT 0x18 /* Register 0.24 */
+#define MIISTAT_LS BIT(10) /* TPI link status */
+#define MIISTAT_DPX BIT(3) /* Duplex (1=full) */
+#define MIISTAT_SPEED_MASK GENMASK(2, 0)
+#define MIISTAT_SPEED_10 0x0
+#define MIISTAT_SPEED_100 0x1
+#define MIISTAT_SPEED_1000 0x2
+#define MIISTAT_SPEED_ANEG 0x3
+#define MIISTAT_SPEED_2500 0x4
+
+#define GPY215_PHY_IMASK 0x19 /* Register 0.25 */
+#define IMASK_LSTC BIT(0) /* Link state change */
+#define IMASK_LSPC BIT(1) /* Link speed change */
+
+#define GPY215_PHY_ISTAT 0x1A /* Register 0.26 (clr-on-rd) */
+#define ISTAT_LSTC BIT(0)
+#define ISTAT_LSPC BIT(1)
+
+/* -----------------------------------------------------------------------
+ * Vendor Specific 1 registers (MMD device 30)
+ * Accessed via Clause 45 or Clause 22 Extended indirect
+ * -----------------------------------------------------------------------
+ */
+#define GPY215_MMD_VSPEC1 30 /* MMD device address */
+
+#define GPY215_VSPEC1_SGMII_CTRL 0x08 /* Register 30.8 */
+#define SGMII_CTRL_RST BIT(15) /* SGMII reset (self-clear) */
+#define SGMII_CTRL_LB BIT(14) /* SGMII loopback */
+#define SGMII_CTRL_ANEN BIT(12) /* SGMII ANEG enable */
+#define SGMII_CTRL_PD BIT(11) /* SGMII power down */
+#define SGMII_CTRL_RXINV BIT(10) /* Invert RX0_M/RX0_P */
+#define SGMII_CTRL_EEE_CAP BIT(7) /* Advertise EEE in ANEG */
+#define SGMII_CTRL_FIXED2G5 BIT(5) /* Force SGMII to 2.5G */
+#define SGMII_CTRL_ANMODE_MASK GENMASK(1, 0)
+#define SGMII_CTRL_ANMODE_1000BX 0x1 /* IEEE 1000Bx Clause 37 */
+#define SGMII_CTRL_ANMODE_CIS_PHY 0x2 /* Cisco SGMII, PHY side */
+#define SGMII_CTRL_ANMODE_CIS_MAC 0x3 /* Cisco SGMII, MAC side */
+/* Default reset value = 0x34DA:
+ * ANEN=1, ANMODE=AN_CIS_PHY(2), FIXED2G5=0
+ */
+
+#define GPY215_VSPEC1_SGMII_STAT 0x09 /* Register 30.9 (read-only) */
+#define SGMII_STAT_ANOK BIT(5) /* ANEG completed */
+#define SGMII_STAT_RF BIT(4) /* Remote fault (latch high) */
+#define SGMII_STAT_ANAB BIT(3) /* ANEG ability (static) */
+#define SGMII_STAT_LS BIT(2) /* SGMII link up (latch low) */
+#define SGMII_STAT_DR_MASK GENMASK(1, 0)
+#define SGMII_STAT_DR_10 0x0
+#define SGMII_STAT_DR_100 0x1
+#define SGMII_STAT_DR_1G 0x2
+#define SGMII_STAT_DR_2G5 0x3
+
+/* -----------------------------------------------------------------------
+ * Timing / retry constants
+ * -----------------------------------------------------------------------
+ */
+#define GPY215_RESET_TIMEOUT_MS 500
+#define GPY215_ANEG_TIMEOUT_MS 5000
+#define GPY215_POLL_INTERVAL_MS 10
+
+/**
+ * gpy215_probe - Verify PHY identity
+ */
+static int gpy215_probe(struct phy_device *phydev)
+{
+ int id1, id2;
+
+ id1 = phy_read(phydev, MDIO_DEVAD_NONE, GPY215_STD_PHYID1);
+ id2 = phy_read(phydev, MDIO_DEVAD_NONE, GPY215_STD_PHYID2);
+
+ if (id1 < 0 || id2 < 0) {
+ printf("GPY215: MDIO read failed (id1=%d id2=%d)\n", id1, id2);
+ return -EIO;
+ }
+
+ if (id1 != GPY215_PHY_ID1 ||
+ (id2 & GPY215_PHY_ID2_MASK) != GPY215_PHY_ID2_VAL) {
+ printf("GPY215: unexpected PHY ID 0x%04X 0x%04X\n", id1, id2);
+ return -ENODEV;
+ }
+
+ debug("GPY215: detected (ID1=0x%04X ID2=0x%04X)\n", id1, id2);
+ return 0;
+}
+
+/**
+ * gpy215_config - Configure PHY for SGMII operation
+ *
+ * The GPY215 SGMII interface comes up automatically after reset with:
+ * - SGMII ANEG enabled (Cisco SGMII, PHY side)
+ * - PHY side SGMII speed tracks TPI link speed
+ */
+static int gpy215_config(struct phy_device *phydev)
+{
+ int val, timeout;
+
+ val = phy_read(phydev, MDIO_DEVAD_NONE, GPY215_STD_CTRL);
+ if (val < 0)
+ return val;
+
+ phy_write(phydev, MDIO_DEVAD_NONE, GPY215_STD_CTRL,
+ val | STD_CTRL_RST);
+
+ /* Wait for self-clearing RST bit */
+ timeout = GPY215_RESET_TIMEOUT_MS / GPY215_POLL_INTERVAL_MS;
+ do {
+ mdelay(GPY215_POLL_INTERVAL_MS);
+ val = phy_read(phydev, MDIO_DEVAD_NONE, GPY215_STD_CTRL);
+ if (val < 0)
+ return val;
+ } while ((val & STD_CTRL_RST) && --timeout);
+
+ if (val & STD_CTRL_RST) {
+ printf("GPY215: reset timeout\n");
+ return -ETIMEDOUT;
+ }
+
+ /* Clear stale interrupt status (read-to-clear) */
+ phy_read(phydev, MDIO_DEVAD_NONE, GPY215_PHY_ISTAT);
+
+ val = phy_read(phydev, MDIO_DEVAD_NONE, GPY215_STD_CTRL);
+ if (val < 0)
+ return val;
+
+ /* Enable ANEG, clear forced speed/duplex bits */
+ val |= STD_CTRL_ANEN;
+ val &= ~(STD_CTRL_SSL | STD_CTRL_SSM | STD_CTRL_PD);
+ phy_write(phydev, MDIO_DEVAD_NONE, GPY215_STD_CTRL, val);
+
+ phy_write(phydev, MDIO_DEVAD_NONE, GPY215_STD_CTRL,
+ val | STD_CTRL_ANRS);
+
+ /* Configure SGMII (Cisco ANEG, PHY side, ANEG enabled) */
+ val = phy_read_mmd(phydev, GPY215_MMD_VSPEC1,
+ GPY215_VSPEC1_SGMII_CTRL);
+ if (val < 0)
+ return val;
+
+ val &= ~SGMII_CTRL_PD;
+ val |= SGMII_CTRL_ANEN;
+ val &= ~SGMII_CTRL_ANMODE_MASK;
+ val |= SGMII_CTRL_ANMODE_CIS_PHY;
+ val &= ~SGMII_CTRL_FIXED2G5;
+ phy_write_mmd(phydev, GPY215_MMD_VSPEC1,
+ GPY215_VSPEC1_SGMII_CTRL, val);
+
+ debug("GPY215: configured for SGMII (Cisco ANEG, PHY side)\n");
+ return 0;
+}
+
+/**
+ * gpy215_startup - Wait for link and report status
+ *
+ * Polls PHY_MIISTAT (TPI link) and VSPEC1_SGMII_STAT (SGMII link).
+ * Updates phydev->speed, phydev->duplex, phydev->link.
+ */
+static int gpy215_startup(struct phy_device *phydev)
+{
+ int miistat, sgmii_stat, val;
+ int timeout = GPY215_ANEG_TIMEOUT_MS / GPY215_POLL_INTERVAL_MS;
+ int speed_code;
+
+ debug("GPY215: waiting for link");
+
+ /* Poll TPI link status */
+ do {
+ mdelay(GPY215_POLL_INTERVAL_MS);
+ miistat = phy_read(phydev, MDIO_DEVAD_NONE,
+ GPY215_PHY_MIISTAT);
+ if (miistat < 0)
+ return miistat;
+ } while (!(miistat & MIISTAT_LS) && --timeout);
+
+ if (!(miistat & MIISTAT_LS)) {
+ printf("GPY215: link down (TPI timeout)\n");
+ phydev->link = 0;
+ return 0;
+ }
+
+ do {
+ mdelay(GPY215_POLL_INTERVAL_MS);
+ val = phy_read(phydev, MDIO_DEVAD_NONE, GPY215_STD_STAT);
+ if (val < 0)
+ return val;
+ } while (!(val & STD_STAT_ANEG_DONE) && --timeout);
+
+ if (!(val & STD_STAT_ANEG_DONE)) {
+ printf("GPY215: auto-negotiation not complete\n");
+ phydev->link = 0;
+ return 0;
+ }
+
+ do {
+ mdelay(GPY215_POLL_INTERVAL_MS);
+ phy_read_mmd(phydev, GPY215_MMD_VSPEC1,
+ GPY215_VSPEC1_SGMII_STAT);
+ sgmii_stat = phy_read_mmd(phydev, GPY215_MMD_VSPEC1,
+ GPY215_VSPEC1_SGMII_STAT);
+ if (sgmii_stat < 0)
+ return sgmii_stat;
+ } while (((sgmii_stat & (SGMII_STAT_LS | SGMII_STAT_ANOK)) !=
+ (SGMII_STAT_LS | SGMII_STAT_ANOK)) && --timeout);
+
+ if ((sgmii_stat & (SGMII_STAT_LS | SGMII_STAT_ANOK)) !=
+ (SGMII_STAT_LS | SGMII_STAT_ANOK)) {
+ printf("GPY215: SGMII not ready (stat=0x%04X link=%d ANOK=%d RF=%d)\n",
+ sgmii_stat,
+ !!(sgmii_stat & SGMII_STAT_LS),
+ !!(sgmii_stat & SGMII_STAT_ANOK),
+ !!(sgmii_stat & SGMII_STAT_RF));
+ phydev->link = 0;
+ return 0;
+ }
+
+ /* Clear interrupt status */
+ phy_read(phydev, MDIO_DEVAD_NONE, GPY215_PHY_ISTAT);
+
+ miistat = phy_read(phydev, MDIO_DEVAD_NONE, GPY215_PHY_MIISTAT);
+ if (miistat < 0)
+ return miistat;
+
+ speed_code = miistat & MIISTAT_SPEED_MASK;
+ switch (speed_code) {
+ case MIISTAT_SPEED_10:
+ phydev->speed = SPEED_10;
+ break;
+ case MIISTAT_SPEED_100:
+ phydev->speed = SPEED_100;
+ break;
+ case MIISTAT_SPEED_1000:
+ phydev->speed = SPEED_1000;
+ break;
+ case MIISTAT_SPEED_2500:
+ phydev->speed = SPEED_2500;
+ break;
+ default:
+ printf("GPY215: speed not resolved (miistat=0x%04X)\n",
+ miistat);
+ phydev->link = 0;
+ return 0;
+ }
+
+ phydev->duplex = (miistat & MIISTAT_DPX) ? DUPLEX_FULL : DUPLEX_HALF;
+ phydev->link = 1;
+
+ debug("GPY215: TPI link up %d Mbit/s %s-duplex\n",
+ phydev->speed,
+ phydev->duplex == DUPLEX_FULL ? "full" : "half");
+
+ debug("GPY215: SGMII status 0x%04X (link=%d ANOK=%d DR=%ld)\n",
+ sgmii_stat,
+ !!(sgmii_stat & SGMII_STAT_LS),
+ !!(sgmii_stat & SGMII_STAT_ANOK),
+ sgmii_stat & SGMII_STAT_DR_MASK);
+
+ return 0;
+}
+
+/**
+ * gpy215_shutdown - Power down SGMII and TPI
+ */
+static int gpy215_shutdown(struct phy_device *phydev)
+{
+ int val;
+
+ val = phy_read_mmd(phydev, GPY215_MMD_VSPEC1,
+ GPY215_VSPEC1_SGMII_CTRL);
+ if (val >= 0)
+ phy_write_mmd(phydev, GPY215_MMD_VSPEC1,
+ GPY215_VSPEC1_SGMII_CTRL,
+ val | SGMII_CTRL_PD);
+
+ val = phy_read(phydev, MDIO_DEVAD_NONE, GPY215_STD_CTRL);
+ if (val >= 0)
+ phy_write(phydev, MDIO_DEVAD_NONE, GPY215_STD_CTRL,
+ val | STD_CTRL_PD);
+
+ return 0;
+}
+
+U_BOOT_PHY_DRIVER(mxl_gpy215c) = {
+ .name = "MaxLinear GPY215C",
+ /*
+ * Match on OUI only. The 22-bit OUI spans PHYID1[15:0] and PHYID2[15:10].
+ * Combined 22-bit PHY ID = (0x67C9 << 6) | (0xDC00 >> 10) = 0x19F277
+ * U-Boot phy_id = (PHYID1 << 16) | PHYID2 with mask 0xFFFFFC00
+ */
+ .uid = (GPY215_PHY_ID1 << 16) | GPY215_PHY_ID2_VAL,
+ .mask = 0xFFFFFC00,
+ .features = PHY_GBIT_FEATURES,
+ .probe = &gpy215_probe,
+ .config = &gpy215_config,
+ .startup = &gpy215_startup,
+ .shutdown = &gpy215_shutdown,
+};
---
base-commit: a18265f1ccb7a272721ed4286ed3b5a6182ff424
change-id: 20260710-phy-a8d7427ab27d
Best regards,
--
Alice Guo <alice.guo@nxp.com>
next reply other threads:[~2026-07-10 8:09 UTC|newest]
Thread overview: 2+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-07-10 8:12 alice.guo [this message]
2026-07-27 13:25 ` [PATCH v1] net: phy: Add MaxLinear GPY215C Ethernet PHY driver Jerome Forissier
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260710-phy-v1-1-87f37559b2ed@nxp.com \
--to=alice.guo@oss.nxp.com \
--cc=SkyLake.Huang@mediatek.com \
--cc=alice.guo@nxp.com \
--cc=festevam@gmail.com \
--cc=jbx6244@gmail.com \
--cc=jerome.forissier@arm.com \
--cc=lucienzx159@gmail.com \
--cc=marek.vasut+renesas@mailbox.org \
--cc=pbrobinson@gmail.com \
--cc=quentin.schulz@cherry.de \
--cc=root@infi.wang \
--cc=s-vadapalli@ti.com \
--cc=trini@konsulko.com \
--cc=u-boot@lists.denx.de \
--cc=u-boot@lists.u-boot-project.org \
--cc=weijie.gao@mediatek.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.