162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
262306a36Sopenharmony_ci/* Copyright (C) 2021 Maxlinear Corporation
362306a36Sopenharmony_ci * Copyright (C) 2020 Intel Corporation
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Drivers for Maxlinear Ethernet GPY
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <linux/module.h>
1062306a36Sopenharmony_ci#include <linux/bitfield.h>
1162306a36Sopenharmony_ci#include <linux/hwmon.h>
1262306a36Sopenharmony_ci#include <linux/mutex.h>
1362306a36Sopenharmony_ci#include <linux/phy.h>
1462306a36Sopenharmony_ci#include <linux/polynomial.h>
1562306a36Sopenharmony_ci#include <linux/property.h>
1662306a36Sopenharmony_ci#include <linux/netdevice.h>
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci/* PHY ID */
1962306a36Sopenharmony_ci#define PHY_ID_GPYx15B_MASK	0xFFFFFFFC
2062306a36Sopenharmony_ci#define PHY_ID_GPY21xB_MASK	0xFFFFFFF9
2162306a36Sopenharmony_ci#define PHY_ID_GPY2xx		0x67C9DC00
2262306a36Sopenharmony_ci#define PHY_ID_GPY115B		0x67C9DF00
2362306a36Sopenharmony_ci#define PHY_ID_GPY115C		0x67C9DF10
2462306a36Sopenharmony_ci#define PHY_ID_GPY211B		0x67C9DE08
2562306a36Sopenharmony_ci#define PHY_ID_GPY211C		0x67C9DE10
2662306a36Sopenharmony_ci#define PHY_ID_GPY212B		0x67C9DE09
2762306a36Sopenharmony_ci#define PHY_ID_GPY212C		0x67C9DE20
2862306a36Sopenharmony_ci#define PHY_ID_GPY215B		0x67C9DF04
2962306a36Sopenharmony_ci#define PHY_ID_GPY215C		0x67C9DF20
3062306a36Sopenharmony_ci#define PHY_ID_GPY241B		0x67C9DE40
3162306a36Sopenharmony_ci#define PHY_ID_GPY241BM		0x67C9DE80
3262306a36Sopenharmony_ci#define PHY_ID_GPY245B		0x67C9DEC0
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci#define PHY_CTL1		0x13
3562306a36Sopenharmony_ci#define PHY_CTL1_MDICD		BIT(3)
3662306a36Sopenharmony_ci#define PHY_CTL1_MDIAB		BIT(2)
3762306a36Sopenharmony_ci#define PHY_CTL1_AMDIX		BIT(0)
3862306a36Sopenharmony_ci#define PHY_MIISTAT		0x18	/* MII state */
3962306a36Sopenharmony_ci#define PHY_IMASK		0x19	/* interrupt mask */
4062306a36Sopenharmony_ci#define PHY_ISTAT		0x1A	/* interrupt status */
4162306a36Sopenharmony_ci#define PHY_FWV			0x1E	/* firmware version */
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci#define PHY_MIISTAT_SPD_MASK	GENMASK(2, 0)
4462306a36Sopenharmony_ci#define PHY_MIISTAT_DPX		BIT(3)
4562306a36Sopenharmony_ci#define PHY_MIISTAT_LS		BIT(10)
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci#define PHY_MIISTAT_SPD_10	0
4862306a36Sopenharmony_ci#define PHY_MIISTAT_SPD_100	1
4962306a36Sopenharmony_ci#define PHY_MIISTAT_SPD_1000	2
5062306a36Sopenharmony_ci#define PHY_MIISTAT_SPD_2500	4
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci#define PHY_IMASK_WOL		BIT(15)	/* Wake-on-LAN */
5362306a36Sopenharmony_ci#define PHY_IMASK_ANC		BIT(10)	/* Auto-Neg complete */
5462306a36Sopenharmony_ci#define PHY_IMASK_ADSC		BIT(5)	/* Link auto-downspeed detect */
5562306a36Sopenharmony_ci#define PHY_IMASK_DXMC		BIT(2)	/* Duplex mode change */
5662306a36Sopenharmony_ci#define PHY_IMASK_LSPC		BIT(1)	/* Link speed change */
5762306a36Sopenharmony_ci#define PHY_IMASK_LSTC		BIT(0)	/* Link state change */
5862306a36Sopenharmony_ci#define PHY_IMASK_MASK		(PHY_IMASK_LSTC | \
5962306a36Sopenharmony_ci				 PHY_IMASK_LSPC | \
6062306a36Sopenharmony_ci				 PHY_IMASK_DXMC | \
6162306a36Sopenharmony_ci				 PHY_IMASK_ADSC | \
6262306a36Sopenharmony_ci				 PHY_IMASK_ANC)
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci#define PHY_FWV_REL_MASK	BIT(15)
6562306a36Sopenharmony_ci#define PHY_FWV_MAJOR_MASK	GENMASK(11, 8)
6662306a36Sopenharmony_ci#define PHY_FWV_MINOR_MASK	GENMASK(7, 0)
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci#define PHY_PMA_MGBT_POLARITY	0x82
6962306a36Sopenharmony_ci#define PHY_MDI_MDI_X_MASK	GENMASK(1, 0)
7062306a36Sopenharmony_ci#define PHY_MDI_MDI_X_NORMAL	0x3
7162306a36Sopenharmony_ci#define PHY_MDI_MDI_X_AB	0x2
7262306a36Sopenharmony_ci#define PHY_MDI_MDI_X_CD	0x1
7362306a36Sopenharmony_ci#define PHY_MDI_MDI_X_CROSS	0x0
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci/* SGMII */
7662306a36Sopenharmony_ci#define VSPEC1_SGMII_CTRL	0x08
7762306a36Sopenharmony_ci#define VSPEC1_SGMII_CTRL_ANEN	BIT(12)		/* Aneg enable */
7862306a36Sopenharmony_ci#define VSPEC1_SGMII_CTRL_ANRS	BIT(9)		/* Restart Aneg */
7962306a36Sopenharmony_ci#define VSPEC1_SGMII_ANEN_ANRS	(VSPEC1_SGMII_CTRL_ANEN | \
8062306a36Sopenharmony_ci				 VSPEC1_SGMII_CTRL_ANRS)
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci/* Temperature sensor */
8362306a36Sopenharmony_ci#define VSPEC1_TEMP_STA	0x0E
8462306a36Sopenharmony_ci#define VSPEC1_TEMP_STA_DATA	GENMASK(9, 0)
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci/* Mailbox */
8762306a36Sopenharmony_ci#define VSPEC1_MBOX_DATA	0x5
8862306a36Sopenharmony_ci#define VSPEC1_MBOX_ADDRLO	0x6
8962306a36Sopenharmony_ci#define VSPEC1_MBOX_CMD		0x7
9062306a36Sopenharmony_ci#define VSPEC1_MBOX_CMD_ADDRHI	GENMASK(7, 0)
9162306a36Sopenharmony_ci#define VSPEC1_MBOX_CMD_RD	(0 << 8)
9262306a36Sopenharmony_ci#define VSPEC1_MBOX_CMD_READY	BIT(15)
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci/* WoL */
9562306a36Sopenharmony_ci#define VPSPEC2_WOL_CTL		0x0E06
9662306a36Sopenharmony_ci#define VPSPEC2_WOL_AD01	0x0E08
9762306a36Sopenharmony_ci#define VPSPEC2_WOL_AD23	0x0E09
9862306a36Sopenharmony_ci#define VPSPEC2_WOL_AD45	0x0E0A
9962306a36Sopenharmony_ci#define WOL_EN			BIT(0)
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci/* Internal registers, access via mbox */
10262306a36Sopenharmony_ci#define REG_GPIO0_OUT		0xd3ce00
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_cistruct gpy_priv {
10562306a36Sopenharmony_ci	/* serialize mailbox acesses */
10662306a36Sopenharmony_ci	struct mutex mbox_lock;
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	u8 fw_major;
10962306a36Sopenharmony_ci	u8 fw_minor;
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	/* It takes 3 seconds to fully switch out of loopback mode before
11262306a36Sopenharmony_ci	 * it can safely re-enter loopback mode. Record the time when
11362306a36Sopenharmony_ci	 * loopback is disabled. Check and wait if necessary before loopback
11462306a36Sopenharmony_ci	 * is enabled.
11562306a36Sopenharmony_ci	 */
11662306a36Sopenharmony_ci	u64 lb_dis_to;
11762306a36Sopenharmony_ci};
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_cistatic const struct {
12062306a36Sopenharmony_ci	int major;
12162306a36Sopenharmony_ci	int minor;
12262306a36Sopenharmony_ci} ver_need_sgmii_reaneg[] = {
12362306a36Sopenharmony_ci	{7, 0x6D},
12462306a36Sopenharmony_ci	{8, 0x6D},
12562306a36Sopenharmony_ci	{9, 0x73},
12662306a36Sopenharmony_ci};
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_HWMON)
12962306a36Sopenharmony_ci/* The original translation formulae of the temperature (in degrees of Celsius)
13062306a36Sopenharmony_ci * are as follows:
13162306a36Sopenharmony_ci *
13262306a36Sopenharmony_ci *   T = -2.5761e-11*(N^4) + 9.7332e-8*(N^3) + -1.9165e-4*(N^2) +
13362306a36Sopenharmony_ci *       3.0762e-1*(N^1) + -5.2156e1
13462306a36Sopenharmony_ci *
13562306a36Sopenharmony_ci * where [-52.156, 137.961]C and N = [0, 1023].
13662306a36Sopenharmony_ci *
13762306a36Sopenharmony_ci * They must be accordingly altered to be suitable for the integer arithmetics.
13862306a36Sopenharmony_ci * The technique is called 'factor redistribution', which just makes sure the
13962306a36Sopenharmony_ci * multiplications and divisions are made so to have a result of the operations
14062306a36Sopenharmony_ci * within the integer numbers limit. In addition we need to translate the
14162306a36Sopenharmony_ci * formulae to accept millidegrees of Celsius. Here what it looks like after
14262306a36Sopenharmony_ci * the alterations:
14362306a36Sopenharmony_ci *
14462306a36Sopenharmony_ci *   T = -25761e-12*(N^4) + 97332e-9*(N^3) + -191650e-6*(N^2) +
14562306a36Sopenharmony_ci *       307620e-3*(N^1) + -52156
14662306a36Sopenharmony_ci *
14762306a36Sopenharmony_ci * where T = [-52156, 137961]mC and N = [0, 1023].
14862306a36Sopenharmony_ci */
14962306a36Sopenharmony_cistatic const struct polynomial poly_N_to_temp = {
15062306a36Sopenharmony_ci	.terms = {
15162306a36Sopenharmony_ci		{4,  -25761, 1000, 1},
15262306a36Sopenharmony_ci		{3,   97332, 1000, 1},
15362306a36Sopenharmony_ci		{2, -191650, 1000, 1},
15462306a36Sopenharmony_ci		{1,  307620, 1000, 1},
15562306a36Sopenharmony_ci		{0,  -52156,    1, 1}
15662306a36Sopenharmony_ci	}
15762306a36Sopenharmony_ci};
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_cistatic int gpy_hwmon_read(struct device *dev,
16062306a36Sopenharmony_ci			  enum hwmon_sensor_types type,
16162306a36Sopenharmony_ci			  u32 attr, int channel, long *value)
16262306a36Sopenharmony_ci{
16362306a36Sopenharmony_ci	struct phy_device *phydev = dev_get_drvdata(dev);
16462306a36Sopenharmony_ci	int ret;
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	ret = phy_read_mmd(phydev, MDIO_MMD_VEND1, VSPEC1_TEMP_STA);
16762306a36Sopenharmony_ci	if (ret < 0)
16862306a36Sopenharmony_ci		return ret;
16962306a36Sopenharmony_ci	if (!ret)
17062306a36Sopenharmony_ci		return -ENODATA;
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	*value = polynomial_calc(&poly_N_to_temp,
17362306a36Sopenharmony_ci				 FIELD_GET(VSPEC1_TEMP_STA_DATA, ret));
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	return 0;
17662306a36Sopenharmony_ci}
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_cistatic umode_t gpy_hwmon_is_visible(const void *data,
17962306a36Sopenharmony_ci				    enum hwmon_sensor_types type,
18062306a36Sopenharmony_ci				    u32 attr, int channel)
18162306a36Sopenharmony_ci{
18262306a36Sopenharmony_ci	return 0444;
18362306a36Sopenharmony_ci}
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_cistatic const struct hwmon_channel_info * const gpy_hwmon_info[] = {
18662306a36Sopenharmony_ci	HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT),
18762306a36Sopenharmony_ci	NULL
18862306a36Sopenharmony_ci};
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_cistatic const struct hwmon_ops gpy_hwmon_hwmon_ops = {
19162306a36Sopenharmony_ci	.is_visible	= gpy_hwmon_is_visible,
19262306a36Sopenharmony_ci	.read		= gpy_hwmon_read,
19362306a36Sopenharmony_ci};
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_cistatic const struct hwmon_chip_info gpy_hwmon_chip_info = {
19662306a36Sopenharmony_ci	.ops		= &gpy_hwmon_hwmon_ops,
19762306a36Sopenharmony_ci	.info		= gpy_hwmon_info,
19862306a36Sopenharmony_ci};
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_cistatic int gpy_hwmon_register(struct phy_device *phydev)
20162306a36Sopenharmony_ci{
20262306a36Sopenharmony_ci	struct device *dev = &phydev->mdio.dev;
20362306a36Sopenharmony_ci	struct device *hwmon_dev;
20462306a36Sopenharmony_ci	char *hwmon_name;
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci	hwmon_name = devm_hwmon_sanitize_name(dev, dev_name(dev));
20762306a36Sopenharmony_ci	if (IS_ERR(hwmon_name))
20862306a36Sopenharmony_ci		return PTR_ERR(hwmon_name);
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci	hwmon_dev = devm_hwmon_device_register_with_info(dev, hwmon_name,
21162306a36Sopenharmony_ci							 phydev,
21262306a36Sopenharmony_ci							 &gpy_hwmon_chip_info,
21362306a36Sopenharmony_ci							 NULL);
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	return PTR_ERR_OR_ZERO(hwmon_dev);
21662306a36Sopenharmony_ci}
21762306a36Sopenharmony_ci#else
21862306a36Sopenharmony_cistatic int gpy_hwmon_register(struct phy_device *phydev)
21962306a36Sopenharmony_ci{
22062306a36Sopenharmony_ci	return 0;
22162306a36Sopenharmony_ci}
22262306a36Sopenharmony_ci#endif
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_cistatic int gpy_mbox_read(struct phy_device *phydev, u32 addr)
22562306a36Sopenharmony_ci{
22662306a36Sopenharmony_ci	struct gpy_priv *priv = phydev->priv;
22762306a36Sopenharmony_ci	int val, ret;
22862306a36Sopenharmony_ci	u16 cmd;
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	mutex_lock(&priv->mbox_lock);
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, VSPEC1_MBOX_ADDRLO,
23362306a36Sopenharmony_ci			    addr);
23462306a36Sopenharmony_ci	if (ret)
23562306a36Sopenharmony_ci		goto out;
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci	cmd = VSPEC1_MBOX_CMD_RD;
23862306a36Sopenharmony_ci	cmd |= FIELD_PREP(VSPEC1_MBOX_CMD_ADDRHI, addr >> 16);
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci	ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, VSPEC1_MBOX_CMD, cmd);
24162306a36Sopenharmony_ci	if (ret)
24262306a36Sopenharmony_ci		goto out;
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci	/* The mbox read is used in the interrupt workaround. It was observed
24562306a36Sopenharmony_ci	 * that a read might take up to 2.5ms. This is also the time for which
24662306a36Sopenharmony_ci	 * the interrupt line is stuck low. To be on the safe side, poll the
24762306a36Sopenharmony_ci	 * ready bit for 10ms.
24862306a36Sopenharmony_ci	 */
24962306a36Sopenharmony_ci	ret = phy_read_mmd_poll_timeout(phydev, MDIO_MMD_VEND1,
25062306a36Sopenharmony_ci					VSPEC1_MBOX_CMD, val,
25162306a36Sopenharmony_ci					(val & VSPEC1_MBOX_CMD_READY),
25262306a36Sopenharmony_ci					500, 10000, false);
25362306a36Sopenharmony_ci	if (ret)
25462306a36Sopenharmony_ci		goto out;
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	ret = phy_read_mmd(phydev, MDIO_MMD_VEND1, VSPEC1_MBOX_DATA);
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ciout:
25962306a36Sopenharmony_ci	mutex_unlock(&priv->mbox_lock);
26062306a36Sopenharmony_ci	return ret;
26162306a36Sopenharmony_ci}
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_cistatic int gpy_config_init(struct phy_device *phydev)
26462306a36Sopenharmony_ci{
26562306a36Sopenharmony_ci	int ret;
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci	/* Mask all interrupts */
26862306a36Sopenharmony_ci	ret = phy_write(phydev, PHY_IMASK, 0);
26962306a36Sopenharmony_ci	if (ret)
27062306a36Sopenharmony_ci		return ret;
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci	/* Clear all pending interrupts */
27362306a36Sopenharmony_ci	ret = phy_read(phydev, PHY_ISTAT);
27462306a36Sopenharmony_ci	return ret < 0 ? ret : 0;
27562306a36Sopenharmony_ci}
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_cistatic int gpy_probe(struct phy_device *phydev)
27862306a36Sopenharmony_ci{
27962306a36Sopenharmony_ci	struct device *dev = &phydev->mdio.dev;
28062306a36Sopenharmony_ci	struct gpy_priv *priv;
28162306a36Sopenharmony_ci	int fw_version;
28262306a36Sopenharmony_ci	int ret;
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci	if (!phydev->is_c45) {
28562306a36Sopenharmony_ci		ret = phy_get_c45_ids(phydev);
28662306a36Sopenharmony_ci		if (ret < 0)
28762306a36Sopenharmony_ci			return ret;
28862306a36Sopenharmony_ci	}
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
29162306a36Sopenharmony_ci	if (!priv)
29262306a36Sopenharmony_ci		return -ENOMEM;
29362306a36Sopenharmony_ci	phydev->priv = priv;
29462306a36Sopenharmony_ci	mutex_init(&priv->mbox_lock);
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci	if (!device_property_present(dev, "maxlinear,use-broken-interrupts"))
29762306a36Sopenharmony_ci		phydev->dev_flags |= PHY_F_NO_IRQ;
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci	fw_version = phy_read(phydev, PHY_FWV);
30062306a36Sopenharmony_ci	if (fw_version < 0)
30162306a36Sopenharmony_ci		return fw_version;
30262306a36Sopenharmony_ci	priv->fw_major = FIELD_GET(PHY_FWV_MAJOR_MASK, fw_version);
30362306a36Sopenharmony_ci	priv->fw_minor = FIELD_GET(PHY_FWV_MINOR_MASK, fw_version);
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci	ret = gpy_hwmon_register(phydev);
30662306a36Sopenharmony_ci	if (ret)
30762306a36Sopenharmony_ci		return ret;
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci	/* Show GPY PHY FW version in dmesg */
31062306a36Sopenharmony_ci	phydev_info(phydev, "Firmware Version: %d.%d (0x%04X%s)\n",
31162306a36Sopenharmony_ci		    priv->fw_major, priv->fw_minor, fw_version,
31262306a36Sopenharmony_ci		    fw_version & PHY_FWV_REL_MASK ? "" : " test version");
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci	return 0;
31562306a36Sopenharmony_ci}
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_cistatic bool gpy_sgmii_need_reaneg(struct phy_device *phydev)
31862306a36Sopenharmony_ci{
31962306a36Sopenharmony_ci	struct gpy_priv *priv = phydev->priv;
32062306a36Sopenharmony_ci	size_t i;
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(ver_need_sgmii_reaneg); i++) {
32362306a36Sopenharmony_ci		if (priv->fw_major != ver_need_sgmii_reaneg[i].major)
32462306a36Sopenharmony_ci			continue;
32562306a36Sopenharmony_ci		if (priv->fw_minor < ver_need_sgmii_reaneg[i].minor)
32662306a36Sopenharmony_ci			return true;
32762306a36Sopenharmony_ci		break;
32862306a36Sopenharmony_ci	}
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci	return false;
33162306a36Sopenharmony_ci}
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_cistatic bool gpy_2500basex_chk(struct phy_device *phydev)
33462306a36Sopenharmony_ci{
33562306a36Sopenharmony_ci	int ret;
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci	ret = phy_read(phydev, PHY_MIISTAT);
33862306a36Sopenharmony_ci	if (ret < 0) {
33962306a36Sopenharmony_ci		phydev_err(phydev, "Error: MDIO register access failed: %d\n",
34062306a36Sopenharmony_ci			   ret);
34162306a36Sopenharmony_ci		return false;
34262306a36Sopenharmony_ci	}
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci	if (!(ret & PHY_MIISTAT_LS) ||
34562306a36Sopenharmony_ci	    FIELD_GET(PHY_MIISTAT_SPD_MASK, ret) != PHY_MIISTAT_SPD_2500)
34662306a36Sopenharmony_ci		return false;
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci	phydev->speed = SPEED_2500;
34962306a36Sopenharmony_ci	phydev->interface = PHY_INTERFACE_MODE_2500BASEX;
35062306a36Sopenharmony_ci	phy_modify_mmd(phydev, MDIO_MMD_VEND1, VSPEC1_SGMII_CTRL,
35162306a36Sopenharmony_ci		       VSPEC1_SGMII_CTRL_ANEN, 0);
35262306a36Sopenharmony_ci	return true;
35362306a36Sopenharmony_ci}
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_cistatic bool gpy_sgmii_aneg_en(struct phy_device *phydev)
35662306a36Sopenharmony_ci{
35762306a36Sopenharmony_ci	int ret;
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci	ret = phy_read_mmd(phydev, MDIO_MMD_VEND1, VSPEC1_SGMII_CTRL);
36062306a36Sopenharmony_ci	if (ret < 0) {
36162306a36Sopenharmony_ci		phydev_err(phydev, "Error: MMD register access failed: %d\n",
36262306a36Sopenharmony_ci			   ret);
36362306a36Sopenharmony_ci		return true;
36462306a36Sopenharmony_ci	}
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci	return (ret & VSPEC1_SGMII_CTRL_ANEN) ? true : false;
36762306a36Sopenharmony_ci}
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_cistatic int gpy_config_mdix(struct phy_device *phydev, u8 ctrl)
37062306a36Sopenharmony_ci{
37162306a36Sopenharmony_ci	int ret;
37262306a36Sopenharmony_ci	u16 val;
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci	switch (ctrl) {
37562306a36Sopenharmony_ci	case ETH_TP_MDI_AUTO:
37662306a36Sopenharmony_ci		val = PHY_CTL1_AMDIX;
37762306a36Sopenharmony_ci		break;
37862306a36Sopenharmony_ci	case ETH_TP_MDI_X:
37962306a36Sopenharmony_ci		val = (PHY_CTL1_MDIAB | PHY_CTL1_MDICD);
38062306a36Sopenharmony_ci		break;
38162306a36Sopenharmony_ci	case ETH_TP_MDI:
38262306a36Sopenharmony_ci		val = 0;
38362306a36Sopenharmony_ci		break;
38462306a36Sopenharmony_ci	default:
38562306a36Sopenharmony_ci		return 0;
38662306a36Sopenharmony_ci	}
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci	ret =  phy_modify(phydev, PHY_CTL1, PHY_CTL1_AMDIX | PHY_CTL1_MDIAB |
38962306a36Sopenharmony_ci			  PHY_CTL1_MDICD, val);
39062306a36Sopenharmony_ci	if (ret < 0)
39162306a36Sopenharmony_ci		return ret;
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci	return genphy_c45_restart_aneg(phydev);
39462306a36Sopenharmony_ci}
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_cistatic int gpy_config_aneg(struct phy_device *phydev)
39762306a36Sopenharmony_ci{
39862306a36Sopenharmony_ci	bool changed = false;
39962306a36Sopenharmony_ci	u32 adv;
40062306a36Sopenharmony_ci	int ret;
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci	if (phydev->autoneg == AUTONEG_DISABLE) {
40362306a36Sopenharmony_ci		/* Configure half duplex with genphy_setup_forced,
40462306a36Sopenharmony_ci		 * because genphy_c45_pma_setup_forced does not support.
40562306a36Sopenharmony_ci		 */
40662306a36Sopenharmony_ci		return phydev->duplex != DUPLEX_FULL
40762306a36Sopenharmony_ci			? genphy_setup_forced(phydev)
40862306a36Sopenharmony_ci			: genphy_c45_pma_setup_forced(phydev);
40962306a36Sopenharmony_ci	}
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci	ret = gpy_config_mdix(phydev,  phydev->mdix_ctrl);
41262306a36Sopenharmony_ci	if (ret < 0)
41362306a36Sopenharmony_ci		return ret;
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci	ret = genphy_c45_an_config_aneg(phydev);
41662306a36Sopenharmony_ci	if (ret < 0)
41762306a36Sopenharmony_ci		return ret;
41862306a36Sopenharmony_ci	if (ret > 0)
41962306a36Sopenharmony_ci		changed = true;
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_ci	adv = linkmode_adv_to_mii_ctrl1000_t(phydev->advertising);
42262306a36Sopenharmony_ci	ret = phy_modify_changed(phydev, MII_CTRL1000,
42362306a36Sopenharmony_ci				 ADVERTISE_1000FULL | ADVERTISE_1000HALF,
42462306a36Sopenharmony_ci				 adv);
42562306a36Sopenharmony_ci	if (ret < 0)
42662306a36Sopenharmony_ci		return ret;
42762306a36Sopenharmony_ci	if (ret > 0)
42862306a36Sopenharmony_ci		changed = true;
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci	ret = genphy_c45_check_and_restart_aneg(phydev, changed);
43162306a36Sopenharmony_ci	if (ret < 0)
43262306a36Sopenharmony_ci		return ret;
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci	if (phydev->interface == PHY_INTERFACE_MODE_USXGMII ||
43562306a36Sopenharmony_ci	    phydev->interface == PHY_INTERFACE_MODE_INTERNAL)
43662306a36Sopenharmony_ci		return 0;
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_ci	/* No need to trigger re-ANEG if link speed is 2.5G or SGMII ANEG is
43962306a36Sopenharmony_ci	 * disabled.
44062306a36Sopenharmony_ci	 */
44162306a36Sopenharmony_ci	if (!gpy_sgmii_need_reaneg(phydev) || gpy_2500basex_chk(phydev) ||
44262306a36Sopenharmony_ci	    !gpy_sgmii_aneg_en(phydev))
44362306a36Sopenharmony_ci		return 0;
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ci	/* There is a design constraint in GPY2xx device where SGMII AN is
44662306a36Sopenharmony_ci	 * only triggered when there is change of speed. If, PHY link
44762306a36Sopenharmony_ci	 * partner`s speed is still same even after PHY TPI is down and up
44862306a36Sopenharmony_ci	 * again, SGMII AN is not triggered and hence no new in-band message
44962306a36Sopenharmony_ci	 * from GPY to MAC side SGMII.
45062306a36Sopenharmony_ci	 * This could cause an issue during power up, when PHY is up prior to
45162306a36Sopenharmony_ci	 * MAC. At this condition, once MAC side SGMII is up, MAC side SGMII
45262306a36Sopenharmony_ci	 * wouldn`t receive new in-band message from GPY with correct link
45362306a36Sopenharmony_ci	 * status, speed and duplex info.
45462306a36Sopenharmony_ci	 *
45562306a36Sopenharmony_ci	 * 1) If PHY is already up and TPI link status is still down (such as
45662306a36Sopenharmony_ci	 *    hard reboot), TPI link status is polled for 4 seconds before
45762306a36Sopenharmony_ci	 *    retriggerring SGMII AN.
45862306a36Sopenharmony_ci	 * 2) If PHY is already up and TPI link status is also up (such as soft
45962306a36Sopenharmony_ci	 *    reboot), polling of TPI link status is not needed and SGMII AN is
46062306a36Sopenharmony_ci	 *    immediately retriggered.
46162306a36Sopenharmony_ci	 * 3) Other conditions such as PHY is down, speed change etc, skip
46262306a36Sopenharmony_ci	 *    retriggering SGMII AN. Note: in case of speed change, GPY FW will
46362306a36Sopenharmony_ci	 *    initiate SGMII AN.
46462306a36Sopenharmony_ci	 */
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_ci	if (phydev->state != PHY_UP)
46762306a36Sopenharmony_ci		return 0;
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci	ret = phy_read_poll_timeout(phydev, MII_BMSR, ret, ret & BMSR_LSTATUS,
47062306a36Sopenharmony_ci				    20000, 4000000, false);
47162306a36Sopenharmony_ci	if (ret == -ETIMEDOUT)
47262306a36Sopenharmony_ci		return 0;
47362306a36Sopenharmony_ci	else if (ret < 0)
47462306a36Sopenharmony_ci		return ret;
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_ci	/* Trigger SGMII AN. */
47762306a36Sopenharmony_ci	return phy_modify_mmd(phydev, MDIO_MMD_VEND1, VSPEC1_SGMII_CTRL,
47862306a36Sopenharmony_ci			      VSPEC1_SGMII_CTRL_ANRS, VSPEC1_SGMII_CTRL_ANRS);
47962306a36Sopenharmony_ci}
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_cistatic int gpy_update_mdix(struct phy_device *phydev)
48262306a36Sopenharmony_ci{
48362306a36Sopenharmony_ci	int ret;
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ci	ret = phy_read(phydev, PHY_CTL1);
48662306a36Sopenharmony_ci	if (ret < 0)
48762306a36Sopenharmony_ci		return ret;
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_ci	if (ret & PHY_CTL1_AMDIX)
49062306a36Sopenharmony_ci		phydev->mdix_ctrl = ETH_TP_MDI_AUTO;
49162306a36Sopenharmony_ci	else
49262306a36Sopenharmony_ci		if (ret & PHY_CTL1_MDICD || ret & PHY_CTL1_MDIAB)
49362306a36Sopenharmony_ci			phydev->mdix_ctrl = ETH_TP_MDI_X;
49462306a36Sopenharmony_ci		else
49562306a36Sopenharmony_ci			phydev->mdix_ctrl = ETH_TP_MDI;
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_ci	ret = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, PHY_PMA_MGBT_POLARITY);
49862306a36Sopenharmony_ci	if (ret < 0)
49962306a36Sopenharmony_ci		return ret;
50062306a36Sopenharmony_ci
50162306a36Sopenharmony_ci	if ((ret & PHY_MDI_MDI_X_MASK) < PHY_MDI_MDI_X_NORMAL)
50262306a36Sopenharmony_ci		phydev->mdix = ETH_TP_MDI_X;
50362306a36Sopenharmony_ci	else
50462306a36Sopenharmony_ci		phydev->mdix = ETH_TP_MDI;
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_ci	return 0;
50762306a36Sopenharmony_ci}
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_cistatic int gpy_update_interface(struct phy_device *phydev)
51062306a36Sopenharmony_ci{
51162306a36Sopenharmony_ci	int ret;
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_ci	/* Interface mode is fixed for USXGMII and integrated PHY */
51462306a36Sopenharmony_ci	if (phydev->interface == PHY_INTERFACE_MODE_USXGMII ||
51562306a36Sopenharmony_ci	    phydev->interface == PHY_INTERFACE_MODE_INTERNAL)
51662306a36Sopenharmony_ci		return -EINVAL;
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_ci	/* Automatically switch SERDES interface between SGMII and 2500-BaseX
51962306a36Sopenharmony_ci	 * according to speed. Disable ANEG in 2500-BaseX mode.
52062306a36Sopenharmony_ci	 */
52162306a36Sopenharmony_ci	switch (phydev->speed) {
52262306a36Sopenharmony_ci	case SPEED_2500:
52362306a36Sopenharmony_ci		phydev->interface = PHY_INTERFACE_MODE_2500BASEX;
52462306a36Sopenharmony_ci		ret = phy_modify_mmd(phydev, MDIO_MMD_VEND1, VSPEC1_SGMII_CTRL,
52562306a36Sopenharmony_ci				     VSPEC1_SGMII_CTRL_ANEN, 0);
52662306a36Sopenharmony_ci		if (ret < 0) {
52762306a36Sopenharmony_ci			phydev_err(phydev,
52862306a36Sopenharmony_ci				   "Error: Disable of SGMII ANEG failed: %d\n",
52962306a36Sopenharmony_ci				   ret);
53062306a36Sopenharmony_ci			return ret;
53162306a36Sopenharmony_ci		}
53262306a36Sopenharmony_ci		break;
53362306a36Sopenharmony_ci	case SPEED_1000:
53462306a36Sopenharmony_ci	case SPEED_100:
53562306a36Sopenharmony_ci	case SPEED_10:
53662306a36Sopenharmony_ci		phydev->interface = PHY_INTERFACE_MODE_SGMII;
53762306a36Sopenharmony_ci		if (gpy_sgmii_aneg_en(phydev))
53862306a36Sopenharmony_ci			break;
53962306a36Sopenharmony_ci		/* Enable and restart SGMII ANEG for 10/100/1000Mbps link speed
54062306a36Sopenharmony_ci		 * if ANEG is disabled (in 2500-BaseX mode).
54162306a36Sopenharmony_ci		 */
54262306a36Sopenharmony_ci		ret = phy_modify_mmd(phydev, MDIO_MMD_VEND1, VSPEC1_SGMII_CTRL,
54362306a36Sopenharmony_ci				     VSPEC1_SGMII_ANEN_ANRS,
54462306a36Sopenharmony_ci				     VSPEC1_SGMII_ANEN_ANRS);
54562306a36Sopenharmony_ci		if (ret < 0) {
54662306a36Sopenharmony_ci			phydev_err(phydev,
54762306a36Sopenharmony_ci				   "Error: Enable of SGMII ANEG failed: %d\n",
54862306a36Sopenharmony_ci				   ret);
54962306a36Sopenharmony_ci			return ret;
55062306a36Sopenharmony_ci		}
55162306a36Sopenharmony_ci		break;
55262306a36Sopenharmony_ci	}
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_ci	if (phydev->speed == SPEED_2500 || phydev->speed == SPEED_1000) {
55562306a36Sopenharmony_ci		ret = genphy_read_master_slave(phydev);
55662306a36Sopenharmony_ci		if (ret < 0)
55762306a36Sopenharmony_ci			return ret;
55862306a36Sopenharmony_ci	}
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_ci	return gpy_update_mdix(phydev);
56162306a36Sopenharmony_ci}
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_cistatic int gpy_read_status(struct phy_device *phydev)
56462306a36Sopenharmony_ci{
56562306a36Sopenharmony_ci	int ret;
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_ci	ret = genphy_update_link(phydev);
56862306a36Sopenharmony_ci	if (ret)
56962306a36Sopenharmony_ci		return ret;
57062306a36Sopenharmony_ci
57162306a36Sopenharmony_ci	phydev->speed = SPEED_UNKNOWN;
57262306a36Sopenharmony_ci	phydev->duplex = DUPLEX_UNKNOWN;
57362306a36Sopenharmony_ci	phydev->pause = 0;
57462306a36Sopenharmony_ci	phydev->asym_pause = 0;
57562306a36Sopenharmony_ci
57662306a36Sopenharmony_ci	if (phydev->autoneg == AUTONEG_ENABLE && phydev->autoneg_complete) {
57762306a36Sopenharmony_ci		ret = genphy_c45_read_lpa(phydev);
57862306a36Sopenharmony_ci		if (ret < 0)
57962306a36Sopenharmony_ci			return ret;
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_ci		/* Read the link partner's 1G advertisement */
58262306a36Sopenharmony_ci		ret = phy_read(phydev, MII_STAT1000);
58362306a36Sopenharmony_ci		if (ret < 0)
58462306a36Sopenharmony_ci			return ret;
58562306a36Sopenharmony_ci		mii_stat1000_mod_linkmode_lpa_t(phydev->lp_advertising, ret);
58662306a36Sopenharmony_ci	} else if (phydev->autoneg == AUTONEG_DISABLE) {
58762306a36Sopenharmony_ci		linkmode_zero(phydev->lp_advertising);
58862306a36Sopenharmony_ci	}
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_ci	ret = phy_read(phydev, PHY_MIISTAT);
59162306a36Sopenharmony_ci	if (ret < 0)
59262306a36Sopenharmony_ci		return ret;
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_ci	phydev->link = (ret & PHY_MIISTAT_LS) ? 1 : 0;
59562306a36Sopenharmony_ci	phydev->duplex = (ret & PHY_MIISTAT_DPX) ? DUPLEX_FULL : DUPLEX_HALF;
59662306a36Sopenharmony_ci	switch (FIELD_GET(PHY_MIISTAT_SPD_MASK, ret)) {
59762306a36Sopenharmony_ci	case PHY_MIISTAT_SPD_10:
59862306a36Sopenharmony_ci		phydev->speed = SPEED_10;
59962306a36Sopenharmony_ci		break;
60062306a36Sopenharmony_ci	case PHY_MIISTAT_SPD_100:
60162306a36Sopenharmony_ci		phydev->speed = SPEED_100;
60262306a36Sopenharmony_ci		break;
60362306a36Sopenharmony_ci	case PHY_MIISTAT_SPD_1000:
60462306a36Sopenharmony_ci		phydev->speed = SPEED_1000;
60562306a36Sopenharmony_ci		break;
60662306a36Sopenharmony_ci	case PHY_MIISTAT_SPD_2500:
60762306a36Sopenharmony_ci		phydev->speed = SPEED_2500;
60862306a36Sopenharmony_ci		break;
60962306a36Sopenharmony_ci	}
61062306a36Sopenharmony_ci
61162306a36Sopenharmony_ci	if (phydev->link) {
61262306a36Sopenharmony_ci		ret = gpy_update_interface(phydev);
61362306a36Sopenharmony_ci		if (ret < 0)
61462306a36Sopenharmony_ci			return ret;
61562306a36Sopenharmony_ci	}
61662306a36Sopenharmony_ci
61762306a36Sopenharmony_ci	return 0;
61862306a36Sopenharmony_ci}
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_cistatic int gpy_config_intr(struct phy_device *phydev)
62162306a36Sopenharmony_ci{
62262306a36Sopenharmony_ci	u16 mask = 0;
62362306a36Sopenharmony_ci
62462306a36Sopenharmony_ci	if (phydev->interrupts == PHY_INTERRUPT_ENABLED)
62562306a36Sopenharmony_ci		mask = PHY_IMASK_MASK;
62662306a36Sopenharmony_ci
62762306a36Sopenharmony_ci	return phy_write(phydev, PHY_IMASK, mask);
62862306a36Sopenharmony_ci}
62962306a36Sopenharmony_ci
63062306a36Sopenharmony_cistatic irqreturn_t gpy_handle_interrupt(struct phy_device *phydev)
63162306a36Sopenharmony_ci{
63262306a36Sopenharmony_ci	int reg;
63362306a36Sopenharmony_ci
63462306a36Sopenharmony_ci	reg = phy_read(phydev, PHY_ISTAT);
63562306a36Sopenharmony_ci	if (reg < 0) {
63662306a36Sopenharmony_ci		phy_error(phydev);
63762306a36Sopenharmony_ci		return IRQ_NONE;
63862306a36Sopenharmony_ci	}
63962306a36Sopenharmony_ci
64062306a36Sopenharmony_ci	if (!(reg & PHY_IMASK_MASK))
64162306a36Sopenharmony_ci		return IRQ_NONE;
64262306a36Sopenharmony_ci
64362306a36Sopenharmony_ci	/* The PHY might leave the interrupt line asserted even after PHY_ISTAT
64462306a36Sopenharmony_ci	 * is read. To avoid interrupt storms, delay the interrupt handling as
64562306a36Sopenharmony_ci	 * long as the PHY drives the interrupt line. An internal bus read will
64662306a36Sopenharmony_ci	 * stall as long as the interrupt line is asserted, thus just read a
64762306a36Sopenharmony_ci	 * random register here.
64862306a36Sopenharmony_ci	 * Because we cannot access the internal bus at all while the interrupt
64962306a36Sopenharmony_ci	 * is driven by the PHY, there is no way to make the interrupt line
65062306a36Sopenharmony_ci	 * unstuck (e.g. by changing the pinmux to GPIO input) during that time
65162306a36Sopenharmony_ci	 * frame. Therefore, polling is the best we can do and won't do any more
65262306a36Sopenharmony_ci	 * harm.
65362306a36Sopenharmony_ci	 * It was observed that this bug happens on link state and link speed
65462306a36Sopenharmony_ci	 * changes independent of the firmware version.
65562306a36Sopenharmony_ci	 */
65662306a36Sopenharmony_ci	if (reg & (PHY_IMASK_LSTC | PHY_IMASK_LSPC)) {
65762306a36Sopenharmony_ci		reg = gpy_mbox_read(phydev, REG_GPIO0_OUT);
65862306a36Sopenharmony_ci		if (reg < 0) {
65962306a36Sopenharmony_ci			phy_error(phydev);
66062306a36Sopenharmony_ci			return IRQ_NONE;
66162306a36Sopenharmony_ci		}
66262306a36Sopenharmony_ci	}
66362306a36Sopenharmony_ci
66462306a36Sopenharmony_ci	phy_trigger_machine(phydev);
66562306a36Sopenharmony_ci
66662306a36Sopenharmony_ci	return IRQ_HANDLED;
66762306a36Sopenharmony_ci}
66862306a36Sopenharmony_ci
66962306a36Sopenharmony_cistatic int gpy_set_wol(struct phy_device *phydev,
67062306a36Sopenharmony_ci		       struct ethtool_wolinfo *wol)
67162306a36Sopenharmony_ci{
67262306a36Sopenharmony_ci	struct net_device *attach_dev = phydev->attached_dev;
67362306a36Sopenharmony_ci	int ret;
67462306a36Sopenharmony_ci
67562306a36Sopenharmony_ci	if (wol->wolopts & WAKE_MAGIC) {
67662306a36Sopenharmony_ci		/* MAC address - Byte0:Byte1:Byte2:Byte3:Byte4:Byte5
67762306a36Sopenharmony_ci		 * VPSPEC2_WOL_AD45 = Byte0:Byte1
67862306a36Sopenharmony_ci		 * VPSPEC2_WOL_AD23 = Byte2:Byte3
67962306a36Sopenharmony_ci		 * VPSPEC2_WOL_AD01 = Byte4:Byte5
68062306a36Sopenharmony_ci		 */
68162306a36Sopenharmony_ci		ret = phy_set_bits_mmd(phydev, MDIO_MMD_VEND2,
68262306a36Sopenharmony_ci				       VPSPEC2_WOL_AD45,
68362306a36Sopenharmony_ci				       ((attach_dev->dev_addr[0] << 8) |
68462306a36Sopenharmony_ci				       attach_dev->dev_addr[1]));
68562306a36Sopenharmony_ci		if (ret < 0)
68662306a36Sopenharmony_ci			return ret;
68762306a36Sopenharmony_ci
68862306a36Sopenharmony_ci		ret = phy_set_bits_mmd(phydev, MDIO_MMD_VEND2,
68962306a36Sopenharmony_ci				       VPSPEC2_WOL_AD23,
69062306a36Sopenharmony_ci				       ((attach_dev->dev_addr[2] << 8) |
69162306a36Sopenharmony_ci				       attach_dev->dev_addr[3]));
69262306a36Sopenharmony_ci		if (ret < 0)
69362306a36Sopenharmony_ci			return ret;
69462306a36Sopenharmony_ci
69562306a36Sopenharmony_ci		ret = phy_set_bits_mmd(phydev, MDIO_MMD_VEND2,
69662306a36Sopenharmony_ci				       VPSPEC2_WOL_AD01,
69762306a36Sopenharmony_ci				       ((attach_dev->dev_addr[4] << 8) |
69862306a36Sopenharmony_ci				       attach_dev->dev_addr[5]));
69962306a36Sopenharmony_ci		if (ret < 0)
70062306a36Sopenharmony_ci			return ret;
70162306a36Sopenharmony_ci
70262306a36Sopenharmony_ci		/* Enable the WOL interrupt */
70362306a36Sopenharmony_ci		ret = phy_write(phydev, PHY_IMASK, PHY_IMASK_WOL);
70462306a36Sopenharmony_ci		if (ret < 0)
70562306a36Sopenharmony_ci			return ret;
70662306a36Sopenharmony_ci
70762306a36Sopenharmony_ci		/* Enable magic packet matching */
70862306a36Sopenharmony_ci		ret = phy_set_bits_mmd(phydev, MDIO_MMD_VEND2,
70962306a36Sopenharmony_ci				       VPSPEC2_WOL_CTL,
71062306a36Sopenharmony_ci				       WOL_EN);
71162306a36Sopenharmony_ci		if (ret < 0)
71262306a36Sopenharmony_ci			return ret;
71362306a36Sopenharmony_ci
71462306a36Sopenharmony_ci		/* Clear the interrupt status register.
71562306a36Sopenharmony_ci		 * Only WoL is enabled so clear all.
71662306a36Sopenharmony_ci		 */
71762306a36Sopenharmony_ci		ret = phy_read(phydev, PHY_ISTAT);
71862306a36Sopenharmony_ci		if (ret < 0)
71962306a36Sopenharmony_ci			return ret;
72062306a36Sopenharmony_ci	} else {
72162306a36Sopenharmony_ci		/* Disable magic packet matching */
72262306a36Sopenharmony_ci		ret = phy_clear_bits_mmd(phydev, MDIO_MMD_VEND2,
72362306a36Sopenharmony_ci					 VPSPEC2_WOL_CTL,
72462306a36Sopenharmony_ci					 WOL_EN);
72562306a36Sopenharmony_ci		if (ret < 0)
72662306a36Sopenharmony_ci			return ret;
72762306a36Sopenharmony_ci	}
72862306a36Sopenharmony_ci
72962306a36Sopenharmony_ci	if (wol->wolopts & WAKE_PHY) {
73062306a36Sopenharmony_ci		/* Enable the link state change interrupt */
73162306a36Sopenharmony_ci		ret = phy_set_bits(phydev, PHY_IMASK, PHY_IMASK_LSTC);
73262306a36Sopenharmony_ci		if (ret < 0)
73362306a36Sopenharmony_ci			return ret;
73462306a36Sopenharmony_ci
73562306a36Sopenharmony_ci		/* Clear the interrupt status register */
73662306a36Sopenharmony_ci		ret = phy_read(phydev, PHY_ISTAT);
73762306a36Sopenharmony_ci		if (ret < 0)
73862306a36Sopenharmony_ci			return ret;
73962306a36Sopenharmony_ci
74062306a36Sopenharmony_ci		if (ret & (PHY_IMASK_MASK & ~PHY_IMASK_LSTC))
74162306a36Sopenharmony_ci			phy_trigger_machine(phydev);
74262306a36Sopenharmony_ci
74362306a36Sopenharmony_ci		return 0;
74462306a36Sopenharmony_ci	}
74562306a36Sopenharmony_ci
74662306a36Sopenharmony_ci	/* Disable the link state change interrupt */
74762306a36Sopenharmony_ci	return phy_clear_bits(phydev, PHY_IMASK, PHY_IMASK_LSTC);
74862306a36Sopenharmony_ci}
74962306a36Sopenharmony_ci
75062306a36Sopenharmony_cistatic void gpy_get_wol(struct phy_device *phydev,
75162306a36Sopenharmony_ci			struct ethtool_wolinfo *wol)
75262306a36Sopenharmony_ci{
75362306a36Sopenharmony_ci	int ret;
75462306a36Sopenharmony_ci
75562306a36Sopenharmony_ci	wol->supported = WAKE_MAGIC | WAKE_PHY;
75662306a36Sopenharmony_ci	wol->wolopts = 0;
75762306a36Sopenharmony_ci
75862306a36Sopenharmony_ci	ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, VPSPEC2_WOL_CTL);
75962306a36Sopenharmony_ci	if (ret & WOL_EN)
76062306a36Sopenharmony_ci		wol->wolopts |= WAKE_MAGIC;
76162306a36Sopenharmony_ci
76262306a36Sopenharmony_ci	ret = phy_read(phydev, PHY_IMASK);
76362306a36Sopenharmony_ci	if (ret & PHY_IMASK_LSTC)
76462306a36Sopenharmony_ci		wol->wolopts |= WAKE_PHY;
76562306a36Sopenharmony_ci}
76662306a36Sopenharmony_ci
76762306a36Sopenharmony_cistatic int gpy_loopback(struct phy_device *phydev, bool enable)
76862306a36Sopenharmony_ci{
76962306a36Sopenharmony_ci	struct gpy_priv *priv = phydev->priv;
77062306a36Sopenharmony_ci	u16 set = 0;
77162306a36Sopenharmony_ci	int ret;
77262306a36Sopenharmony_ci
77362306a36Sopenharmony_ci	if (enable) {
77462306a36Sopenharmony_ci		u64 now = get_jiffies_64();
77562306a36Sopenharmony_ci
77662306a36Sopenharmony_ci		/* wait until 3 seconds from last disable */
77762306a36Sopenharmony_ci		if (time_before64(now, priv->lb_dis_to))
77862306a36Sopenharmony_ci			msleep(jiffies64_to_msecs(priv->lb_dis_to - now));
77962306a36Sopenharmony_ci
78062306a36Sopenharmony_ci		set = BMCR_LOOPBACK;
78162306a36Sopenharmony_ci	}
78262306a36Sopenharmony_ci
78362306a36Sopenharmony_ci	ret = phy_modify(phydev, MII_BMCR, BMCR_LOOPBACK, set);
78462306a36Sopenharmony_ci	if (ret <= 0)
78562306a36Sopenharmony_ci		return ret;
78662306a36Sopenharmony_ci
78762306a36Sopenharmony_ci	if (enable) {
78862306a36Sopenharmony_ci		/* It takes some time for PHY device to switch into
78962306a36Sopenharmony_ci		 * loopback mode.
79062306a36Sopenharmony_ci		 */
79162306a36Sopenharmony_ci		msleep(100);
79262306a36Sopenharmony_ci	} else {
79362306a36Sopenharmony_ci		priv->lb_dis_to = get_jiffies_64() + HZ * 3;
79462306a36Sopenharmony_ci	}
79562306a36Sopenharmony_ci
79662306a36Sopenharmony_ci	return 0;
79762306a36Sopenharmony_ci}
79862306a36Sopenharmony_ci
79962306a36Sopenharmony_cistatic int gpy115_loopback(struct phy_device *phydev, bool enable)
80062306a36Sopenharmony_ci{
80162306a36Sopenharmony_ci	struct gpy_priv *priv = phydev->priv;
80262306a36Sopenharmony_ci
80362306a36Sopenharmony_ci	if (enable)
80462306a36Sopenharmony_ci		return gpy_loopback(phydev, enable);
80562306a36Sopenharmony_ci
80662306a36Sopenharmony_ci	if (priv->fw_minor > 0x76)
80762306a36Sopenharmony_ci		return gpy_loopback(phydev, 0);
80862306a36Sopenharmony_ci
80962306a36Sopenharmony_ci	return genphy_soft_reset(phydev);
81062306a36Sopenharmony_ci}
81162306a36Sopenharmony_ci
81262306a36Sopenharmony_cistatic struct phy_driver gpy_drivers[] = {
81362306a36Sopenharmony_ci	{
81462306a36Sopenharmony_ci		PHY_ID_MATCH_MODEL(PHY_ID_GPY2xx),
81562306a36Sopenharmony_ci		.name		= "Maxlinear Ethernet GPY2xx",
81662306a36Sopenharmony_ci		.get_features	= genphy_c45_pma_read_abilities,
81762306a36Sopenharmony_ci		.config_init	= gpy_config_init,
81862306a36Sopenharmony_ci		.probe		= gpy_probe,
81962306a36Sopenharmony_ci		.suspend	= genphy_suspend,
82062306a36Sopenharmony_ci		.resume		= genphy_resume,
82162306a36Sopenharmony_ci		.config_aneg	= gpy_config_aneg,
82262306a36Sopenharmony_ci		.aneg_done	= genphy_c45_aneg_done,
82362306a36Sopenharmony_ci		.read_status	= gpy_read_status,
82462306a36Sopenharmony_ci		.config_intr	= gpy_config_intr,
82562306a36Sopenharmony_ci		.handle_interrupt = gpy_handle_interrupt,
82662306a36Sopenharmony_ci		.set_wol	= gpy_set_wol,
82762306a36Sopenharmony_ci		.get_wol	= gpy_get_wol,
82862306a36Sopenharmony_ci		.set_loopback	= gpy_loopback,
82962306a36Sopenharmony_ci	},
83062306a36Sopenharmony_ci	{
83162306a36Sopenharmony_ci		.phy_id		= PHY_ID_GPY115B,
83262306a36Sopenharmony_ci		.phy_id_mask	= PHY_ID_GPYx15B_MASK,
83362306a36Sopenharmony_ci		.name		= "Maxlinear Ethernet GPY115B",
83462306a36Sopenharmony_ci		.get_features	= genphy_c45_pma_read_abilities,
83562306a36Sopenharmony_ci		.config_init	= gpy_config_init,
83662306a36Sopenharmony_ci		.probe		= gpy_probe,
83762306a36Sopenharmony_ci		.suspend	= genphy_suspend,
83862306a36Sopenharmony_ci		.resume		= genphy_resume,
83962306a36Sopenharmony_ci		.config_aneg	= gpy_config_aneg,
84062306a36Sopenharmony_ci		.aneg_done	= genphy_c45_aneg_done,
84162306a36Sopenharmony_ci		.read_status	= gpy_read_status,
84262306a36Sopenharmony_ci		.config_intr	= gpy_config_intr,
84362306a36Sopenharmony_ci		.handle_interrupt = gpy_handle_interrupt,
84462306a36Sopenharmony_ci		.set_wol	= gpy_set_wol,
84562306a36Sopenharmony_ci		.get_wol	= gpy_get_wol,
84662306a36Sopenharmony_ci		.set_loopback	= gpy115_loopback,
84762306a36Sopenharmony_ci	},
84862306a36Sopenharmony_ci	{
84962306a36Sopenharmony_ci		PHY_ID_MATCH_MODEL(PHY_ID_GPY115C),
85062306a36Sopenharmony_ci		.name		= "Maxlinear Ethernet GPY115C",
85162306a36Sopenharmony_ci		.get_features	= genphy_c45_pma_read_abilities,
85262306a36Sopenharmony_ci		.config_init	= gpy_config_init,
85362306a36Sopenharmony_ci		.probe		= gpy_probe,
85462306a36Sopenharmony_ci		.suspend	= genphy_suspend,
85562306a36Sopenharmony_ci		.resume		= genphy_resume,
85662306a36Sopenharmony_ci		.config_aneg	= gpy_config_aneg,
85762306a36Sopenharmony_ci		.aneg_done	= genphy_c45_aneg_done,
85862306a36Sopenharmony_ci		.read_status	= gpy_read_status,
85962306a36Sopenharmony_ci		.config_intr	= gpy_config_intr,
86062306a36Sopenharmony_ci		.handle_interrupt = gpy_handle_interrupt,
86162306a36Sopenharmony_ci		.set_wol	= gpy_set_wol,
86262306a36Sopenharmony_ci		.get_wol	= gpy_get_wol,
86362306a36Sopenharmony_ci		.set_loopback	= gpy115_loopback,
86462306a36Sopenharmony_ci	},
86562306a36Sopenharmony_ci	{
86662306a36Sopenharmony_ci		.phy_id		= PHY_ID_GPY211B,
86762306a36Sopenharmony_ci		.phy_id_mask	= PHY_ID_GPY21xB_MASK,
86862306a36Sopenharmony_ci		.name		= "Maxlinear Ethernet GPY211B",
86962306a36Sopenharmony_ci		.get_features	= genphy_c45_pma_read_abilities,
87062306a36Sopenharmony_ci		.config_init	= gpy_config_init,
87162306a36Sopenharmony_ci		.probe		= gpy_probe,
87262306a36Sopenharmony_ci		.suspend	= genphy_suspend,
87362306a36Sopenharmony_ci		.resume		= genphy_resume,
87462306a36Sopenharmony_ci		.config_aneg	= gpy_config_aneg,
87562306a36Sopenharmony_ci		.aneg_done	= genphy_c45_aneg_done,
87662306a36Sopenharmony_ci		.read_status	= gpy_read_status,
87762306a36Sopenharmony_ci		.config_intr	= gpy_config_intr,
87862306a36Sopenharmony_ci		.handle_interrupt = gpy_handle_interrupt,
87962306a36Sopenharmony_ci		.set_wol	= gpy_set_wol,
88062306a36Sopenharmony_ci		.get_wol	= gpy_get_wol,
88162306a36Sopenharmony_ci		.set_loopback	= gpy_loopback,
88262306a36Sopenharmony_ci	},
88362306a36Sopenharmony_ci	{
88462306a36Sopenharmony_ci		PHY_ID_MATCH_MODEL(PHY_ID_GPY211C),
88562306a36Sopenharmony_ci		.name		= "Maxlinear Ethernet GPY211C",
88662306a36Sopenharmony_ci		.get_features	= genphy_c45_pma_read_abilities,
88762306a36Sopenharmony_ci		.config_init	= gpy_config_init,
88862306a36Sopenharmony_ci		.probe		= gpy_probe,
88962306a36Sopenharmony_ci		.suspend	= genphy_suspend,
89062306a36Sopenharmony_ci		.resume		= genphy_resume,
89162306a36Sopenharmony_ci		.config_aneg	= gpy_config_aneg,
89262306a36Sopenharmony_ci		.aneg_done	= genphy_c45_aneg_done,
89362306a36Sopenharmony_ci		.read_status	= gpy_read_status,
89462306a36Sopenharmony_ci		.config_intr	= gpy_config_intr,
89562306a36Sopenharmony_ci		.handle_interrupt = gpy_handle_interrupt,
89662306a36Sopenharmony_ci		.set_wol	= gpy_set_wol,
89762306a36Sopenharmony_ci		.get_wol	= gpy_get_wol,
89862306a36Sopenharmony_ci		.set_loopback	= gpy_loopback,
89962306a36Sopenharmony_ci	},
90062306a36Sopenharmony_ci	{
90162306a36Sopenharmony_ci		.phy_id		= PHY_ID_GPY212B,
90262306a36Sopenharmony_ci		.phy_id_mask	= PHY_ID_GPY21xB_MASK,
90362306a36Sopenharmony_ci		.name		= "Maxlinear Ethernet GPY212B",
90462306a36Sopenharmony_ci		.get_features	= genphy_c45_pma_read_abilities,
90562306a36Sopenharmony_ci		.config_init	= gpy_config_init,
90662306a36Sopenharmony_ci		.probe		= gpy_probe,
90762306a36Sopenharmony_ci		.suspend	= genphy_suspend,
90862306a36Sopenharmony_ci		.resume		= genphy_resume,
90962306a36Sopenharmony_ci		.config_aneg	= gpy_config_aneg,
91062306a36Sopenharmony_ci		.aneg_done	= genphy_c45_aneg_done,
91162306a36Sopenharmony_ci		.read_status	= gpy_read_status,
91262306a36Sopenharmony_ci		.config_intr	= gpy_config_intr,
91362306a36Sopenharmony_ci		.handle_interrupt = gpy_handle_interrupt,
91462306a36Sopenharmony_ci		.set_wol	= gpy_set_wol,
91562306a36Sopenharmony_ci		.get_wol	= gpy_get_wol,
91662306a36Sopenharmony_ci		.set_loopback	= gpy_loopback,
91762306a36Sopenharmony_ci	},
91862306a36Sopenharmony_ci	{
91962306a36Sopenharmony_ci		PHY_ID_MATCH_MODEL(PHY_ID_GPY212C),
92062306a36Sopenharmony_ci		.name		= "Maxlinear Ethernet GPY212C",
92162306a36Sopenharmony_ci		.get_features	= genphy_c45_pma_read_abilities,
92262306a36Sopenharmony_ci		.config_init	= gpy_config_init,
92362306a36Sopenharmony_ci		.probe		= gpy_probe,
92462306a36Sopenharmony_ci		.suspend	= genphy_suspend,
92562306a36Sopenharmony_ci		.resume		= genphy_resume,
92662306a36Sopenharmony_ci		.config_aneg	= gpy_config_aneg,
92762306a36Sopenharmony_ci		.aneg_done	= genphy_c45_aneg_done,
92862306a36Sopenharmony_ci		.read_status	= gpy_read_status,
92962306a36Sopenharmony_ci		.config_intr	= gpy_config_intr,
93062306a36Sopenharmony_ci		.handle_interrupt = gpy_handle_interrupt,
93162306a36Sopenharmony_ci		.set_wol	= gpy_set_wol,
93262306a36Sopenharmony_ci		.get_wol	= gpy_get_wol,
93362306a36Sopenharmony_ci		.set_loopback	= gpy_loopback,
93462306a36Sopenharmony_ci	},
93562306a36Sopenharmony_ci	{
93662306a36Sopenharmony_ci		.phy_id		= PHY_ID_GPY215B,
93762306a36Sopenharmony_ci		.phy_id_mask	= PHY_ID_GPYx15B_MASK,
93862306a36Sopenharmony_ci		.name		= "Maxlinear Ethernet GPY215B",
93962306a36Sopenharmony_ci		.get_features	= genphy_c45_pma_read_abilities,
94062306a36Sopenharmony_ci		.config_init	= gpy_config_init,
94162306a36Sopenharmony_ci		.probe		= gpy_probe,
94262306a36Sopenharmony_ci		.suspend	= genphy_suspend,
94362306a36Sopenharmony_ci		.resume		= genphy_resume,
94462306a36Sopenharmony_ci		.config_aneg	= gpy_config_aneg,
94562306a36Sopenharmony_ci		.aneg_done	= genphy_c45_aneg_done,
94662306a36Sopenharmony_ci		.read_status	= gpy_read_status,
94762306a36Sopenharmony_ci		.config_intr	= gpy_config_intr,
94862306a36Sopenharmony_ci		.handle_interrupt = gpy_handle_interrupt,
94962306a36Sopenharmony_ci		.set_wol	= gpy_set_wol,
95062306a36Sopenharmony_ci		.get_wol	= gpy_get_wol,
95162306a36Sopenharmony_ci		.set_loopback	= gpy_loopback,
95262306a36Sopenharmony_ci	},
95362306a36Sopenharmony_ci	{
95462306a36Sopenharmony_ci		PHY_ID_MATCH_MODEL(PHY_ID_GPY215C),
95562306a36Sopenharmony_ci		.name		= "Maxlinear Ethernet GPY215C",
95662306a36Sopenharmony_ci		.get_features	= genphy_c45_pma_read_abilities,
95762306a36Sopenharmony_ci		.config_init	= gpy_config_init,
95862306a36Sopenharmony_ci		.probe		= gpy_probe,
95962306a36Sopenharmony_ci		.suspend	= genphy_suspend,
96062306a36Sopenharmony_ci		.resume		= genphy_resume,
96162306a36Sopenharmony_ci		.config_aneg	= gpy_config_aneg,
96262306a36Sopenharmony_ci		.aneg_done	= genphy_c45_aneg_done,
96362306a36Sopenharmony_ci		.read_status	= gpy_read_status,
96462306a36Sopenharmony_ci		.config_intr	= gpy_config_intr,
96562306a36Sopenharmony_ci		.handle_interrupt = gpy_handle_interrupt,
96662306a36Sopenharmony_ci		.set_wol	= gpy_set_wol,
96762306a36Sopenharmony_ci		.get_wol	= gpy_get_wol,
96862306a36Sopenharmony_ci		.set_loopback	= gpy_loopback,
96962306a36Sopenharmony_ci	},
97062306a36Sopenharmony_ci	{
97162306a36Sopenharmony_ci		PHY_ID_MATCH_MODEL(PHY_ID_GPY241B),
97262306a36Sopenharmony_ci		.name		= "Maxlinear Ethernet GPY241B",
97362306a36Sopenharmony_ci		.get_features	= genphy_c45_pma_read_abilities,
97462306a36Sopenharmony_ci		.config_init	= gpy_config_init,
97562306a36Sopenharmony_ci		.probe		= gpy_probe,
97662306a36Sopenharmony_ci		.suspend	= genphy_suspend,
97762306a36Sopenharmony_ci		.resume		= genphy_resume,
97862306a36Sopenharmony_ci		.config_aneg	= gpy_config_aneg,
97962306a36Sopenharmony_ci		.aneg_done	= genphy_c45_aneg_done,
98062306a36Sopenharmony_ci		.read_status	= gpy_read_status,
98162306a36Sopenharmony_ci		.config_intr	= gpy_config_intr,
98262306a36Sopenharmony_ci		.handle_interrupt = gpy_handle_interrupt,
98362306a36Sopenharmony_ci		.set_wol	= gpy_set_wol,
98462306a36Sopenharmony_ci		.get_wol	= gpy_get_wol,
98562306a36Sopenharmony_ci		.set_loopback	= gpy_loopback,
98662306a36Sopenharmony_ci	},
98762306a36Sopenharmony_ci	{
98862306a36Sopenharmony_ci		PHY_ID_MATCH_MODEL(PHY_ID_GPY241BM),
98962306a36Sopenharmony_ci		.name		= "Maxlinear Ethernet GPY241BM",
99062306a36Sopenharmony_ci		.get_features	= genphy_c45_pma_read_abilities,
99162306a36Sopenharmony_ci		.config_init	= gpy_config_init,
99262306a36Sopenharmony_ci		.probe		= gpy_probe,
99362306a36Sopenharmony_ci		.suspend	= genphy_suspend,
99462306a36Sopenharmony_ci		.resume		= genphy_resume,
99562306a36Sopenharmony_ci		.config_aneg	= gpy_config_aneg,
99662306a36Sopenharmony_ci		.aneg_done	= genphy_c45_aneg_done,
99762306a36Sopenharmony_ci		.read_status	= gpy_read_status,
99862306a36Sopenharmony_ci		.config_intr	= gpy_config_intr,
99962306a36Sopenharmony_ci		.handle_interrupt = gpy_handle_interrupt,
100062306a36Sopenharmony_ci		.set_wol	= gpy_set_wol,
100162306a36Sopenharmony_ci		.get_wol	= gpy_get_wol,
100262306a36Sopenharmony_ci		.set_loopback	= gpy_loopback,
100362306a36Sopenharmony_ci	},
100462306a36Sopenharmony_ci	{
100562306a36Sopenharmony_ci		PHY_ID_MATCH_MODEL(PHY_ID_GPY245B),
100662306a36Sopenharmony_ci		.name		= "Maxlinear Ethernet GPY245B",
100762306a36Sopenharmony_ci		.get_features	= genphy_c45_pma_read_abilities,
100862306a36Sopenharmony_ci		.config_init	= gpy_config_init,
100962306a36Sopenharmony_ci		.probe		= gpy_probe,
101062306a36Sopenharmony_ci		.suspend	= genphy_suspend,
101162306a36Sopenharmony_ci		.resume		= genphy_resume,
101262306a36Sopenharmony_ci		.config_aneg	= gpy_config_aneg,
101362306a36Sopenharmony_ci		.aneg_done	= genphy_c45_aneg_done,
101462306a36Sopenharmony_ci		.read_status	= gpy_read_status,
101562306a36Sopenharmony_ci		.config_intr	= gpy_config_intr,
101662306a36Sopenharmony_ci		.handle_interrupt = gpy_handle_interrupt,
101762306a36Sopenharmony_ci		.set_wol	= gpy_set_wol,
101862306a36Sopenharmony_ci		.get_wol	= gpy_get_wol,
101962306a36Sopenharmony_ci		.set_loopback	= gpy_loopback,
102062306a36Sopenharmony_ci	},
102162306a36Sopenharmony_ci};
102262306a36Sopenharmony_cimodule_phy_driver(gpy_drivers);
102362306a36Sopenharmony_ci
102462306a36Sopenharmony_cistatic struct mdio_device_id __maybe_unused gpy_tbl[] = {
102562306a36Sopenharmony_ci	{PHY_ID_MATCH_MODEL(PHY_ID_GPY2xx)},
102662306a36Sopenharmony_ci	{PHY_ID_GPY115B, PHY_ID_GPYx15B_MASK},
102762306a36Sopenharmony_ci	{PHY_ID_MATCH_MODEL(PHY_ID_GPY115C)},
102862306a36Sopenharmony_ci	{PHY_ID_GPY211B, PHY_ID_GPY21xB_MASK},
102962306a36Sopenharmony_ci	{PHY_ID_MATCH_MODEL(PHY_ID_GPY211C)},
103062306a36Sopenharmony_ci	{PHY_ID_GPY212B, PHY_ID_GPY21xB_MASK},
103162306a36Sopenharmony_ci	{PHY_ID_MATCH_MODEL(PHY_ID_GPY212C)},
103262306a36Sopenharmony_ci	{PHY_ID_GPY215B, PHY_ID_GPYx15B_MASK},
103362306a36Sopenharmony_ci	{PHY_ID_MATCH_MODEL(PHY_ID_GPY215C)},
103462306a36Sopenharmony_ci	{PHY_ID_MATCH_MODEL(PHY_ID_GPY241B)},
103562306a36Sopenharmony_ci	{PHY_ID_MATCH_MODEL(PHY_ID_GPY241BM)},
103662306a36Sopenharmony_ci	{PHY_ID_MATCH_MODEL(PHY_ID_GPY245B)},
103762306a36Sopenharmony_ci	{ }
103862306a36Sopenharmony_ci};
103962306a36Sopenharmony_ciMODULE_DEVICE_TABLE(mdio, gpy_tbl);
104062306a36Sopenharmony_ci
104162306a36Sopenharmony_ciMODULE_DESCRIPTION("Maxlinear Ethernet GPY Driver");
104262306a36Sopenharmony_ciMODULE_AUTHOR("Xu Liang");
104362306a36Sopenharmony_ciMODULE_LICENSE("GPL");
1044