All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v6 1/2] net: phy: add API to read 802.3-c45 IDs
@ 2021-07-19  5:32 Xu Liang
  2021-07-19  5:32 ` [PATCH v6 2/2] net: phy: add Maxlinear GPY115/21x/24x driver Xu Liang
                   ` (2 more replies)
  0 siblings, 3 replies; 12+ messages in thread
From: Xu Liang @ 2021-07-19  5:32 UTC (permalink / raw)
  To: andrew, hkallweit1, netdev, davem, kuba, vee.khee.wong
  Cc: linux, hmehrtens, tmohren, mohammad.athari.ismail, Xu Liang

Add API to read 802.3-c45 IDs so that C22/C45 mixed device can use
C45 APIs without failing ID checks.

Signed-off-by: Xu Liang <lxu@maxlinear.com>
---
v5 changes:
 Fix incorrect prototype name in comment.

 drivers/net/phy/phy_device.c | 14 ++++++++++++++
 include/linux/phy.h          |  1 +
 2 files changed, 15 insertions(+)

diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c
index 5d5f9a9ee768..107aa6d7bc6b 100644
--- a/drivers/net/phy/phy_device.c
+++ b/drivers/net/phy/phy_device.c
@@ -968,6 +968,20 @@ void phy_device_remove(struct phy_device *phydev)
 }
 EXPORT_SYMBOL(phy_device_remove);
 
+/**
+ * phy_get_c45_ids - Read 802.3-c45 IDs for phy device.
+ * @phydev: phy_device structure to read 802.3-c45 IDs
+ *
+ * Returns zero on success, %-EIO on bus access error, or %-ENODEV if
+ * the "devices in package" is invalid.
+ */
+int phy_get_c45_ids(struct phy_device *phydev)
+{
+	return get_phy_c45_ids(phydev->mdio.bus, phydev->mdio.addr,
+			       &phydev->c45_ids);
+}
+EXPORT_SYMBOL(phy_get_c45_ids);
+
 /**
  * phy_find_first - finds the first PHY device on the bus
  * @bus: the target MII bus
diff --git a/include/linux/phy.h b/include/linux/phy.h
index 3b80dc3ed68b..736e1d1a47c4 100644
--- a/include/linux/phy.h
+++ b/include/linux/phy.h
@@ -1431,6 +1431,7 @@ static inline int phy_device_register(struct phy_device *phy)
 static inline void phy_device_free(struct phy_device *phydev) { }
 #endif /* CONFIG_PHYLIB */
 void phy_device_remove(struct phy_device *phydev);
+int phy_get_c45_ids(struct phy_device *phydev);
 int phy_init_hw(struct phy_device *phydev);
 int phy_suspend(struct phy_device *phydev);
 int phy_resume(struct phy_device *phydev);
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH v6 2/2] net: phy: add Maxlinear GPY115/21x/24x driver
  2021-07-19  5:32 [PATCH v6 1/2] net: phy: add API to read 802.3-c45 IDs Xu Liang
