All of lore.kernel.org
 help / color / mirror / Atom feed
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>


             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.