@ 2021-07-19  5:32 ` Xu Liang
  2021-07-19 18:30   ` Hauke Mehrtens
                     ` (4 more replies)
  2021-07-19 18:30 ` [PATCH v6 1/2] net: phy: add API to read 802.3-c45 IDs Hauke Mehrtens
  2021-07-19 20:40 ` Andrew Lunn
  2 siblings, 5 replies; 12+ messages in thread
From: Xu Liang @ 2021-07-19  5:32 UTC (permalink / raw)
  To: andrew, hkallweit1, netdev, davem, kuba, vee.khee.wong
  Cc: linux, hmehrtens, tmohren, mohammad.athari.ismail, Xu Liang

Add driver to support the Maxlinear GPY115, GPY211, GPY212, GPY215,
GPY241, GPY245 PHYs. Separate from XWAY PHY driver because this series
has different register layout and new features not supported in XWAY PHY.

Signed-off-by: Xu Liang <lxu@maxlinear.com>
---
v2 changes:
 Fix format warning from checkpath and some comments.
 Use smaller PHY ID mask.
 Split FWV register mask.
 Call phy_trigger_machine if necessary when clear interrupt.
v3 changes:
 Replace unnecessary phy_modify_mmd_changed with phy_modify_mmd
 Move firmware version print to probe.
v4 changes:
 Separate PHY ID for new silicon.
 Use full Maxlinear name in Kconfig.
 Add and use C45 ID read API, and use genphy_c45_pma_read_abilities.
 Use my name instead of company as author.
v5 changes:
 Fix comment for link speed 2.5G.
v6 changes:
 Add GPY241B, GPY241BM, GPY245B models.

 MAINTAINERS               |   6 +
 drivers/net/phy/Kconfig   |   6 +
 drivers/net/phy/Makefile  |   1 +
 drivers/net/phy/mxl-gpy.c | 728 ++++++++++++++++++++++++++++++++++++++
 4 files changed, 741 insertions(+)
 create mode 100644 drivers/net/phy/mxl-gpy.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 6c8be735cc91..59b997201cee 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -11327,6 +11327,12 @@ W:	https://linuxtv.org
 T:	git git://linuxtv.org/media_tree.git
 F:	drivers/media/radio/radio-maxiradio*
 
+MAXLINEAR ETHERNET PHY DRIVER
+M:	Xu Liang <lxu@maxlinear.com>
+L:	netdev@vger.kernel.org
+S:	Supported
+F:	drivers/net/phy/mxl-gpy.c
+
 MCAN MMIO DEVICE DRIVER
 M:	Chandrasekar Ramakrishnan <rcsekar@samsung.com>
 L:	linux-can@vger.kernel.org
diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig
index c56f703ae998..7564ae0c1997 100644
--- a/drivers/net/phy/Kconfig
+++ b/drivers/net/phy/Kconfig
@@ -207,6 +207,12 @@ config MARVELL_88X2222_PHY
 	  Support for the Marvell 88X2222 Dual-port Multi-speed Ethernet
 	  Transceiver.
 
+config MAXLINEAR_GPHY
+	tristate "Maxlinear Ethernet PHYs"
+	help
+	  Support for the Maxlinear GPY115, GPY211, GPY212, GPY215,
+	  GPY241, GPY245 PHYs.
+
 config MEDIATEK_GE_PHY
 	tristate "MediaTek Gigabit Ethernet PHYs"
 	help
diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile
index 172bb193ae6a..b2728d00fc9a 100644
--- a/drivers/net/phy/Makefile
+++ b/drivers/net/phy/Makefile
@@ -64,6 +64,7 @@ obj-$(CONFIG_LXT_PHY)		+= lxt.o
 obj-$(CONFIG_MARVELL_10G_PHY)	+= marvell10g.o
 obj-$(CONFIG_MARVELL_PHY)	+= marvell.o
 obj-$(CONFIG_MARVELL_88X2222_PHY)	+= marvell-88x2222.o
+obj-$(CONFIG_MAXLINEAR_GPHY)	+= mxl-gpy.o
 obj-$(CONFIG_MEDIATEK_GE_PHY)	+= mediatek-ge.o
 obj-$(CONFIG_MESON_GXL_PHY)	+= meson-gxl.o
 obj-$(CONFIG_MICREL_KS8995MA)	+= spi_ks8995.o
diff --git a/drivers/net/phy/mxl-gpy.c b/drivers/net/phy/mxl-gpy.c
new file mode 100644
index 000000000000..12a02d8ce09b
--- /dev/null
+++ b/drivers/net/phy/mxl-gpy.c
@@ -0,0 +1,728 @@
+// SPDX-License-Identifier: GPL-2.0+
+/* Copyright (C) 2021 Maxlinear Corporation
+ * Copyright (C) 2020 Intel Corporation
+ *
+ * Drivers for Maxlinear Ethernet GPY
+ *
+ */
+
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/bitfield.h>
+#include <linux/phy.h>
+#include <linux/netdevice.h>
+
+/* PHY ID */
+#define PHY_ID_GPYx15B_MASK	0xFFFFFFFC
+#define PHY_ID_GPY21xB_MASK	0xFFFFFFF9
+#define PHY_ID_GPY2xx		0x67C9DC00
+#define PHY_ID_GPY115B		0x67C9DF00
+#define PHY_ID_GPY115C		0x67C9DF10
+#define PHY_ID_GPY211B		0x67C9DE08
+#define PHY_ID_GPY211C		0x67C9DE10
+#define PHY_ID_GPY212B		0x67C9DE09
+#define PHY_ID_GPY212C		0x67C9DE20
+#define PHY_ID_GPY215B		0x67C9DF04
+#define PHY_ID_GPY215C		0x67C9DF20
+#define PHY_ID_GPY241B		0x67C9DE40
+#define PHY_ID_GPY241BM		0x67C9DE80
+#define PHY_ID_GPY245B		0x67C9DEC0
+
+#define PHY_MIISTAT		0x18	/* MII state */
+#define PHY_IMASK		0x19	/* interrupt mask */
+#define PHY_ISTAT		0x1A	/* interrupt status */
+#define PHY_FWV			0x1E	/* firmware version */
+
+#define PHY_MIISTAT_SPD_MASK	GENMASK(2, 0)
+#define PHY_MIISTAT_DPX		BIT(3)
+#define PHY_MIISTAT_LS		BIT(10)
+
+#define PHY_MIISTAT_SPD_10	0
+#define PHY_MIISTAT_SPD_100	1
+#define PHY_MIISTAT_SPD_1000	2
+#define PHY_MIISTAT_SPD_2500	4
+
+#define PHY_IMASK_WOL		BIT(15)	/* Wake-on-LAN */
+#define PHY_IMASK_ANC		BIT(10)	/* Auto-Neg complete */
+#define PHY_IMASK_ADSC		BIT(5)	/* Link auto-downspeed detect */
+#define PHY_IMASK_DXMC		BIT(2)	/* Duplex mode change */
+#define PHY_IMASK_LSPC		BIT(1)	/* Link speed change */
+#define PHY_IMASK_LSTC		BIT(0)	/* Link state change */
+#define PHY_IMASK_MASK		(PHY_IMASK_LSTC | \
+				 PHY_IMASK_LSPC | \
+				 PHY_IMASK_DXMC | \
+				 PHY_IMASK_ADSC | \
+				 PHY_IMASK_ANC)
+
+#define PHY_FWV_REL_MASK	BIT(15)
+#define PHY_FWV_TYPE_MASK	GENMASK(11, 8)
+#define PHY_FWV_MINOR_MASK	GENMASK(7, 0)
+
+/* SGMII */
+#define VSPEC1_SGMII_CTRL	0x08
+#define VSPEC1_SGMII_CTRL_ANEN	BIT(12)		/* Aneg enable */
+#define VSPEC1_SGMII_CTRL_ANRS	BIT(9)		/* Restart Aneg */
+#define VSPEC1_SGMII_ANEN_ANRS	(VSPEC1_SGMII_CTRL_ANEN | \
+				 VSPEC1_SGMII_CTRL_ANRS)
+
+/* WoL */
+#define VPSPEC2_WOL_CTL		0x0E06
+#define VPSPEC2_WOL_AD01	0x0E08
+#define VPSPEC2_WOL_AD23	0x0E09
+#define VPSPEC2_WOL_AD45	0x0E0A
+#define WOL_EN			BIT(0)
+
+static const struct {
+	int type;
+	int minor;
+} ver_need_sgmii_reaneg[] = {
+	{7, 0x6D},
+	{8, 0x6D},
+	{9, 0x73},
+};
+
+static int gpy_config_init(struct phy_device *phydev)
+{
+	int ret;
+
+	/* Mask all interrupts */
+	ret = phy_write(phydev, PHY_IMASK, 0);
+	if (ret)
+		return ret;
+
+	/* Clear all pending interrupts */
+	ret = phy_read(phydev, PHY_ISTAT);
+	return ret < 0 ? ret : 0;
+}
+
+static int gpy_probe(struct phy_device *phydev)
+{
+	int ret;
+
+	if (!phydev->is_c45) {
+		ret = phy_get_c45_ids(phydev);
+		if (ret < 0)
+			return ret;
+	}
+
+	/* Show GPY PHY FW version in dmesg */
+	ret = phy_read(phydev, PHY_FWV);
+	if (ret < 0)
+		return ret;
+
+	phydev_info(phydev, "Firmware Version: 0x%04X (%s)\n", ret,
+		    (ret & PHY_FWV_REL_MASK) ? "release" : "test");
+
+	return 0;
+}
+
+static bool gpy_sgmii_need_reaneg(struct phy_device *phydev)
+{
+	int fw_ver, fw_type, fw_minor;
+	size_t i;
+
+	fw_ver = phy_read(phydev, PHY_FWV);
+	if (fw_ver < 0)
+		return true;
+
+	fw_type = FIELD_GET(PHY_FWV_TYPE_MASK, fw_ver);
+	fw_minor = FIELD_GET(PHY_FWV_MINOR_MASK, fw_ver);
+
+	for (i = 0; i < ARRAY_SIZE(ver_need_sgmii_reaneg); i++) {
+		if (fw_type != ver_need_sgmii_reaneg[i].type)
+			continue;
+		if (fw_minor < ver_need_sgmii_reaneg[i].minor)
+			return true;
+		break;
+	}
+
+	return false;
+}
+
+static bool gpy_2500basex_chk(struct phy_device *phydev)
+{
+	int ret;
+
+	ret = phy_read(phydev, PHY_MIISTAT);
+	if (ret < 0) {
+		phydev_err(phydev, "Error: MDIO register access failed: %d\n",
+			   ret);
+		return false;
+	}
+
+	if (!(ret & PHY_MIISTAT_LS) ||
+	    FIELD_GET(PHY_MIISTAT_SPD_MASK, ret) != PHY_MIISTAT_SPD_2500)
+		return false;
+
+	phydev->speed = SPEED_2500;
+	phydev->interface = PHY_INTERFACE_MODE_2500BASEX;
+	phy_modify_mmd(phydev, MDIO_MMD_VEND1, VSPEC1_SGMII_CTRL,
+		       VSPEC1_SGMII_CTRL_ANEN, 0);
+	return true;
+}
+
+static bool gpy_sgmii_aneg_en(struct phy_device *phydev)
+{
+	int ret;
+
+	ret = phy_read_mmd(phydev, MDIO_MMD_VEND1, VSPEC1_SGMII_CTRL);
+	if (ret < 0) {
+		phydev_err(phydev, "Error: MMD register access failed: %d\n",
+			   ret);
+		return true;
+	}
+
+	return (ret & VSPEC1_SGMII_CTRL_ANEN) ? true : false;
+}
+
+static int gpy_config_aneg(struct phy_device *phydev)
+{
+	bool changed = false;
+	u32 adv;
+	int ret;
+
+	if (phydev->autoneg == AUTONEG_DISABLE) {
+		/* Configure half duplex with genphy_setup_forced,
+		 * because genphy_c45_pma_setup_forced does not support.
+		 */
+		return phydev->duplex != DUPLEX_FULL
+			? genphy_setup_forced(phydev)
+			: genphy_c45_pma_setup_forced(phydev);
+	}
+
+	ret = genphy_c45_an_config_aneg(phydev);
+	if (ret < 0)
+		return ret;
+	if (ret > 0)
+		changed = true;
+
+	adv = linkmode_adv_to_mii_ctrl1000_t(phydev->advertising);
+	ret = phy_modify_changed(phydev, MII_CTRL1000,
+				 ADVERTISE_1000FULL | ADVERTISE_1000HALF,
+				 adv);
+	if (ret < 0)
+		return ret;
+	if (ret > 0)
+		changed = true;
+
+	ret = genphy_c45_check_and_restart_aneg(phydev, changed);
+	if (ret < 0)
+		return ret;
+
+	if (phydev->interface == PHY_INTERFACE_MODE_USXGMII ||
+	    phydev->interface == PHY_INTERFACE_MODE_INTERNAL)
+		return 0;
+
+	/* No need to trigger re-ANEG if link speed is 2.5G or SGMII ANEG is
+	 * disabled.
+	 */
+	if (!gpy_sgmii_need_reaneg(phydev) || gpy_2500basex_chk(phydev) ||
+	    !gpy_sgmii_aneg_en(phydev))
+		return 0;
+
+	/* There is a design constraint in GPY2xx device where SGMII AN is
+	 * only triggered when there is change of speed. If, PHY link
+	 * partner`s speed is still same even after PHY TPI is down and up
+	 * again, SGMII AN is not triggered and hence no new in-band message
+	 * from GPY to MAC side SGMII.
+	 * This could cause an issue during power up, when PHY is up prior to
+	 * MAC. At this condition, once MAC side SGMII is up, MAC side SGMII
+	 * wouldn`t receive new in-band message from GPY with correct link
+	 * status, speed and duplex info.
+	 *
+	 * 1) If PHY is already up and TPI link status is still down (such as
+	 *    hard reboot), TPI link status is polled for 4 seconds before
+	 *    retriggerring SGMII AN.
+	 * 2) If PHY is already up and TPI link status is also up (such as soft
+	 *    reboot), polling of TPI link status is not needed and SGMII AN is
+	 *    immediately retriggered.
+	 * 3) Other conditions such as PHY is down, speed change etc, skip
+	 *    retriggering SGMII AN. Note: in case of speed change, GPY FW will
+	 *    initiate SGMII AN.
+	 */
+
+	if (phydev->state != PHY_UP)
+		return 0;
+
+	ret = phy_read_poll_timeout(phydev, MII_BMSR, ret, ret & BMSR_LSTATUS,
+				    20000, 4000000, false);
+	if (ret == -ETIMEDOUT)
+		return 0;
+	else if (ret < 0)
+		return ret;
+
+	/* Trigger SGMII AN. */
+	return phy_modify_mmd(phydev, MDIO_MMD_VEND1, VSPEC1_SGMII_CTRL,
+			      VSPEC1_SGMII_CTRL_ANRS, VSPEC1_SGMII_CTRL_ANRS);
+}
+
+static void gpy_update_interface(struct phy_device *phydev)
+{
+	int ret;
+
+	/* Interface mode is fixed for USXGMII and integrated PHY */
+	if (phydev->interface == PHY_INTERFACE_MODE_USXGMII ||
+	    phydev->interface == PHY_INTERFACE_MODE_INTERNAL)
+		return;
+
+	/* Automatically switch SERDES interface between SGMII and 2500-BaseX
+	 * according to speed. Disable ANEG in 2500-BaseX mode.
+	 */
+	switch (phydev->speed) {
+	case SPEED_2500:
+		phydev->interface = PHY_INTERFACE_MODE_2500BASEX;
+		ret = phy_modify_mmd(phydev, MDIO_MMD_VEND1, VSPEC1_SGMII_CTRL,
+				     VSPEC1_SGMII_CTRL_ANEN, 0);
+		if (ret < 0)
+			phydev_err(phydev,
+				   "Error: Disable of SGMII ANEG failed: %d\n",
+				   ret);
+		break;
+	case SPEED_1000:
+	case SPEED_100:
+	case SPEED_10:
+		phydev->interface = PHY_INTERFACE_MODE_SGMII;
+		if (gpy_sgmii_aneg_en(phydev))
+			break;
+		/* Enable and restart SGMII ANEG for 10/100/1000Mbps link speed
+		 * if ANEG is disabled (in 2500-BaseX mode).
+		 */
+		ret = phy_modify_mmd(phydev, MDIO_MMD_VEND1, VSPEC1_SGMII_CTRL,
+				     VSPEC1_SGMII_ANEN_ANRS,
+				     VSPEC1_SGMII_ANEN_ANRS);
+		if (ret < 0)
+			phydev_err(phydev,
+				   "Error: Enable of SGMII ANEG failed: %d\n",
+				   ret);
+		break;
+	}
+}
+
+static int gpy_read_status(struct phy_device *phydev)
+{
+	int ret;
+
+	ret = genphy_update_link(phydev);
+	if (ret)
+		return ret;
+
+	phydev->speed = SPEED_UNKNOWN;
+	phydev->duplex = DUPLEX_UNKNOWN;
+	phydev->pause = 0;
+	phydev->asym_pause = 0;
+
+	if (phydev->autoneg == AUTONEG_ENABLE && phydev->autoneg_complete) {
+		ret = genphy_c45_read_lpa(phydev);
+		if (ret < 0)
+			return ret;
+
+		/* Read the link partner's 1G advertisement */
+		ret = phy_read(phydev, MII_STAT1000);
+		if (ret < 0)
+			return ret;
+		mii_stat1000_mod_linkmode_lpa_t(phydev->lp_advertising, ret);
+	} else if (phydev->autoneg == AUTONEG_DISABLE) {
+		linkmode_zero(phydev->lp_advertising);
+	}
+
+	ret = phy_read(phydev, PHY_MIISTAT);
+	if (ret < 0)
+		return ret;
+
+	phydev->link = (ret & PHY_MIISTAT_LS) ? 1 : 0;
+	phydev->duplex = (ret & PHY_MIISTAT_DPX) ? DUPLEX_FULL : DUPLEX_HALF;
+	switch (FIELD_GET(PHY_MIISTAT_SPD_MASK, ret)) {
+	case PHY_MIISTAT_SPD_10:
+		phydev->speed = SPEED_10;
+		break;
+	case PHY_MIISTAT_SPD_100:
+		phydev->speed = SPEED_100;
+		break;
+	case PHY_MIISTAT_SPD_1000:
+		phydev->speed = SPEED_1000;
+		break;
+	case PHY_MIISTAT_SPD_2500:
+		phydev->speed = SPEED_2500;
+		break;
+	}
+
+	if (phydev->link)
+		gpy_update_interface(phydev);
+
+	return 0;
+}
+
+static int gpy_config_intr(struct phy_device *phydev)
+{
+	u16 mask = 0;
+
+	if (phydev->interrupts == PHY_INTERRUPT_ENABLED)
+		mask = PHY_IMASK_MASK;
+
+	return phy_write(phydev, PHY_IMASK, mask);
+}
+
+static irqreturn_t gpy_handle_interrupt(struct phy_device *phydev)
+{
+	int reg;
+
+	reg = phy_read(phydev, PHY_ISTAT);
+	if (reg < 0) {
+		phy_error(phydev);
+		return IRQ_NONE;
+	}
+
+	if (!(reg & PHY_IMASK_MASK))
+		return IRQ_NONE;
+
+	phy_trigger_machine(phydev);
+
+	return IRQ_HANDLED;
+}
+
+static int gpy_set_wol(struct phy_device *phydev,
+		       struct ethtool_wolinfo *wol)
+{
+	struct net_device *attach_dev = phydev->attached_dev;
+	int ret;
+
+	if (wol->wolopts & WAKE_MAGIC) {
+		/* MAC address - Byte0:Byte1:Byte2:Byte3:Byte4:Byte5
+		 * VPSPEC2_WOL_AD45 = Byte0:Byte1
+		 * VPSPEC2_WOL_AD23 = Byte2:Byte3
+		 * VPSPEC2_WOL_AD01 = Byte4:Byte5
+		 */
+		ret = phy_set_bits_mmd(phydev, MDIO_MMD_VEND2,
+				       VPSPEC2_WOL_AD45,
+				       ((attach_dev->dev_addr[0] << 8) |
+				       attach_dev->dev_addr[1]));
+		if (ret < 0)
+			return ret;
+
+		ret = phy_set_bits_mmd(phydev, MDIO_MMD_VEND2,
+				       VPSPEC2_WOL_AD23,
+				       ((attach_dev->dev_addr[2] << 8) |
+				       attach_dev->dev_addr[3]));
+		if (ret < 0)
+			return ret;
+
+		ret = phy_set_bits_mmd(phydev, MDIO_MMD_VEND2,
+				       VPSPEC2_WOL_AD01,
+				       ((attach_dev->dev_addr[4] << 8) |
+				       attach_dev->dev_addr[5]));
+		if (ret < 0)
+			return ret;
+
+		/* Enable the WOL interrupt */
+		ret = phy_write(phydev, PHY_IMASK, PHY_IMASK_WOL);
+		if (ret < 0)
+			return ret;
+
+		/* Enable magic packet matching */
+		ret = phy_set_bits_mmd(phydev, MDIO_MMD_VEND2,
+				       VPSPEC2_WOL_CTL,
+				       WOL_EN);
+		if (ret < 0)
+			return ret;
+
+		/* Clear the interrupt status register.
+		 * Only WoL is enabled so clear all.
+		 */
+		ret = phy_read(phydev, PHY_ISTAT);
+		if (ret < 0)
+			return ret;
+	} else {
+		/* Disable magic packet matching */
+		ret = phy_clear_bits_mmd(phydev, MDIO_MMD_VEND2,
+					 VPSPEC2_WOL_CTL,
+					 WOL_EN);
+		if (ret < 0)
+			return ret;
+	}
+
+	if (wol->wolopts & WAKE_PHY) {
+		/* Enable the link state change interrupt */
+		ret = phy_set_bits(phydev, PHY_IMASK, PHY_IMASK_LSTC);
+		if (ret < 0)
+			return ret;
+
+		/* Clear the interrupt status register */
+		ret = phy_read(phydev, PHY_ISTAT);
+		if (ret < 0)
+			return ret;
+
+		if (ret & (PHY_IMASK_MASK & ~PHY_IMASK_LSTC))
+			phy_trigger_machine(phydev);
+
+		return 0;
+	}
+
+	/* Disable the link state change interrupt */
+	return phy_clear_bits(phydev, PHY_IMASK, PHY_IMASK_LSTC);
+}
+
+static void gpy_get_wol(struct phy_device *phydev,
+			struct ethtool_wolinfo *wol)
+{
+	int ret;
+
+	wol->supported = WAKE_MAGIC | WAKE_PHY;
+	wol->wolopts = 0;
+
+	ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, VPSPEC2_WOL_CTL);
+	if (ret & WOL_EN)
+		wol->wolopts |= WAKE_MAGIC;
+
+	ret = phy_read(phydev, PHY_IMASK);
+	if (ret & PHY_IMASK_LSTC)
+		wol->wolopts |= WAKE_PHY;
+}
+
+static int gpy_loopback(struct phy_device *phydev, bool enable)
+{
+	int ret;
+
+	ret = phy_modify(phydev, MII_BMCR, BMCR_LOOPBACK,
+			 enable ? BMCR_LOOPBACK : 0);
+	if (!ret) {
+		/* It takes some time for PHY device to switch
+		 * into/out-of loopback mode.
+		 */
+		msleep(100);
+	}
+
+	return ret;
+}
+
+static struct phy_driver gpy_drivers[] = {
+	{
+		PHY_ID_MATCH_MODEL(PHY_ID_GPY2xx),
+		.name		= "Maxlinear Ethernet GPY2xx",
+		.get_features	= genphy_c45_pma_read_abilities,
+		.config_init	= gpy_config_init,
+		.probe		= gpy_probe,
+		.suspend	= genphy_suspend,
+		.resume		= genphy_resume,
+		.config_aneg	= gpy_config_aneg,
+		.aneg_done	= genphy_c45_aneg_done,
+		.read_status	= gpy_read_status,
+		.config_intr	= gpy_config_intr,
+		.handle_interrupt = gpy_handle_interrupt,
+		.set_wol	= gpy_set_wol,
+		.get_wol	= gpy_get_wol,
+		.set_loopback	= gpy_loopback,
+	},
+	{
+		.phy_id		= PHY_ID_GPY115B,
+		.phy_id_mask	= PHY_ID_GPYx15B_MASK,
+		.name		= "Maxlinear Ethernet GPY115B",
+		.get_features	= genphy_c45_pma_read_abilities,
+		.config_init	= gpy_config_init,
+		.probe		= gpy_probe,
+		.suspend	= genphy_suspend,
+		.resume		= genphy_resume,
+		.config_aneg	= gpy_config_aneg,
+		.aneg_done	= genphy_c45_aneg_done,
+		.read_status	= gpy_read_status,
+		.config_intr	= gpy_config_intr,
+		.handle_interrupt = gpy_handle_interrupt,
+		.set_wol	= gpy_set_wol,
+		.get_wol	= gpy_get_wol,
+		.set_loopback	= gpy_loopback,
+	},
+	{
+		PHY_ID_MATCH_MODEL(PHY_ID_GPY115C),
+		.name		= "Maxlinear Ethernet GPY115C",
+		.get_features	= genphy_c45_pma_read_abilities,
+		.config_init	= gpy_config_init,
+		.probe		= gpy_probe,
+		.suspend	= genphy_suspend,
+		.resume		= genphy_resume,
+		.config_aneg	= gpy_config_aneg,
+		.aneg_done	= genphy_c45_aneg_done,
+		.read_status	= gpy_read_status,
+		.config_intr	= gpy_config_intr,
+		.handle_interrupt = gpy_handle_interrupt,
+		.set_wol	= gpy_set_wol,
+		.get_wol	= gpy_get_wol,
+		.set_loopback	= gpy_loopback,
+	},
+	{
+		.phy_id		= PHY_ID_GPY211B,
+		.phy_id_mask	= PHY_ID_GPY21xB_MASK,
+		.name		= "Maxlinear Ethernet GPY211B",
+		.get_features	= genphy_c45_pma_read_abilities,
+		.config_init	= gpy_config_init,
+		.probe		= gpy_probe,
+		.suspend	= genphy_suspend,
+		.resume		= genphy_resume,
+		.config_aneg	= gpy_config_aneg,
+		.aneg_done	= genphy_c45_aneg_done,
+		.read_status	= gpy_read_status,
+		.config_intr	= gpy_config_intr,
+		.handle_interrupt = gpy_handle_interrupt,
+		.set_wol	= gpy_set_wol,
+		.get_wol	= gpy_get_wol,
+		.set_loopback	= gpy_loopback,
+	},
+	{
+		PHY_ID_MATCH_MODEL(PHY_ID_GPY211C),
+		.name		= "Maxlinear Ethernet GPY211C",
+		.get_features	= genphy_c45_pma_read_abilities,
+		.config_init	= gpy_config_init,
+		.probe		= gpy_probe,
+		.suspend	= genphy_suspend,
+		.resume		= genphy_resume,
+		.config_aneg	= gpy_config_aneg,
+		.aneg_done	= genphy_c45_aneg_done,
+		.read_status	= gpy_read_status,
+		.config_intr	= gpy_config_intr,
+		.handle_interrupt = gpy_handle_interrupt,
+		.set_wol	= gpy_set_wol,
+		.get_wol	= gpy_get_wol,
+		.set_loopback	= gpy_loopback,
+	},
+	{
+		.phy_id		= PHY_ID_GPY212B,
+		.phy_id_mask	= PHY_ID_GPY21xB_MASK,
+		.name		= "Maxlinear Ethernet GPY212B",
+		.get_features	= genphy_c45_pma_read_abilities,
+		.config_init	= gpy_config_init,
+		.probe		= gpy_probe,
+		.suspend	= genphy_suspend,
+		.resume		= genphy_resume,
+		.config_aneg	= gpy_config_aneg,
+		.aneg_done	= genphy_c45_aneg_done,
+		.read_status	= gpy_read_status,
+		.config_intr	= gpy_config_intr,
+		.handle_interrupt = gpy_handle_interrupt,
+		.set_wol	= gpy_set_wol,
+		.get_wol	= gpy_get_wol,
+		.set_loopback	= gpy_loopback,
+	},
+	{
+		PHY_ID_MATCH_MODEL(PHY_ID_GPY212C),
+		.name		= "Maxlinear Ethernet GPY212C",
+		.get_features	= genphy_c45_pma_read_abilities,
+		.config_init	= gpy_config_init,
+		.probe		= gpy_probe,
+		.suspend	= genphy_suspend,
+		.resume		= genphy_resume,
+		.config_aneg	= gpy_config_aneg,
+		.aneg_done	= genphy_c45_aneg_done,
+		.read_status	= gpy_read_status,
+		.config_intr	= gpy_config_intr,
+		.handle_interrupt = gpy_handle_interrupt,
+		.set_wol	= gpy_set_wol,
+		.get_wol	= gpy_get_wol,
+		.set_loopback	= gpy_loopback,
+	},
+	{
+		.phy_id		= PHY_ID_GPY215B,
+		.phy_id_mask	= PHY_ID_GPYx15B_MASK,
+		.name		= "Maxlinear Ethernet GPY215B",
+		.get_features	= genphy_c45_pma_read_abilities,
+		.config_init	= gpy_config_init,
+		.probe		= gpy_probe,
+		.suspend	= genphy_suspend,
+		.resume		= genphy_resume,
+		.config_aneg	= gpy_config_aneg,
+		.aneg_done	= genphy_c45_aneg_done,
+		.read_status	= gpy_read_status,
+		.config_intr	= gpy_config_intr,
+		.handle_interrupt = gpy_handle_interrupt,
+		.set_wol	= gpy_set_wol,
+		.get_wol	= gpy_get_wol,
+		.set_loopback	= gpy_loopback,
+	},
+	{
+		PHY_ID_MATCH_MODEL(PHY_ID_GPY215C),
+		.name		= "Maxlinear Ethernet GPY215C",
+		.get_features	= genphy_c45_pma_read_abilities,
+		.config_init	= gpy_config_init,
+		.probe		= gpy_probe,
+		.suspend	= genphy_suspend,
+		.resume		= genphy_resume,
+		.config_aneg	= gpy_config_aneg,
+		.aneg_done	= genphy_c45_aneg_done,
+		.read_status	= gpy_read_status,
+		.config_intr	= gpy_config_intr,
+		.handle_interrupt = gpy_handle_interrupt,
+		.set_wol	= gpy_set_wol,
+		.get_wol	= gpy_get_wol,
+		.set_loopback	= gpy_loopback,
+	},
+	{
+		PHY_ID_MATCH_MODEL(PHY_ID_GPY241B),
+		.name		= "Maxlinear Ethernet GPY241B",
+		.get_features	= genphy_c45_pma_read_abilities,
+		.config_init	= gpy_config_init,
+		.probe		= gpy_probe,
+		.suspend	= genphy_suspend,
+		.resume		= genphy_resume,
+		.config_aneg	= gpy_config_aneg,
+		.aneg_done	= genphy_c45_aneg_done,
+		.read_status	= gpy_read_status,
+		.config_intr	= gpy_config_intr,
+		.handle_interrupt = gpy_handle_interrupt,
+		.set_wol	= gpy_set_wol,
+		.get_wol	= gpy_get_wol,
+		.set_loopback	= gpy_loopback,
+	},
+	{
+		PHY_ID_MATCH_MODEL(PHY_ID_GPY241BM),
+		.name		= "Maxlinear Ethernet GPY241BM",
+		.get_features	= genphy_c45_pma_read_abilities,
+		.config_init	= gpy_config_init,
+		.probe		= gpy_probe,
+		.suspend	= genphy_suspend,
+		.resume		= genphy_resume,
+		.config_aneg	= gpy_config_aneg,
+		.aneg_done	= genphy_c45_aneg_done,
+		.read_status	= gpy_read_status,
+		.config_intr	= gpy_config_intr,
+		.handle_interrupt = gpy_handle_interrupt,
+		.set_wol	= gpy_set_wol,
+		.get_wol	= gpy_get_wol,
+		.set_loopback	= gpy_loopback,
+	},
+	{
+		PHY_ID_MATCH_MODEL(PHY_ID_GPY245B),
+		.name		= "Maxlinear Ethernet GPY245B",
+		.get_features	= genphy_c45_pma_read_abilities,
+		.config_init	= gpy_config_init,
+		.probe		= gpy_probe,
+		.suspend	= genphy_suspend,
+		.resume		= genphy_resume,
+		.config_aneg	= gpy_config_aneg,
+		.aneg_done	= genphy_c45_aneg_done,
+		.read_status	= gpy_read_status,
+		.config_intr	= gpy_config_intr,
+		.handle_interrupt = gpy_handle_interrupt,
+		.set_wol	= gpy_set_wol,
+		.get_wol	= gpy_get_wol,
+		.set_loopback	= gpy_loopback,
+	},
+};
+module_phy_driver(gpy_drivers);
+
+static struct mdio_device_id __maybe_unused gpy_tbl[] = {
+	{PHY_ID_MATCH_MODEL(PHY_ID_GPY2xx)},
+	{PHY_ID_GPY115B, PHY_ID_GPYx15B_MASK},
+	{PHY_ID_MATCH_MODEL(PHY_ID_GPY115C)},
+	{PHY_ID_GPY211B, PHY_ID_GPY21xB_MASK},
+	{PHY_ID_MATCH_MODEL(PHY_ID_GPY211C)},
+	{PHY_ID_GPY212B, PHY_ID_GPY21xB_MASK},
+	{PHY_ID_MATCH_MODEL(PHY_ID_GPY212C)},
+	{PHY_ID_GPY215B, PHY_ID_GPYx15B_MASK},
+	{PHY_ID_MATCH_MODEL(PHY_ID_GPY215C)},
+	{PHY_ID_MATCH_MODEL(PHY_ID_GPY241B)},
+	{PHY_ID_MATCH_MODEL(PHY_ID_GPY241BM)},
+	{PHY_ID_MATCH_MODEL(PHY_ID_GPY245B)},
+	{ }
+};
+MODULE_DEVICE_TABLE(mdio, gpy_tbl);
+
+MODULE_DESCRIPTION("Maxlinear Ethernet GPY Driver");
+MODULE_AUTHOR("Xu Liang");
+MODULE_LICENSE("GPL");
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* Re: [PATCH v6 2/2] net: phy: add Maxlinear GPY115/21x/24x driver
  2021-07-19  5:32 ` [PATCH v6 2/2] net: phy: add Maxlinear GPY115/21x/24x driver Xu Liang
@ 2021-07-19 18:30   ` Hauke Mehrtens
  2021-07-19 20:48   ` Andrew Lunn
                     ` (3 subsequent siblings)
  4 siblings, 0 replies; 12+ messages in thread
From: Hauke Mehrtens @ 2021-07-19 18:30 UTC (permalink / raw)
  To: Xu Liang, andrew, hkallweit1, netdev, davem, kuba, vee.khee.wong
  Cc: linux, hmehrtens, tmohren, mohammad.athari.ismail

On 7/19/21 7:32 AM, Xu Liang wrote:
> Add driver to support the Maxlinear GPY115, GPY211, GPY212, GPY215,
> GPY241, GPY245 PHYs. Separate from XWAY PHY driver because this series
> has different register layout and new features not supported in XWAY PHY.
> 
> Signed-off-by: Xu Liang <lxu@maxlinear.com>

Acked-by: Hauke Mehrtens <hmehrtens@maxlinear.com>

> ---
> v2 changes:
>   Fix format warning from checkpath and some comments.
>   Use smaller PHY ID mask.
>   Split FWV register mask.
>   Call phy_trigger_machine if necessary when clear interrupt.
> v3 changes:
>   Replace unnecessary phy_modify_mmd_changed with phy_modify_mmd
>   Move firmware version print to probe.
> v4 changes:
>   Separate PHY ID for new silicon.
>   Use full Maxlinear name in Kconfig.
>   Add and use C45 ID read API, and use genphy_c45_pma_read_abilities.
>   Use my name instead of company as author.
> v5 changes:
>   Fix comment for link speed 2.5G.
> v6 changes:
>   Add GPY241B, GPY241BM, GPY245B models.
> 
>   MAINTAINERS               |   6 +
>   drivers/net/phy/Kconfig   |   6 +
>   drivers/net/phy/Makefile  |   1 +
>   drivers/net/phy/mxl-gpy.c | 728 ++++++++++++++++++++++++++++++++++++++
>   4 files changed, 741 insertions(+)
>   create mode 100644 drivers/net/phy/mxl-gpy.c
> 
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 6c8be735cc91..59b997201cee 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -11327,6 +11327,12 @@ W:	https://linuxtv.org
>   T:	git git://linuxtv.org/media_tree.git
>   F:	drivers/media/radio/radio-maxiradio*
>   
> +MAXLINEAR ETHERNET PHY DRIVER
> +M:	Xu Liang <lxu@maxlinear.com>
> +L:	netdev@vger.kernel.org
> +S:	Supported
> +F:	drivers/net/phy/mxl-gpy.c
> +
>   MCAN MMIO DEVICE DRIVER
>   M:	Chandrasekar Ramakrishnan <rcsekar@samsung.com>
>   L:	linux-can@vger.kernel.org
> diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig
> index c56f703ae998..7564ae0c1997 100644
> --- a/drivers/net/phy/Kconfig
> +++ b/drivers/net/phy/Kconfig
> @@ -207,6 +207,12 @@ config MARVELL_88X2222_PHY
>   	  Support for the Marvell 88X2222 Dual-port Multi-speed Ethernet
>   	  Transceiver.
>   
> +config MAXLINEAR_GPHY
> +	tristate "Maxlinear Ethernet PHYs"
> +	help
> +	  Support for the Maxlinear GPY115, GPY211, GPY212, GPY215,
> +	  GPY241, GPY245 PHYs.
> +
>   config MEDIATEK_GE_PHY
>   	tristate "MediaTek Gigabit Ethernet PHYs"
>   	help
> diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile
> index 172bb193ae6a..b2728d00fc9a 100644
> --- a/drivers/net/phy/Makefile
> +++ b/drivers/net/phy/Makefile
> @@ -64,6 +64,7 @@ obj-$(CONFIG_LXT_PHY)		+= lxt.o
>   obj-$(CONFIG_MARVELL_10G_PHY)	+= marvell10g.o
>   obj-$(CONFIG_MARVELL_PHY)	+= marvell.o
>   obj-$(CONFIG_MARVELL_88X2222_PHY)	+= marvell-88x2222.o
> +obj-$(CONFIG_MAXLINEAR_GPHY)	+= mxl-gpy.o
>   obj-$(CONFIG_MEDIATEK_GE_PHY)	+= mediatek-ge.o
>   obj-$(CONFIG_MESON_GXL_PHY)	+= meson-gxl.o
>   obj-$(CONFIG_MICREL_KS8995MA)	+= spi_ks8995.o
> diff --git a/drivers/net/phy/mxl-gpy.c b/drivers/net/phy/mxl-gpy.c
> new file mode 100644
> index 000000000000..12a02d8ce09b
> --- /dev/null
> +++ b/drivers/net/phy/mxl-gpy.c
> @@ -0,0 +1,728 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/* Copyright (C) 2021 Maxlinear Corporation
> + * Copyright (C) 2020 Intel Corporation
> + *
> + * Drivers for Maxlinear Ethernet GPY
> + *
> + */
> +
> +#include <linux/version.h>
> +#include <linux/module.h>
> +#include <linux/bitfield.h>
> +#include <linux/phy.h>
> +#include <linux/netdevice.h>
> +
> +/* PHY ID */
> +#define PHY_ID_GPYx15B_MASK	0xFFFFFFFC
> +#define PHY_ID_GPY21xB_MASK	0xFFFFFFF9
> +#define PHY_ID_GPY2xx		0x67C9DC00
> +#define PHY_ID_GPY115B		0x67C9DF00
> +#define PHY_ID_GPY115C		0x67C9DF10
> +#define PHY_ID_GPY211B		0x67C9DE08
> +#define PHY_ID_GPY211C		0x67C9DE10
> +#define PHY_ID_GPY212B		0x67C9DE09
> +#define PHY_ID_GPY212C		0x67C9DE20
> +#define PHY_ID_GPY215B		0x67C9DF04
> +#define PHY_ID_GPY215C		0x67C9DF20
> +#define PHY_ID_GPY241B		0x67C9DE40
> +#define PHY_ID_GPY241BM		0x67C9DE80
> +#define PHY_ID_GPY245B		0x67C9DEC0
> +
> +#define PHY_MIISTAT		0x18	/* MII state */
> +#define PHY_IMASK		0x19	/* interrupt mask */
> +#define PHY_ISTAT		0x1A	/* interrupt status */
> +#define PHY_FWV			0x1E	/* firmware version */
> +
> +#define PHY_MIISTAT_SPD_MASK	GENMASK(2, 0)
> +#define PHY_MIISTAT_DPX		BIT(3)
> +#define PHY_MIISTAT_LS		BIT(10)
> +
> +#define PHY_MIISTAT_SPD_10	0
> +#define PHY_MIISTAT_SPD_100	1
> +#define PHY_MIISTAT_SPD_1000	2
> +#define PHY_MIISTAT_SPD_2500	4
> +
> +#define PHY_IMASK_WOL		BIT(15)	/* Wake-on-LAN */
> +#define PHY_IMASK_ANC		BIT(10)	/* Auto-Neg complete */
> +#define PHY_IMASK_ADSC		BIT(5)	/* Link auto-downspeed detect */
> +#define PHY_IMASK_DXMC		BIT(2)	/* Duplex mode change */
> +#define PHY_IMASK_LSPC		BIT(1)	/* Link speed change */
> +#define PHY_IMASK_LSTC		BIT(0)	/* Link state change */
> +#define PHY_IMASK_MASK		(PHY_IMASK_LSTC | \
> +				 PHY_IMASK_LSPC | \
> +				 PHY_IMASK_DXMC | \
> +				 PHY_IMASK_ADSC | \
> +				 PHY_IMASK_ANC)
> +
> +#define PHY_FWV_REL_MASK	BIT(15)
> +#define PHY_FWV_TYPE_MASK	GENMASK(11, 8)
> +#define PHY_FWV_MINOR_MASK	GENMASK(7, 0)
> +
> +/* SGMII */
> +#define VSPEC1_SGMII_CTRL	0x08
> +#define VSPEC1_SGMII_CTRL_ANEN	BIT(12)		/* Aneg enable */
> +#define VSPEC1_SGMII_CTRL_ANRS	BIT(9)		/* Restart Aneg */
> +#define VSPEC1_SGMII_ANEN_ANRS	(VSPEC1_SGMII_CTRL_ANEN | \
> +				 VSPEC1_SGMII_CTRL_ANRS)
> +
> +/* WoL */
> +#define VPSPEC2_WOL_CTL		0x0E06
> +#define VPSPEC2_WOL_AD01	0x0E08
> +#define VPSPEC2_WOL_AD23	0x0E09
> +#define VPSPEC2_WOL_AD45	0x0E0A
> +#define WOL_EN			BIT(0)
> +
> +static const struct {
> +	int type;
> +	int minor;
> +} ver_need_sgmii_reaneg[] = {
> +	{7, 0x6D},
> +	{8, 0x6D},
> +	{9, 0x73},
> +};
> +
> +static int gpy_config_init(struct phy_device *phydev)
> +{
> +	int ret;
> +
> +	/* Mask all interrupts */
> +	ret = phy_write(phydev, PHY_IMASK, 0);
> +	if (ret)
> +		return ret;
> +
> +	/* Clear all pending interrupts */
> +	ret = phy_read(phydev, PHY_ISTAT);
> +	return ret < 0 ? ret : 0;
> +}
> +
> +static int gpy_probe(struct phy_device *phydev)
> +{
> +	int ret;
> +
> +	if (!phydev->is_c45) {
> +		ret = phy_get_c45_ids(phydev);
> +		if (ret < 0)
> +			return ret;
> +	}
> +
> +	/* Show GPY PHY FW version in dmesg */
> +	ret = phy_read(phydev, PHY_FWV);
> +	if (ret < 0)
> +		return ret;
> +
> +	phydev_info(phydev, "Firmware Version: 0x%04X (%s)\n", ret,
> +		    (ret & PHY_FWV_REL_MASK) ? "release" : "test");
> +
> +	return 0;
> +}
> +
> +static bool gpy_sgmii_need_reaneg(struct phy_device *phydev)
> +{
> +	int fw_ver, fw_type, fw_minor;
> +	size_t i;
> +
> +	fw_ver = phy_read(phydev, PHY_FWV);
> +	if (fw_ver < 0)
> +		return true;
> +
> +	fw_type = FIELD_GET(PHY_FWV_TYPE_MASK, fw_ver);
> +	fw_minor = FIELD_GET(PHY_FWV_MINOR_MASK, fw_ver);
> +
> +	for (i = 0; i < ARRAY_SIZE(ver_need_sgmii_reaneg); i++) {
> +		if (fw_type != ver_need_sgmii_reaneg[i].type)
> +			continue;
> +		if (fw_minor < ver_need_sgmii_reaneg[i].minor)
> +			return true;
> +		break;
> +	}
> +
> +	return false;
> +}
> +
> +static bool gpy_2500basex_chk(struct phy_device *phydev)
> +{
> +	int ret;
> +
> +	ret = phy_read(phydev, PHY_MIISTAT);
> +	if (ret < 0) {
> +		phydev_err(phydev, "Error: MDIO register access failed: %d\n",
> +			   ret);
> +		return false;
> +	}
> +
> +	if (!(ret & PHY_MIISTAT_LS) ||
> +	    FIELD_GET(PHY_MIISTAT_SPD_MASK, ret) != PHY_MIISTAT_SPD_2500)
> +		return false;
> +
> +	phydev->speed = SPEED_2500;
> +	phydev->interface = PHY_INTERFACE_MODE_2500BASEX;
> +	phy_modify_mmd(phydev, MDIO_MMD_VEND1, VSPEC1_SGMII_CTRL,
> +		       VSPEC1_SGMII_CTRL_ANEN, 0);
> +	return true;
> +}
> +
> +static bool gpy_sgmii_aneg_en(struct phy_device *phydev)
> +{
> +	int ret;
> +
> +	ret = phy_read_mmd(phydev, MDIO_MMD_VEND1, VSPEC1_SGMII_CTRL);
> +	if (ret < 0) {
> +		phydev_err(phydev, "Error: MMD register access failed: %d\n",
> +			   ret);
> +		return true;
> +	}
> +
> +	return (ret & VSPEC1_SGMII_CTRL_ANEN) ? true : false;
> +}
> +
> +static int gpy_config_aneg(struct phy_device *phydev)
> +{
> +	bool changed = false;
> +	u32 adv;
> +	int ret;
> +
> +	if (phydev->autoneg == AUTONEG_DISABLE) {
> +		/* Configure half duplex with genphy_setup_forced,
> +		 * because genphy_c45_pma_setup_forced does not support.
> +		 */
> +		return phydev->duplex != DUPLEX_FULL
> +			? genphy_setup_forced(phydev)
> +			: genphy_c45_pma_setup_forced(phydev);
> +	}
> +
> +	ret = genphy_c45_an_config_aneg(phydev);
> +	if (ret < 0)
> +		return ret;
> +	if (ret > 0)
> +		changed = true;
> +
> +	adv = linkmode_adv_to_mii_ctrl1000_t(phydev->advertising);
> +	ret = phy_modify_changed(phydev, MII_CTRL1000,
> +				 ADVERTISE_1000FULL | ADVERTISE_1000HALF,
> +				 adv);
> +	if (ret < 0)
> +		return ret;
> +	if (ret > 0)
> +		changed = true;
> +
> +	ret = genphy_c45_check_and_restart_aneg(phydev, changed);
> +	if (ret < 0)
> +		return ret;
> +
> +	if (phydev->interface == PHY_INTERFACE_MODE_USXGMII ||
> +	    phydev->interface == PHY_INTERFACE_MODE_INTERNAL)
> +		return 0;
> +
> +	/* No need to trigger re-ANEG if link speed is 2.5G or SGMII ANEG is
> +	 * disabled.
> +	 */
> +	if (!gpy_sgmii_need_reaneg(phydev) || gpy_2500basex_chk(phydev) ||
> +	    !gpy_sgmii_aneg_en(phydev))
> +		return 0;
> +
> +	/* There is a design constraint in GPY2xx device where SGMII AN is
> +	 * only triggered when there is change of speed. If, PHY link
> +	 * partner`s speed is still same even after PHY TPI is down and up
> +	 * again, SGMII AN is not triggered and hence no new in-band message
> +	 * from GPY to MAC side SGMII.
> +	 * This could cause an issue during power up, when PHY is up prior to
> +	 * MAC. At this condition, once MAC side SGMII is up, MAC side SGMII
> +	 * wouldn`t receive new in-band message from GPY with correct link
> +	 * status, speed and duplex info.
> +	 *
> +	 * 1) If PHY is already up and TPI link status is still down (such as
> +	 *    hard reboot), TPI link status is polled for 4 seconds before
> +	 *    retriggerring SGMII AN.
> +	 * 2) If PHY is already up and TPI link status is also up (such as soft
> +	 *    reboot), polling of TPI link status is not needed and SGMII AN is
> +	 *    immediately retriggered.
> +	 * 3) Other conditions such as PHY is down, speed change etc, skip
> +	 *    retriggering SGMII AN. Note: in case of speed change, GPY FW will
> +	 *    initiate SGMII AN.
> +	 */
> +
> +	if (phydev->state != PHY_UP)
> +		return 0;
> +
> +	ret = phy_read_poll_timeout(phydev, MII_BMSR, ret, ret & BMSR_LSTATUS,
> +				    20000, 4000000, false);
> +	if (ret == -ETIMEDOUT)
> +		return 0;
> +	else if (ret < 0)
> +		return ret;
> +
> +	/* Trigger SGMII AN. */
> +	return phy_modify_mmd(phydev, MDIO_MMD_VEND1, VSPEC1_SGMII_CTRL,
> +			      VSPEC1_SGMII_CTRL_ANRS, VSPEC1_SGMII_CTRL_ANRS);
> +}
> +
> +static void gpy_update_interface(struct phy_device *phydev)
> +{
> +	int ret;
> +
> +	/* Interface mode is fixed for USXGMII and integrated PHY */
> +	if (phydev->interface == PHY_INTERFACE_MODE_USXGMII ||
> +	    phydev->interface == PHY_INTERFACE_MODE_INTERNAL)
> +		return;
> +
> +	/* Automatically switch SERDES interface between SGMII and 2500-BaseX
> +	 * according to speed. Disable ANEG in 2500-BaseX mode.
> +	 */
> +	switch (phydev->speed) {
> +	case SPEED_2500:
> +		phydev->interface = PHY_INTERFACE_MODE_2500BASEX;
> +		ret = phy_modify_mmd(phydev, MDIO_MMD_VEND1, VSPEC1_SGMII_CTRL,
> +				     VSPEC1_SGMII_CTRL_ANEN, 0);
> +		if (ret < 0)
> +			phydev_err(phydev,
> +				   "Error: Disable of SGMII ANEG failed: %d\n",
> +				   ret);
> +		break;
> +	case SPEED_1000:
> +	case SPEED_100:
> +	case SPEED_10:
> +		phydev->interface = PHY_INTERFACE_MODE_SGMII;
> +		if (gpy_sgmii_aneg_en(phydev))
> +			break;
> +		/* Enable and restart SGMII ANEG for 10/100/1000Mbps link speed
> +		 * if ANEG is disabled (in 2500-BaseX mode).
> +		 */
> +		ret = phy_modify_mmd(phydev, MDIO_MMD_VEND1, VSPEC1_SGMII_CTRL,
> +				     VSPEC1_SGMII_ANEN_ANRS,
> +				     VSPEC1_SGMII_ANEN_ANRS);
> +		if (ret < 0)
> +			phydev_err(phydev,
> +				   "Error: Enable of SGMII ANEG failed: %d\n",
> +				   ret);
> +		break;
> +	}
> +}
> +
> +static int gpy_read_status(struct phy_device *phydev)
> +{
> +	int ret;
> +
> +	ret = genphy_update_link(phydev);
> +	if (ret)
> +		return ret;
> +
> +	phydev->speed = SPEED_UNKNOWN;
> +	phydev->duplex = DUPLEX_UNKNOWN;
> +	phydev->pause = 0;
> +	phydev->asym_pause = 0;
> +
> +	if (phydev->autoneg == AUTONEG_ENABLE && phydev->autoneg_complete) {
> +		ret = genphy_c45_read_lpa(phydev);
> +		if (ret < 0)
> +			return ret;
> +
> +		/* Read the link partner's 1G advertisement */
> +		ret = phy_read(phydev, MII_STAT1000);
> +		if (ret < 0)
> +			return ret;
> +		mii_stat1000_mod_linkmode_lpa_t(phydev->lp_advertising, ret);
> +	} else if (phydev->autoneg == AUTONEG_DISABLE) {
> +		linkmode_zero(phydev->lp_advertising);
> +	}
> +
> +	ret = phy_read(phydev, PHY_MIISTAT);
> +	if (ret < 0)
> +		return ret;
> +
> +	phydev->link = (ret & PHY_MIISTAT_LS) ? 1 : 0;
> +	phydev->duplex = (ret & PHY_MIISTAT_DPX) ? DUPLEX_FULL : DUPLEX_HALF;
> +	switch (FIELD_GET(PHY_MIISTAT_SPD_MASK, ret)) {
> +	case PHY_MIISTAT_SPD_10:
> +		phydev->speed = SPEED_10;
> +		break;
> +	case PHY_MIISTAT_SPD_100:
> +		phydev->speed = SPEED_100;
> +		break;
> +	case PHY_MIISTAT_SPD_1000:
> +		phydev->speed = SPEED_1000;
> +		break;
> +	case PHY_MIISTAT_SPD_2500:
> +		phydev->speed = SPEED_2500;
> +		break;
> +	}
> +
> +	if (phydev->link)
> +		gpy_update_interface(phydev);
> +
> +	return 0;
> +}
> +
> +static int gpy_config_intr(struct phy_device *phydev)
> +{
> +	u16 mask = 0;
> +
> +	if (phydev->interrupts == PHY_INTERRUPT_ENABLED)
> +		mask = PHY_IMASK_MASK;
> +
> +	return phy_write(phydev, PHY_IMASK, mask);
> +}
> +
> +static irqreturn_t gpy_handle_interrupt(struct phy_device *phydev)
> +{
> +	int reg;
> +
> +	reg = phy_read(phydev, PHY_ISTAT);
> +	if (reg < 0) {
> +		phy_error(phydev);
> +		return IRQ_NONE;
> +	}
> +
> +	if (!(reg & PHY_IMASK_MASK))
> +		return IRQ_NONE;
> +
> +	phy_trigger_machine(phydev);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static int gpy_set_wol(struct phy_device *phydev,
> +		       struct ethtool_wolinfo *wol)
> +{
> +	struct net_device *attach_dev = phydev->attached_dev;
> +	int ret;
> +
> +	if (wol->wolopts & WAKE_MAGIC) {
> +		/* MAC address - Byte0:Byte1:Byte2:Byte3:Byte4:Byte5
> +		 * VPSPEC2_WOL_AD45 = Byte0:Byte1
> +		 * VPSPEC2_WOL_AD23 = Byte2:Byte3
> +		 * VPSPEC2_WOL_AD01 = Byte4:Byte5
> +		 */
> +		ret = phy_set_bits_mmd(phydev, MDIO_MMD_VEND2,
> +				       VPSPEC2_WOL_AD45,
> +				       ((attach_dev->dev_addr[0] << 8) |
> +				       attach_dev->dev_addr[1]));
> +		if (ret < 0)
> +			return ret;
> +
> +		ret = phy_set_bits_mmd(phydev, MDIO_MMD_VEND2,
> +				       VPSPEC2_WOL_AD23,
> +				       ((attach_dev->dev_addr[2] << 8) |
> +				       attach_dev->dev_addr[3]));
> +		if (ret < 0)
> +			return ret;
> +
> +		ret = phy_set_bits_mmd(phydev, MDIO_MMD_VEND2,
> +				       VPSPEC2_WOL_AD01,
> +				       ((attach_dev->dev_addr[4] << 8) |
> +				       attach_dev->dev_addr[5]));
> +		if (ret < 0)
> +			return ret;
> +
> +		/* Enable the WOL interrupt */
> +		ret = phy_write(phydev, PHY_IMASK, PHY_IMASK_WOL);
> +		if (ret < 0)
> +			return ret;
> +
> +		/* Enable magic packet matching */
> +		ret = phy_set_bits_mmd(phydev, MDIO_MMD_VEND2,
> +				       VPSPEC2_WOL_CTL,
> +				       WOL_EN);
> +		if (ret < 0)
> +			return ret;
> +
> +		/* Clear the interrupt status register.
> +		 * Only WoL is enabled so clear all.
> +		 */
> +		ret = phy_read(phydev, PHY_ISTAT);
> +		if (ret < 0)
> +			return ret;
> +	} else {
> +		/* Disable magic packet matching */
> +		ret = phy_clear_bits_mmd(phydev, MDIO_MMD_VEND2,
> +					 VPSPEC2_WOL_CTL,
> +					 WOL_EN);
> +		if (ret < 0)
> +			return ret;
> +	}
> +
> +	if (wol->wolopts & WAKE_PHY) {
> +		/* Enable the link state change interrupt */
> +		ret = phy_set_bits(phydev, PHY_IMASK, PHY_IMASK_LSTC);
> +		if (ret < 0)
> +			return ret;
> +
> +		/* Clear the interrupt status register */
> +		ret = phy_read(phydev, PHY_ISTAT);
> +		if (ret < 0)
> +			return ret;
> +
> +		if (ret & (PHY_IMASK_MASK & ~PHY_IMASK_LSTC))
> +			phy_trigger_machine(phydev);
> +
> +		return 0;
> +	}
> +
> +	/* Disable the link state change interrupt */
> +	return phy_clear_bits(phydev, PHY_IMASK, PHY_IMASK_LSTC);
> +}
> +
> +static void gpy_get_wol(struct phy_device *phydev,
> +			struct ethtool_wolinfo *wol)
> +{
> +	int ret;
> +
> +	wol->supported = WAKE_MAGIC | WAKE_PHY;
> +	wol->wolopts = 0;
> +
> +	ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, VPSPEC2_WOL_CTL);
> +	if (ret & WOL_EN)
> +		wol->wolopts |= WAKE_MAGIC;
> +
> +	ret = phy_read(phydev, PHY_IMASK);
> +	if (ret & PHY_IMASK_LSTC)
> +		wol->wolopts |= WAKE_PHY;
> +}
> +
> +static int gpy_loopback(struct phy_device *phydev, bool enable)
> +{
> +	int ret;
> +
> +	ret = phy_modify(phydev, MII_BMCR, BMCR_LOOPBACK,
> +			 enable ? BMCR_LOOPBACK : 0);
> +	if (!ret) {
> +		/* It takes some time for PHY device to switch
> +		 * into/out-of loopback mode.
> +		 */
> +		msleep(100);
> +	}
> +
> +	return ret;
> +}
> +
> +static struct phy_driver gpy_drivers[] = {
> +	{
> +		PHY_ID_MATCH_MODEL(PHY_ID_GPY2xx),
> +		.name		= "Maxlinear Ethernet GPY2xx",
> +		.get_features	= genphy_c45_pma_read_abilities,
> +		.config_init	= gpy_config_init,
> +		.probe		= gpy_probe,
> +		.suspend	= genphy_suspend,
> +		.resume		= genphy_resume,
> +		.config_aneg	= gpy_config_aneg,
> +		.aneg_done	= genphy_c45_aneg_done,
> +		.read_status	= gpy_read_status,
> +		.config_intr	= gpy_config_intr,
> +		.handle_interrupt = gpy_handle_interrupt,
> +		.set_wol	= gpy_set_wol,
> +		.get_wol	= gpy_get_wol,
> +		.set_loopback	= gpy_loopback,
> +	},
> +	{
> +		.phy_id		= PHY_ID_GPY115B,
> +		.phy_id_mask	= PHY_ID_GPYx15B_MASK,
> +		.name		= "Maxlinear Ethernet GPY115B",
> +		.get_features	= genphy_c45_pma_read_abilities,
> +		.config_init	= gpy_config_init,
> +		.probe		= gpy_probe,
> +		.suspend	= genphy_suspend,
> +		.resume		= genphy_resume,
> +		.config_aneg	= gpy_config_aneg,
> +		.aneg_done	= genphy_c45_aneg_done,
> +		.read_status	= gpy_read_status,
> +		.config_intr	= gpy_config_intr,
> +		.handle_interrupt = gpy_handle_interrupt,
> +		.set_wol	= gpy_set_wol,
> +		.get_wol	= gpy_get_wol,
> +		.set_loopback	= gpy_loopback,
> +	},
> +	{
> +		PHY_ID_MATCH_MODEL(PHY_ID_GPY115C),
> +		.name		= "Maxlinear Ethernet GPY115C",
> +		.get_features	= genphy_c45_pma_read_abilities,
> +		.config_init	= gpy_config_init,
> +		.probe		= gpy_probe,
> +		.suspend	= genphy_suspend,
> +		.resume		= genphy_resume,
> +		.config_aneg	= gpy_config_aneg,
> +		.aneg_done	= genphy_c45_aneg_done,
> +		.read_status	= gpy_read_status,
> +		.config_intr	= gpy_config_intr,
> +		.handle_interrupt = gpy_handle_interrupt,
> +		.set_wol	= gpy_set_wol,
> +		.get_wol	= gpy_get_wol,
> +		.set_loopback	= gpy_loopback,
> +	},
> +	{
> +		.phy_id		= PHY_ID_GPY211B,
> +		.phy_id_mask	= PHY_ID_GPY21xB_MASK,
> +		.name		= "Maxlinear Ethernet GPY211B",
> +		.get_features	= genphy_c45_pma_read_abilities,
> +		.config_init	= gpy_config_init,
> +		.probe		= gpy_probe,
> +		.suspend	= genphy_suspend,
> +		.resume		= genphy_resume,
> +		.config_aneg	= gpy_config_aneg,
> +		.aneg_done	= genphy_c45_aneg_done,
> +		.read_status	= gpy_read_status,
> +		.config_intr	= gpy_config_intr,
> +		.handle_interrupt = gpy_handle_interrupt,
> +		.set_wol	= gpy_set_wol,
> +		.get_wol	= gpy_get_wol,
> +		.set_loopback	= gpy_loopback,
> +	},
> +	{
> +		PHY_ID_MATCH_MODEL(PHY_ID_GPY211C),
> +		.name		= "Maxlinear Ethernet GPY211C",
> +		.get_features	= genphy_c45_pma_read_abilities,
> +		.config_init	= gpy_config_init,
> +		.probe		= gpy_probe,
> +		.suspend	= genphy_suspend,
> +		.resume		= genphy_resume,
> +		.config_aneg	= gpy_config_aneg,
> +		.aneg_done	= genphy_c45_aneg_done,
> +		.read_status	= gpy_read_status,
> +		.config_intr	= gpy_config_intr,
> +		.handle_interrupt = gpy_handle_interrupt,
> +		.set_wol	= gpy_set_wol,
> +		.get_wol	= gpy_get_wol,
> +		.set_loopback	= gpy_loopback,
> +	},
> +	{
> +		.phy_id		= PHY_ID_GPY212B,
> +		.phy_id_mask	= PHY_ID_GPY21xB_MASK,
> +		.name		= "Maxlinear Ethernet GPY212B",
> +		.get_features	= genphy_c45_pma_read_abilities,
> +		.config_init	= gpy_config_init,
> +		.probe		= gpy_probe,
> +		.suspend	= genphy_suspend,
> +		.resume		= genphy_resume,
> +		.config_aneg	= gpy_config_aneg,
> +		.aneg_done	= genphy_c45_aneg_done,
> +		.read_status	= gpy_read_status,
> +		.config_intr	= gpy_config_intr,
> +		.handle_interrupt = gpy_handle_interrupt,
> +		.set_wol	= gpy_set_wol,
> +		.get_wol	= gpy_get_wol,
> +		.set_loopback	= gpy_loopback,
> +	},
> +	{
> +		PHY_ID_MATCH_MODEL(PHY_ID_GPY212C),
> +		.name		= "Maxlinear Ethernet GPY212C",
> +		.get_features	= genphy_c45_pma_read_abilities,
> +		.config_init	= gpy_config_init,
> +		.probe		= gpy_probe,
> +		.suspend	= genphy_suspend,
> +		.resume		= genphy_resume,
> +		.config_aneg	= gpy_config_aneg,
> +		.aneg_done	= genphy_c45_aneg_done,
> +		.read_status	= gpy_read_status,
> +		.config_intr	= gpy_config_intr,
> +		.handle_interrupt = gpy_handle_interrupt,
> +		.set_wol	= gpy_set_wol,
> +		.get_wol	= gpy_get_wol,
> +		.set_loopback	= gpy_loopback,
> +	},
> +	{
> +		.phy_id		= PHY_ID_GPY215B,
> +		.phy_id_mask	= PHY_ID_GPYx15B_MASK,
> +		.name		= "Maxlinear Ethernet GPY215B",
> +		.get_features	= genphy_c45_pma_read_abilities,
> +		.config_init	= gpy_config_init,
> +		.probe		= gpy_probe,
> +		.suspend	= genphy_suspend,
> +		.resume		= genphy_resume,
> +		.config_aneg	= gpy_config_aneg,
> +		.aneg_done	= genphy_c45_aneg_done,
> +		.read_status	= gpy_read_status,
> +		.config_intr	= gpy_config_intr,
> +		.handle_interrupt = gpy_handle_interrupt,
> +		.set_wol	= gpy_set_wol,
> +		.get_wol	= gpy_get_wol,
> +		.set_loopback	= gpy_loopback,
> +	},
> +	{
> +		PHY_ID_MATCH_MODEL(PHY_ID_GPY215C),
> +		.name		= "Maxlinear Ethernet GPY215C",
> +		.get_features	= genphy_c45_pma_read_abilities,
> +		.config_init	= gpy_config_init,
> +		.probe		= gpy_probe,
> +		.suspend	= genphy_suspend,
> +		.resume		= genphy_resume,
> +		.config_aneg	= gpy_config_aneg,
> +		.aneg_done	= genphy_c45_aneg_done,
> +		.read_status	= gpy_read_status,
> +		.config_intr	= gpy_config_intr,
> +		.handle_interrupt = gpy_handle_interrupt,
> +		.set_wol	= gpy_set_wol,
> +		.get_wol	= gpy_get_wol,
> +		.set_loopback	= gpy_loopback,
> +	},
> +	{
> +		PHY_ID_MATCH_MODEL(PHY_ID_GPY241B),
> +		.name		= "Maxlinear Ethernet GPY241B",
> +		.get_features	= genphy_c45_pma_read_abilities,
> +		.config_init	= gpy_config_init,
> +		.probe		= gpy_probe,
> +		.suspend	= genphy_suspend,
> +		.resume		= genphy_resume,
> +		.config_aneg	= gpy_config_aneg,
> +		.aneg_done	= genphy_c45_aneg_done,
> +		.read_status	= gpy_read_status,
> +		.config_intr	= gpy_config_intr,
> +		.handle_interrupt = gpy_handle_interrupt,
> +		.set_wol	= gpy_set_wol,
> +		.get_wol	= gpy_get_wol,
> +		.set_loopback	= gpy_loopback,
> +	},
> +	{
> +		PHY_ID_MATCH_MODEL(PHY_ID_GPY241BM),
> +		.name		= "Maxlinear Ethernet GPY241BM",
> +		.get_features	= genphy_c45_pma_read_abilities,
> +		.config_init	= gpy_config_init,
> +		.probe		= gpy_probe,
> +		.suspend	= genphy_suspend,
> +		.resume		= genphy_resume,
> +		.config_aneg	= gpy_config_aneg,
> +		.aneg_done	= genphy_c45_aneg_done,
> +		.read_status	= gpy_read_status,
> +		.config_intr	= gpy_config_intr,
> +		.handle_interrupt = gpy_handle_interrupt,
> +		.set_wol	= gpy_set_wol,
> +		.get_wol	= gpy_get_wol,
> +		.set_loopback	= gpy_loopback,
> +	},
> +	{
> +		PHY_ID_MATCH_MODEL(PHY_ID_GPY245B),
> +		.name		= "Maxlinear Ethernet GPY245B",
> +		.get_features	= genphy_c45_pma_read_abilities,
> +		.config_init	= gpy_config_init,
> +		.probe		= gpy_probe,
> +		.suspend	= genphy_suspend,
> +		.resume		= genphy_resume,
> +		.config_aneg	= gpy_config_aneg,
> +		.aneg_done	= genphy_c45_aneg_done,
> +		.read_status	= gpy_read_status,
> +		.config_intr	= gpy_config_intr,
> +		.handle_interrupt = gpy_handle_interrupt,
> +		.set_wol	= gpy_set_wol,
> +		.get_wol	= gpy_get_wol,
> +		.set_loopback	= gpy_loopback,
> +	},
> +};
> +module_phy_driver(gpy_drivers);
> +
> +static struct mdio_device_id __maybe_unused gpy_tbl[] = {
> +	{PHY_ID_MATCH_MODEL(PHY_ID_GPY2xx)},
> +	{PHY_ID_GPY115B, PHY_ID_GPYx15B_MASK},
> +	{PHY_ID_MATCH_MODEL(PHY_ID_GPY115C)},
> +	{PHY_ID_GPY211B, PHY_ID_GPY21xB_MASK},
> +	{PHY_ID_MATCH_MODEL(PHY_ID_GPY211C)},
> +	{PHY_ID_GPY212B, PHY_ID_GPY21xB_MASK},
> +	{PHY_ID_MATCH_MODEL(PHY_ID_GPY212C)},
> +	{PHY_ID_GPY215B, PHY_ID_GPYx15B_MASK},
> +	{PHY_ID_MATCH_MODEL(PHY_ID_GPY215C)},
> +	{PHY_ID_MATCH_MODEL(PHY_ID_GPY241B)},
> +	{PHY_ID_MATCH_MODEL(PHY_ID_GPY241BM)},
> +	{PHY_ID_MATCH_MODEL(PHY_ID_GPY245B)},
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(mdio, gpy_tbl);
> +
> +MODULE_DESCRIPTION("Maxlinear Ethernet GPY Driver");
> +MODULE_AUTHOR("Xu Liang");
> +MODULE_LICENSE("GPL");
> 


^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH v6 1/2] net: phy: add API to read 802.3-c45 IDs
  2021-07-19  5:32 [PATCH v6 1/2] net: phy: add API to read 802.3-c45 IDs Xu Liang
  2021-07-19  5:32 ` [PATCH v6 2/2] net: phy: add Maxlinear GPY115/21x/24x driver Xu Liang
@ 2021-07-19 18:30 ` Hauke Mehrtens
  2021-07-19 20:40 ` Andrew Lunn
  2 siblings, 0 replies; 12+ messages in thread
From: Hauke Mehrtens @ 2021-07-19 18:30 UTC (permalink / raw)
  To: Xu Liang, andrew, hkallweit1, netdev, davem, kuba, vee.khee.wong
  Cc: linux, hmehrtens, tmohren, mohammad.athari.ismail

On 7/19/21 7:32 AM, Xu Liang wrote:
> Add API to read 802.3-c45 IDs so that C22/C45 mixed device can use
> C45 APIs without failing ID checks.
> 
> Signed-off-by: Xu Liang <lxu@maxlinear.com>

Acked-by: Hauke Mehrtens <hmehrtens@maxlinear.com>

> ---
> v5 changes:
>   Fix incorrect prototype name in comment.
> 
>   drivers/net/phy/phy_device.c | 14 ++++++++++++++
>   include/linux/phy.h          |  1 +
>   2 files changed, 15 insertions(+)
> 
> diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c
> index 5d5f9a9ee768..107aa6d7bc6b 100644
> --- a/drivers/net/phy/phy_device.c
> +++ b/drivers/net/phy/phy_device.c
> @@ -968,6 +968,20 @@ void phy_device_remove(struct phy_device *phydev)
>   }
>   EXPORT_SYMBOL(phy_device_remove);
>   
> +/**
> + * phy_get_c45_ids - Read 802.3-c45 IDs for phy device.
> + * @phydev: phy_device structure to read 802.3-c45 IDs
> + *
> + * Returns zero on success, %-EIO on bus access error, or %-ENODEV if
> + * the "devices in package" is invalid.
> + */
> +int phy_get_c45_ids(struct phy_device *phydev)
> +{
> +	return get_phy_c45_ids(phydev->mdio.bus, phydev->mdio.addr,
> +			       &phydev->c45_ids);
> +}
> +EXPORT_SYMBOL(phy_get_c45_ids);
> +
>   /**
>    * phy_find_first - finds the first PHY device on the bus
>    * @bus: the target MII bus
> diff --git a/include/linux/phy.h b/include/linux/phy.h
> index 3b80dc3ed68b..736e1d1a47c4 100644
> --- a/include/linux/phy.h
> +++ b/include/linux/phy.h
> @@ -1431,6 +1431,7 @@ static inline int phy_device_register(struct phy_device *phy)
>   static inline void phy_device_free(struct phy_device *phydev) { }
>   #endif /* CONFIG_PHYLIB */
>   void phy_device_remove(struct phy_device *phydev);
> +int phy_get_c45_ids(struct phy_device *phydev);
>   int phy_init_hw(struct phy_device *phydev);
>   int phy_suspend(struct phy_device *phydev);
>   int phy_resume(struct phy_device *phydev);
> 


^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH v6 1/2] net: phy: add API to read 802.3-c45 IDs
  2021-07-19  5:32 [PATCH v6 1/2] net: phy: add API to read 802.3-c45 IDs Xu Liang
  2021-07-19  5:32 ` [PATCH v6 2/2] net: phy: add Maxlinear GPY115/21x/24x driver Xu Liang
  2021-07-19 18:30 ` [PATCH v6 1/2] net: phy: add API to read 802.3-c45 IDs Hauke Mehrtens
@ 2021-07-19 20:40 ` Andrew Lunn
  2 siblings, 0 replies; 12+ messages in thread
From: Andrew Lunn @ 2021-07-19 20:40 UTC (permalink / raw)
  To: Xu Liang
  Cc: hkallweit1, netdev, davem, kuba, vee.khee.wong, linux, hmehrtens,
	tmohren, mohammad.athari.ismail

On Mon, Jul 19, 2021 at 01:32:11PM +0800, Xu Liang wrote:
> Add API to read 802.3-c45 IDs so that C22/C45 mixed device can use
> C45 APIs without failing ID checks.
> 
> Signed-off-by: Xu Liang <lxu@maxlinear.com>

Reviewed-by: Andrew Lunn <andrew@lunn.ch>

    Andrew

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH v6 2/2] net: phy: add Maxlinear GPY115/21x/24x driver
  2021-07-19  5:32 ` [PATCH v6 2/2] net: phy: add Maxlinear GPY115/21x/24x driver Xu Liang
  2021-07-19 18:30   ` Hauke Mehrtens
@ 2021-07-19 20:48   ` Andrew Lunn
  2021-07-20  3:35     ` Liang Xu
  2021-07-20  0:58   ` Wong Vee Khee
                     ` (2 subsequent siblings)
  4 siblings, 1 reply; 12+ messages in thread
From: Andrew Lunn @ 2021-07-19 20:48 UTC (permalink / raw)
  To: Xu Liang
  Cc: hkallweit1, netdev, davem, kuba, vee.khee.wong, linux, hmehrtens,
	tmohren, mohammad.athari.ismail

> +/* PHY ID */
> +#define PHY_ID_GPYx15B_MASK	0xFFFFFFFC
> +#define PHY_ID_GPY21xB_MASK	0xFFFFFFF9

That is an odd mask. Is that really correct?

     Andrew

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH v6 2/2] net: phy: add Maxlinear GPY115/21x/24x driver
  2021-07-19  5:32 ` [PATCH v6 2/2] net: phy: add Maxlinear GPY115/21x/24x driver Xu Liang
  2021-07-19 18:30   ` Hauke Mehrtens
  2021-07-19 20:48   ` Andrew Lunn
@ 2021-07-20  0:58   ` Wong Vee Khee
  2021-07-20  1:03   ` Wong Vee Khee
  2021-07-20 14:11   ` Andrew Lunn
  4 siblings, 0 replies; 12+ messages in thread
From: Wong Vee Khee @ 2021-07-20  0:58 UTC (permalink / raw)
  To: Xu Liang
  Cc: andrew, hkallweit1, netdev, davem, kuba, linux, hmehrtens,
	tmohren, mohammad.athari.ismail

On Mon, Jul 19, 2021 at 01:32:12PM +0800, Xu Liang wrote:
> Add driver to support the Maxlinear GPY115, GPY211, GPY212, GPY215,
> GPY241, GPY245 PHYs. Separate from XWAY PHY driver because this series
> has different register layout and new features not supported in XWAY PHY.
> 
> Signed-off-by: Xu Liang <lxu@maxlinear.com>
> ---

Tested-by: Wong Vee Khee <vee.khee.wong@linux.intel.com>


^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH v6 2/2] net: phy: add Maxlinear GPY115/21x/24x driver
  2021-07-19  5:32 ` [PATCH v6 2/2] net: phy: add Maxlinear GPY115/21x/24x driver Xu Liang
                     ` (2 preceding siblings ...)
  2021-07-20  0:58   ` Wong Vee Khee
@ 2021-07-20  1:03   ` Wong Vee Khee
  2021-07-20 14:11   ` Andrew Lunn
  4 siblings, 0 replies; 12+ messages in thread
From: Wong Vee Khee @ 2021-07-20  1:03 UTC (permalink / raw)
  To: Xu Liang
  Cc: andrew, hkallweit1, netdev, davem, kuba, linux, hmehrtens,
	tmohren, mohammad.athari.ismail

On Mon, Jul 19, 2021 at 01:32:12PM +0800, Xu Liang wrote:
> Add driver to support the Maxlinear GPY115, GPY211, GPY212, GPY215,
> GPY241, GPY245 PHYs. Separate from XWAY PHY driver because this series
> has different register layout and new features not supported in XWAY PHY.
> 
> Signed-off-by: Xu Liang <lxu@maxlinear.com>
> ---

Tested-by: Wong Vee Khee <vee.khee.wong@linux.intel.com>


^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH v6 2/2] net: phy: add Maxlinear GPY115/21x/24x driver
  2021-07-19 20:48   ` Andrew Lunn
@ 2021-07-20  3:35     ` Liang Xu
  2021-07-20 13:02       ` Andrew Lunn
  0 siblings, 1 reply; 12+ messages in thread
From: Liang Xu @ 2021-07-20  3:35 UTC (permalink / raw)
  To: Andrew Lunn
  Cc: hkallweit1, netdev, davem, kuba, vee.khee.wong, linux,
	Hauke Mehrtens, Thomas Mohren, mohammad.athari.ismail

On 20/7/2021 4:48 am, Andrew Lunn wrote:
> This email was sent from outside of MaxLinear.
>
>
>> +/* PHY ID */
>> +#define PHY_ID_GPYx15B_MASK  0xFFFFFFFC
>> +#define PHY_ID_GPY21xB_MASK  0xFFFFFFF9
> That is an odd mask. Is that really correct?
>
>       Andrew
>
Hi Andrew,


Yes, this is correct and has been tested.

It's special because of a PHY ID scheme change during manufacturing.


Thanks & Regards,

Xu Liang


^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH v6 2/2] net: phy: add Maxlinear GPY115/21x/24x driver
  2021-07-20  3:35     ` Liang Xu
@ 2021-07-20 13:02       ` Andrew Lunn
  2021-07-20 13:18         ` Liang Xu
  0 siblings, 1 reply; 12+ messages in thread
From: Andrew Lunn @ 2021-07-20 13:02 UTC (permalink / raw)
  To: Liang Xu
  Cc: hkallweit1, netdev, davem, kuba, vee.khee.wong, linux,
	Hauke Mehrtens, Thomas Mohren, mohammad.athari.ismail

On Tue, Jul 20, 2021 at 03:35:17AM +0000, Liang Xu wrote:
> On 20/7/2021 4:48 am, Andrew Lunn wrote:
> > This email was sent from outside of MaxLinear.
> >
> >
> >> +/* PHY ID */
> >> +#define PHY_ID_GPYx15B_MASK  0xFFFFFFFC
> >> +#define PHY_ID_GPY21xB_MASK  0xFFFFFFF9
> > That is an odd mask. Is that really correct?
> >
> >       Andrew
> >
> Hi Andrew,
> 
> 
> Yes, this is correct and has been tested.
> 
> It's special because of a PHY ID scheme change during manufacturing.

O.K. It is just a really odd mask. And putting the revision in the
middle, not at the end? And none of the IDs have bit 0 set. It just
all adds up to it looking wrong. So i had to ask.

    Andrew

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH v6 2/2] net: phy: add Maxlinear GPY115/21x/24x driver
  2021-07-20 13:02       ` Andrew Lunn
@ 2021-07-20 13:18         ` Liang Xu
  0 siblings, 0 replies; 12+ messages in thread
From: Liang Xu @ 2021-07-20 13:18 UTC (permalink / raw)
  To: Andrew Lunn
  Cc: hkallweit1, netdev, davem, kuba, vee.khee.wong, linux,
	Hauke Mehrtens, Thomas Mohren, mohammad.athari.ismail

On 20/7/2021 9:02 pm, Andrew Lunn wrote:
> This email was sent from outside of MaxLinear.
>
>
> On Tue, Jul 20, 2021 at 03:35:17AM +0000, Liang Xu wrote:
>> On 20/7/2021 4:48 am, Andrew Lunn wrote:
>>> This email was sent from outside of MaxLinear.
>>>
>>>
>>>> +/* PHY ID */
>>>> +#define PHY_ID_GPYx15B_MASK  0xFFFFFFFC
>>>> +#define PHY_ID_GPY21xB_MASK  0xFFFFFFF9
>>> That is an odd mask. Is that really correct?
>>>
>>>        Andrew
>>>
>> Hi Andrew,
>>
>>
>> Yes, this is correct and has been tested.
>>
>> It's special because of a PHY ID scheme change during manufacturing.
> O.K. It is just a really odd mask. And putting the revision in the
> middle, not at the end? And none of the IDs have bit 0 set. It just
> all adds up to it looking wrong. So i had to ask.
>
>      Andrew
>
Understood. Here are the revisions already in the market (shipped):

1. GPY211B - 67C9DE08, 67C9DE0A

2. GPY212B - 67C9DE09, 67C9DE0B



^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH v6 2/2] net: phy: add Maxlinear GPY115/21x/24x driver
  2021-07-19  5:32 ` [PATCH v6 2/2] net: phy: add Maxlinear GPY115/21x/24x driver Xu Liang
                     ` (3 preceding siblings ...)
  2021-07-20  1:03   ` Wong Vee Khee
@ 2021-07-20 14:11   ` Andrew Lunn
  4 siblings, 0 replies; 12+ messages in thread
From: Andrew Lunn @ 2021-07-20 14:11 UTC (permalink / raw)
  To: Xu Liang
  Cc: hkallweit1, netdev, davem, kuba, vee.khee.wong, linux, hmehrtens,
	tmohren, mohammad.athari.ismail

On Mon, Jul 19, 2021 at 01:32:12PM +0800, Xu Liang wrote:
> Add driver to support the Maxlinear GPY115, GPY211, GPY212, GPY215,
> GPY241, GPY245 PHYs. Separate from XWAY PHY driver because this series
> has different register layout and new features not supported in XWAY PHY.
> 
> Signed-off-by: Xu Liang <lxu@maxlinear.com>

Reviewed-by: Andrew Lunn <andrew@lunn.ch>

    Andrew

^ permalink raw reply	[flat|nested] 12+ messages in thread

end of thread, other threads:[~2021-07-20 14:15 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-07-19  5:32 [PATCH v6 1/2] net: phy: add API to read 802.3-c45 IDs Xu Liang
2021-07-19  5:32 ` [PATCH v6 2/2] net: phy: add Maxlinear GPY115/21x/24x driver Xu Liang
2021-07-19 18:30   ` Hauke Mehrtens
2021-07-19 20:48   ` Andrew Lunn
2021-07-20  3:35     ` Liang Xu
2021-07-20 13:02       ` Andrew Lunn
2021-07-20 13:18         ` Liang Xu
2021-07-20  0:58   ` Wong Vee Khee
2021-07-20  1:03   ` Wong Vee Khee
2021-07-20 14:11   ` Andrew Lunn
2021-07-19 18:30 ` [PATCH v6 1/2] net: phy: add API to read 802.3-c45 IDs Hauke Mehrtens
2021-07-19 20:40 ` Andrew Lunn

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.