18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Marvell 10G 88x3310 PHY driver
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Based upon the ID registers, this PHY appears to be a mixture of IPs
68c2ecf20Sopenharmony_ci * from two different companies.
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * There appears to be several different data paths through the PHY which
98c2ecf20Sopenharmony_ci * are automatically managed by the PHY.  The following has been determined
108c2ecf20Sopenharmony_ci * via observation and experimentation for a setup using single-lane Serdes:
118c2ecf20Sopenharmony_ci *
128c2ecf20Sopenharmony_ci *       SGMII PHYXS -- BASE-T PCS -- 10G PMA -- AN -- Copper (for <= 1G)
138c2ecf20Sopenharmony_ci *  10GBASE-KR PHYXS -- BASE-T PCS -- 10G PMA -- AN -- Copper (for 10G)
148c2ecf20Sopenharmony_ci *  10GBASE-KR PHYXS -- BASE-R PCS -- Fiber
158c2ecf20Sopenharmony_ci *
168c2ecf20Sopenharmony_ci * With XAUI, observation shows:
178c2ecf20Sopenharmony_ci *
188c2ecf20Sopenharmony_ci *        XAUI PHYXS -- <appropriate PCS as above>
198c2ecf20Sopenharmony_ci *
208c2ecf20Sopenharmony_ci * and no switching of the host interface mode occurs.
218c2ecf20Sopenharmony_ci *
228c2ecf20Sopenharmony_ci * If both the fiber and copper ports are connected, the first to gain
238c2ecf20Sopenharmony_ci * link takes priority and the other port is completely locked out.
248c2ecf20Sopenharmony_ci */
258c2ecf20Sopenharmony_ci#include <linux/ctype.h>
268c2ecf20Sopenharmony_ci#include <linux/delay.h>
278c2ecf20Sopenharmony_ci#include <linux/hwmon.h>
288c2ecf20Sopenharmony_ci#include <linux/marvell_phy.h>
298c2ecf20Sopenharmony_ci#include <linux/phy.h>
308c2ecf20Sopenharmony_ci#include <linux/sfp.h>
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci#define MV_PHY_ALASKA_NBT_QUIRK_MASK	0xfffffffe
338c2ecf20Sopenharmony_ci#define MV_PHY_ALASKA_NBT_QUIRK_REV	(MARVELL_PHY_ID_88X3310 | 0xa)
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_cienum {
368c2ecf20Sopenharmony_ci	MV_PMA_FW_VER0		= 0xc011,
378c2ecf20Sopenharmony_ci	MV_PMA_FW_VER1		= 0xc012,
388c2ecf20Sopenharmony_ci	MV_PMA_BOOT		= 0xc050,
398c2ecf20Sopenharmony_ci	MV_PMA_BOOT_FATAL	= BIT(0),
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci	MV_PCS_BASE_T		= 0x0000,
428c2ecf20Sopenharmony_ci	MV_PCS_BASE_R		= 0x1000,
438c2ecf20Sopenharmony_ci	MV_PCS_1000BASEX	= 0x2000,
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci	MV_PCS_CSCR1		= 0x8000,
468c2ecf20Sopenharmony_ci	MV_PCS_CSCR1_ED_MASK	= 0x0300,
478c2ecf20Sopenharmony_ci	MV_PCS_CSCR1_ED_OFF	= 0x0000,
488c2ecf20Sopenharmony_ci	MV_PCS_CSCR1_ED_RX	= 0x0200,
498c2ecf20Sopenharmony_ci	MV_PCS_CSCR1_ED_NLP	= 0x0300,
508c2ecf20Sopenharmony_ci	MV_PCS_CSCR1_MDIX_MASK	= 0x0060,
518c2ecf20Sopenharmony_ci	MV_PCS_CSCR1_MDIX_MDI	= 0x0000,
528c2ecf20Sopenharmony_ci	MV_PCS_CSCR1_MDIX_MDIX	= 0x0020,
538c2ecf20Sopenharmony_ci	MV_PCS_CSCR1_MDIX_AUTO	= 0x0060,
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci	MV_PCS_CSSR1		= 0x8008,
568c2ecf20Sopenharmony_ci	MV_PCS_CSSR1_SPD1_MASK	= 0xc000,
578c2ecf20Sopenharmony_ci	MV_PCS_CSSR1_SPD1_SPD2	= 0xc000,
588c2ecf20Sopenharmony_ci	MV_PCS_CSSR1_SPD1_1000	= 0x8000,
598c2ecf20Sopenharmony_ci	MV_PCS_CSSR1_SPD1_100	= 0x4000,
608c2ecf20Sopenharmony_ci	MV_PCS_CSSR1_SPD1_10	= 0x0000,
618c2ecf20Sopenharmony_ci	MV_PCS_CSSR1_DUPLEX_FULL= BIT(13),
628c2ecf20Sopenharmony_ci	MV_PCS_CSSR1_RESOLVED	= BIT(11),
638c2ecf20Sopenharmony_ci	MV_PCS_CSSR1_MDIX	= BIT(6),
648c2ecf20Sopenharmony_ci	MV_PCS_CSSR1_SPD2_MASK	= 0x000c,
658c2ecf20Sopenharmony_ci	MV_PCS_CSSR1_SPD2_5000	= 0x0008,
668c2ecf20Sopenharmony_ci	MV_PCS_CSSR1_SPD2_2500	= 0x0004,
678c2ecf20Sopenharmony_ci	MV_PCS_CSSR1_SPD2_10000	= 0x0000,
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	/* Temperature read register (88E2110 only) */
708c2ecf20Sopenharmony_ci	MV_PCS_TEMP		= 0x8042,
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci	/* These registers appear at 0x800X and 0xa00X - the 0xa00X control
738c2ecf20Sopenharmony_ci	 * registers appear to set themselves to the 0x800X when AN is
748c2ecf20Sopenharmony_ci	 * restarted, but status registers appear readable from either.
758c2ecf20Sopenharmony_ci	 */
768c2ecf20Sopenharmony_ci	MV_AN_CTRL1000		= 0x8000, /* 1000base-T control register */
778c2ecf20Sopenharmony_ci	MV_AN_STAT1000		= 0x8001, /* 1000base-T status register */
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci	/* Vendor2 MMD registers */
808c2ecf20Sopenharmony_ci	MV_V2_PORT_CTRL		= 0xf001,
818c2ecf20Sopenharmony_ci	MV_V2_PORT_CTRL_SWRST	= BIT(15),
828c2ecf20Sopenharmony_ci	MV_V2_PORT_CTRL_PWRDOWN = BIT(11),
838c2ecf20Sopenharmony_ci	MV_V2_PORT_MAC_TYPE_MASK = 0x7,
848c2ecf20Sopenharmony_ci	MV_V2_PORT_MAC_TYPE_RATE_MATCH = 0x6,
858c2ecf20Sopenharmony_ci	/* Temperature control/read registers (88X3310 only) */
868c2ecf20Sopenharmony_ci	MV_V2_TEMP_CTRL		= 0xf08a,
878c2ecf20Sopenharmony_ci	MV_V2_TEMP_CTRL_MASK	= 0xc000,
888c2ecf20Sopenharmony_ci	MV_V2_TEMP_CTRL_SAMPLE	= 0x0000,
898c2ecf20Sopenharmony_ci	MV_V2_TEMP_CTRL_DISABLE	= 0xc000,
908c2ecf20Sopenharmony_ci	MV_V2_TEMP		= 0xf08c,
918c2ecf20Sopenharmony_ci	MV_V2_TEMP_UNKNOWN	= 0x9600, /* unknown function */
928c2ecf20Sopenharmony_ci};
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_cistruct mv3310_priv {
958c2ecf20Sopenharmony_ci	u32 firmware_ver;
968c2ecf20Sopenharmony_ci	bool rate_match;
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	struct device *hwmon_dev;
998c2ecf20Sopenharmony_ci	char *hwmon_name;
1008c2ecf20Sopenharmony_ci};
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci#ifdef CONFIG_HWMON
1038c2ecf20Sopenharmony_cistatic umode_t mv3310_hwmon_is_visible(const void *data,
1048c2ecf20Sopenharmony_ci				       enum hwmon_sensor_types type,
1058c2ecf20Sopenharmony_ci				       u32 attr, int channel)
1068c2ecf20Sopenharmony_ci{
1078c2ecf20Sopenharmony_ci	if (type == hwmon_chip && attr == hwmon_chip_update_interval)
1088c2ecf20Sopenharmony_ci		return 0444;
1098c2ecf20Sopenharmony_ci	if (type == hwmon_temp && attr == hwmon_temp_input)
1108c2ecf20Sopenharmony_ci		return 0444;
1118c2ecf20Sopenharmony_ci	return 0;
1128c2ecf20Sopenharmony_ci}
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_cistatic int mv3310_hwmon_read_temp_reg(struct phy_device *phydev)
1158c2ecf20Sopenharmony_ci{
1168c2ecf20Sopenharmony_ci	return phy_read_mmd(phydev, MDIO_MMD_VEND2, MV_V2_TEMP);
1178c2ecf20Sopenharmony_ci}
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_cistatic int mv2110_hwmon_read_temp_reg(struct phy_device *phydev)
1208c2ecf20Sopenharmony_ci{
1218c2ecf20Sopenharmony_ci	return phy_read_mmd(phydev, MDIO_MMD_PCS, MV_PCS_TEMP);
1228c2ecf20Sopenharmony_ci}
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_cistatic int mv10g_hwmon_read_temp_reg(struct phy_device *phydev)
1258c2ecf20Sopenharmony_ci{
1268c2ecf20Sopenharmony_ci	if (phydev->drv->phy_id == MARVELL_PHY_ID_88X3310)
1278c2ecf20Sopenharmony_ci		return mv3310_hwmon_read_temp_reg(phydev);
1288c2ecf20Sopenharmony_ci	else /* MARVELL_PHY_ID_88E2110 */
1298c2ecf20Sopenharmony_ci		return mv2110_hwmon_read_temp_reg(phydev);
1308c2ecf20Sopenharmony_ci}
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_cistatic int mv3310_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
1338c2ecf20Sopenharmony_ci			     u32 attr, int channel, long *value)
1348c2ecf20Sopenharmony_ci{
1358c2ecf20Sopenharmony_ci	struct phy_device *phydev = dev_get_drvdata(dev);
1368c2ecf20Sopenharmony_ci	int temp;
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci	if (type == hwmon_chip && attr == hwmon_chip_update_interval) {
1398c2ecf20Sopenharmony_ci		*value = MSEC_PER_SEC;
1408c2ecf20Sopenharmony_ci		return 0;
1418c2ecf20Sopenharmony_ci	}
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci	if (type == hwmon_temp && attr == hwmon_temp_input) {
1448c2ecf20Sopenharmony_ci		temp = mv10g_hwmon_read_temp_reg(phydev);
1458c2ecf20Sopenharmony_ci		if (temp < 0)
1468c2ecf20Sopenharmony_ci			return temp;
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci		*value = ((temp & 0xff) - 75) * 1000;
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci		return 0;
1518c2ecf20Sopenharmony_ci	}
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci	return -EOPNOTSUPP;
1548c2ecf20Sopenharmony_ci}
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_cistatic const struct hwmon_ops mv3310_hwmon_ops = {
1578c2ecf20Sopenharmony_ci	.is_visible = mv3310_hwmon_is_visible,
1588c2ecf20Sopenharmony_ci	.read = mv3310_hwmon_read,
1598c2ecf20Sopenharmony_ci};
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_cistatic u32 mv3310_hwmon_chip_config[] = {
1628c2ecf20Sopenharmony_ci	HWMON_C_REGISTER_TZ | HWMON_C_UPDATE_INTERVAL,
1638c2ecf20Sopenharmony_ci	0,
1648c2ecf20Sopenharmony_ci};
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_cistatic const struct hwmon_channel_info mv3310_hwmon_chip = {
1678c2ecf20Sopenharmony_ci	.type = hwmon_chip,
1688c2ecf20Sopenharmony_ci	.config = mv3310_hwmon_chip_config,
1698c2ecf20Sopenharmony_ci};
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_cistatic u32 mv3310_hwmon_temp_config[] = {
1728c2ecf20Sopenharmony_ci	HWMON_T_INPUT,
1738c2ecf20Sopenharmony_ci	0,
1748c2ecf20Sopenharmony_ci};
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_cistatic const struct hwmon_channel_info mv3310_hwmon_temp = {
1778c2ecf20Sopenharmony_ci	.type = hwmon_temp,
1788c2ecf20Sopenharmony_ci	.config = mv3310_hwmon_temp_config,
1798c2ecf20Sopenharmony_ci};
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_cistatic const struct hwmon_channel_info *mv3310_hwmon_info[] = {
1828c2ecf20Sopenharmony_ci	&mv3310_hwmon_chip,
1838c2ecf20Sopenharmony_ci	&mv3310_hwmon_temp,
1848c2ecf20Sopenharmony_ci	NULL,
1858c2ecf20Sopenharmony_ci};
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_cistatic const struct hwmon_chip_info mv3310_hwmon_chip_info = {
1888c2ecf20Sopenharmony_ci	.ops = &mv3310_hwmon_ops,
1898c2ecf20Sopenharmony_ci	.info = mv3310_hwmon_info,
1908c2ecf20Sopenharmony_ci};
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_cistatic int mv3310_hwmon_config(struct phy_device *phydev, bool enable)
1938c2ecf20Sopenharmony_ci{
1948c2ecf20Sopenharmony_ci	u16 val;
1958c2ecf20Sopenharmony_ci	int ret;
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci	if (phydev->drv->phy_id != MARVELL_PHY_ID_88X3310)
1988c2ecf20Sopenharmony_ci		return 0;
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci	ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, MV_V2_TEMP,
2018c2ecf20Sopenharmony_ci			    MV_V2_TEMP_UNKNOWN);
2028c2ecf20Sopenharmony_ci	if (ret < 0)
2038c2ecf20Sopenharmony_ci		return ret;
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	val = enable ? MV_V2_TEMP_CTRL_SAMPLE : MV_V2_TEMP_CTRL_DISABLE;
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci	return phy_modify_mmd(phydev, MDIO_MMD_VEND2, MV_V2_TEMP_CTRL,
2088c2ecf20Sopenharmony_ci			      MV_V2_TEMP_CTRL_MASK, val);
2098c2ecf20Sopenharmony_ci}
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_cistatic int mv3310_hwmon_probe(struct phy_device *phydev)
2128c2ecf20Sopenharmony_ci{
2138c2ecf20Sopenharmony_ci	struct device *dev = &phydev->mdio.dev;
2148c2ecf20Sopenharmony_ci	struct mv3310_priv *priv = dev_get_drvdata(&phydev->mdio.dev);
2158c2ecf20Sopenharmony_ci	int i, j, ret;
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci	priv->hwmon_name = devm_kstrdup(dev, dev_name(dev), GFP_KERNEL);
2188c2ecf20Sopenharmony_ci	if (!priv->hwmon_name)
2198c2ecf20Sopenharmony_ci		return -ENODEV;
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci	for (i = j = 0; priv->hwmon_name[i]; i++) {
2228c2ecf20Sopenharmony_ci		if (isalnum(priv->hwmon_name[i])) {
2238c2ecf20Sopenharmony_ci			if (i != j)
2248c2ecf20Sopenharmony_ci				priv->hwmon_name[j] = priv->hwmon_name[i];
2258c2ecf20Sopenharmony_ci			j++;
2268c2ecf20Sopenharmony_ci		}
2278c2ecf20Sopenharmony_ci	}
2288c2ecf20Sopenharmony_ci	priv->hwmon_name[j] = '\0';
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci	ret = mv3310_hwmon_config(phydev, true);
2318c2ecf20Sopenharmony_ci	if (ret)
2328c2ecf20Sopenharmony_ci		return ret;
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci	priv->hwmon_dev = devm_hwmon_device_register_with_info(dev,
2358c2ecf20Sopenharmony_ci				priv->hwmon_name, phydev,
2368c2ecf20Sopenharmony_ci				&mv3310_hwmon_chip_info, NULL);
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci	return PTR_ERR_OR_ZERO(priv->hwmon_dev);
2398c2ecf20Sopenharmony_ci}
2408c2ecf20Sopenharmony_ci#else
2418c2ecf20Sopenharmony_cistatic inline int mv3310_hwmon_config(struct phy_device *phydev, bool enable)
2428c2ecf20Sopenharmony_ci{
2438c2ecf20Sopenharmony_ci	return 0;
2448c2ecf20Sopenharmony_ci}
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_cistatic int mv3310_hwmon_probe(struct phy_device *phydev)
2478c2ecf20Sopenharmony_ci{
2488c2ecf20Sopenharmony_ci	return 0;
2498c2ecf20Sopenharmony_ci}
2508c2ecf20Sopenharmony_ci#endif
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_cistatic int mv3310_power_down(struct phy_device *phydev)
2538c2ecf20Sopenharmony_ci{
2548c2ecf20Sopenharmony_ci	return phy_set_bits_mmd(phydev, MDIO_MMD_VEND2, MV_V2_PORT_CTRL,
2558c2ecf20Sopenharmony_ci				MV_V2_PORT_CTRL_PWRDOWN);
2568c2ecf20Sopenharmony_ci}
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_cistatic int mv3310_power_up(struct phy_device *phydev)
2598c2ecf20Sopenharmony_ci{
2608c2ecf20Sopenharmony_ci	struct mv3310_priv *priv = dev_get_drvdata(&phydev->mdio.dev);
2618c2ecf20Sopenharmony_ci	int ret;
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci	ret = phy_clear_bits_mmd(phydev, MDIO_MMD_VEND2, MV_V2_PORT_CTRL,
2648c2ecf20Sopenharmony_ci				 MV_V2_PORT_CTRL_PWRDOWN);
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci	/* Sometimes, the power down bit doesn't clear immediately, and
2678c2ecf20Sopenharmony_ci	 * a read of this register causes the bit not to clear. Delay
2688c2ecf20Sopenharmony_ci	 * 100us to allow the PHY to come out of power down mode before
2698c2ecf20Sopenharmony_ci	 * the next access.
2708c2ecf20Sopenharmony_ci	 */
2718c2ecf20Sopenharmony_ci	udelay(100);
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci	if (phydev->drv->phy_id != MARVELL_PHY_ID_88X3310 ||
2748c2ecf20Sopenharmony_ci	    priv->firmware_ver < 0x00030000)
2758c2ecf20Sopenharmony_ci		return ret;
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci	return phy_set_bits_mmd(phydev, MDIO_MMD_VEND2, MV_V2_PORT_CTRL,
2788c2ecf20Sopenharmony_ci				MV_V2_PORT_CTRL_SWRST);
2798c2ecf20Sopenharmony_ci}
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_cistatic int mv3310_reset(struct phy_device *phydev, u32 unit)
2828c2ecf20Sopenharmony_ci{
2838c2ecf20Sopenharmony_ci	int val, err;
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ci	err = phy_modify_mmd(phydev, MDIO_MMD_PCS, unit + MDIO_CTRL1,
2868c2ecf20Sopenharmony_ci			     MDIO_CTRL1_RESET, MDIO_CTRL1_RESET);
2878c2ecf20Sopenharmony_ci	if (err < 0)
2888c2ecf20Sopenharmony_ci		return err;
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci	return phy_read_mmd_poll_timeout(phydev, MDIO_MMD_PCS,
2918c2ecf20Sopenharmony_ci					 unit + MDIO_CTRL1, val,
2928c2ecf20Sopenharmony_ci					 !(val & MDIO_CTRL1_RESET),
2938c2ecf20Sopenharmony_ci					 5000, 100000, true);
2948c2ecf20Sopenharmony_ci}
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_cistatic int mv3310_get_edpd(struct phy_device *phydev, u16 *edpd)
2978c2ecf20Sopenharmony_ci{
2988c2ecf20Sopenharmony_ci	int val;
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ci	val = phy_read_mmd(phydev, MDIO_MMD_PCS, MV_PCS_CSCR1);
3018c2ecf20Sopenharmony_ci	if (val < 0)
3028c2ecf20Sopenharmony_ci		return val;
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci	switch (val & MV_PCS_CSCR1_ED_MASK) {
3058c2ecf20Sopenharmony_ci	case MV_PCS_CSCR1_ED_NLP:
3068c2ecf20Sopenharmony_ci		*edpd = 1000;
3078c2ecf20Sopenharmony_ci		break;
3088c2ecf20Sopenharmony_ci	case MV_PCS_CSCR1_ED_RX:
3098c2ecf20Sopenharmony_ci		*edpd = ETHTOOL_PHY_EDPD_NO_TX;
3108c2ecf20Sopenharmony_ci		break;
3118c2ecf20Sopenharmony_ci	default:
3128c2ecf20Sopenharmony_ci		*edpd = ETHTOOL_PHY_EDPD_DISABLE;
3138c2ecf20Sopenharmony_ci		break;
3148c2ecf20Sopenharmony_ci	}
3158c2ecf20Sopenharmony_ci	return 0;
3168c2ecf20Sopenharmony_ci}
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_cistatic int mv3310_set_edpd(struct phy_device *phydev, u16 edpd)
3198c2ecf20Sopenharmony_ci{
3208c2ecf20Sopenharmony_ci	u16 val;
3218c2ecf20Sopenharmony_ci	int err;
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_ci	switch (edpd) {
3248c2ecf20Sopenharmony_ci	case 1000:
3258c2ecf20Sopenharmony_ci	case ETHTOOL_PHY_EDPD_DFLT_TX_MSECS:
3268c2ecf20Sopenharmony_ci		val = MV_PCS_CSCR1_ED_NLP;
3278c2ecf20Sopenharmony_ci		break;
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci	case ETHTOOL_PHY_EDPD_NO_TX:
3308c2ecf20Sopenharmony_ci		val = MV_PCS_CSCR1_ED_RX;
3318c2ecf20Sopenharmony_ci		break;
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_ci	case ETHTOOL_PHY_EDPD_DISABLE:
3348c2ecf20Sopenharmony_ci		val = MV_PCS_CSCR1_ED_OFF;
3358c2ecf20Sopenharmony_ci		break;
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ci	default:
3388c2ecf20Sopenharmony_ci		return -EINVAL;
3398c2ecf20Sopenharmony_ci	}
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_ci	err = phy_modify_mmd_changed(phydev, MDIO_MMD_PCS, MV_PCS_CSCR1,
3428c2ecf20Sopenharmony_ci				     MV_PCS_CSCR1_ED_MASK, val);
3438c2ecf20Sopenharmony_ci	if (err > 0)
3448c2ecf20Sopenharmony_ci		err = mv3310_reset(phydev, MV_PCS_BASE_T);
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_ci	return err;
3478c2ecf20Sopenharmony_ci}
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_cistatic int mv3310_sfp_insert(void *upstream, const struct sfp_eeprom_id *id)
3508c2ecf20Sopenharmony_ci{
3518c2ecf20Sopenharmony_ci	struct phy_device *phydev = upstream;
3528c2ecf20Sopenharmony_ci	__ETHTOOL_DECLARE_LINK_MODE_MASK(support) = { 0, };
3538c2ecf20Sopenharmony_ci	phy_interface_t iface;
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ci	sfp_parse_support(phydev->sfp_bus, id, support);
3568c2ecf20Sopenharmony_ci	iface = sfp_select_interface(phydev->sfp_bus, support);
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ci	if (iface != PHY_INTERFACE_MODE_10GBASER) {
3598c2ecf20Sopenharmony_ci		dev_err(&phydev->mdio.dev, "incompatible SFP module inserted\n");
3608c2ecf20Sopenharmony_ci		return -EINVAL;
3618c2ecf20Sopenharmony_ci	}
3628c2ecf20Sopenharmony_ci	return 0;
3638c2ecf20Sopenharmony_ci}
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_cistatic const struct sfp_upstream_ops mv3310_sfp_ops = {
3668c2ecf20Sopenharmony_ci	.attach = phy_sfp_attach,
3678c2ecf20Sopenharmony_ci	.detach = phy_sfp_detach,
3688c2ecf20Sopenharmony_ci	.module_insert = mv3310_sfp_insert,
3698c2ecf20Sopenharmony_ci};
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_cistatic int mv3310_probe(struct phy_device *phydev)
3728c2ecf20Sopenharmony_ci{
3738c2ecf20Sopenharmony_ci	struct mv3310_priv *priv;
3748c2ecf20Sopenharmony_ci	u32 mmd_mask = MDIO_DEVS_PMAPMD | MDIO_DEVS_AN;
3758c2ecf20Sopenharmony_ci	int ret;
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_ci	if (!phydev->is_c45 ||
3788c2ecf20Sopenharmony_ci	    (phydev->c45_ids.devices_in_package & mmd_mask) != mmd_mask)
3798c2ecf20Sopenharmony_ci		return -ENODEV;
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci	ret = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MV_PMA_BOOT);
3828c2ecf20Sopenharmony_ci	if (ret < 0)
3838c2ecf20Sopenharmony_ci		return ret;
3848c2ecf20Sopenharmony_ci
3858c2ecf20Sopenharmony_ci	if (ret & MV_PMA_BOOT_FATAL) {
3868c2ecf20Sopenharmony_ci		dev_warn(&phydev->mdio.dev,
3878c2ecf20Sopenharmony_ci			 "PHY failed to boot firmware, status=%04x\n", ret);
3888c2ecf20Sopenharmony_ci		return -ENODEV;
3898c2ecf20Sopenharmony_ci	}
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_ci	priv = devm_kzalloc(&phydev->mdio.dev, sizeof(*priv), GFP_KERNEL);
3928c2ecf20Sopenharmony_ci	if (!priv)
3938c2ecf20Sopenharmony_ci		return -ENOMEM;
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_ci	dev_set_drvdata(&phydev->mdio.dev, priv);
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_ci	ret = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MV_PMA_FW_VER0);
3988c2ecf20Sopenharmony_ci	if (ret < 0)
3998c2ecf20Sopenharmony_ci		return ret;
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_ci	priv->firmware_ver = ret << 16;
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_ci	ret = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MV_PMA_FW_VER1);
4048c2ecf20Sopenharmony_ci	if (ret < 0)
4058c2ecf20Sopenharmony_ci		return ret;
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_ci	priv->firmware_ver |= ret;
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_ci	phydev_info(phydev, "Firmware version %u.%u.%u.%u\n",
4108c2ecf20Sopenharmony_ci		    priv->firmware_ver >> 24, (priv->firmware_ver >> 16) & 255,
4118c2ecf20Sopenharmony_ci		    (priv->firmware_ver >> 8) & 255, priv->firmware_ver & 255);
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_ci	/* Powering down the port when not in use saves about 600mW */
4148c2ecf20Sopenharmony_ci	ret = mv3310_power_down(phydev);
4158c2ecf20Sopenharmony_ci	if (ret)
4168c2ecf20Sopenharmony_ci		return ret;
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_ci	ret = mv3310_hwmon_probe(phydev);
4198c2ecf20Sopenharmony_ci	if (ret)
4208c2ecf20Sopenharmony_ci		return ret;
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_ci	return phy_sfp_probe(phydev, &mv3310_sfp_ops);
4238c2ecf20Sopenharmony_ci}
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_cistatic void mv3310_remove(struct phy_device *phydev)
4268c2ecf20Sopenharmony_ci{
4278c2ecf20Sopenharmony_ci	mv3310_hwmon_config(phydev, false);
4288c2ecf20Sopenharmony_ci}
4298c2ecf20Sopenharmony_ci
4308c2ecf20Sopenharmony_cistatic int mv3310_suspend(struct phy_device *phydev)
4318c2ecf20Sopenharmony_ci{
4328c2ecf20Sopenharmony_ci	return mv3310_power_down(phydev);
4338c2ecf20Sopenharmony_ci}
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_cistatic int mv3310_resume(struct phy_device *phydev)
4368c2ecf20Sopenharmony_ci{
4378c2ecf20Sopenharmony_ci	int ret;
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_ci	ret = mv3310_power_up(phydev);
4408c2ecf20Sopenharmony_ci	if (ret)
4418c2ecf20Sopenharmony_ci		return ret;
4428c2ecf20Sopenharmony_ci
4438c2ecf20Sopenharmony_ci	return mv3310_hwmon_config(phydev, true);
4448c2ecf20Sopenharmony_ci}
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_ci/* Some PHYs in the Alaska family such as the 88X3310 and the 88E2010
4478c2ecf20Sopenharmony_ci * don't set bit 14 in PMA Extended Abilities (1.11), although they do
4488c2ecf20Sopenharmony_ci * support 2.5GBASET and 5GBASET. For these models, we can still read their
4498c2ecf20Sopenharmony_ci * 2.5G/5G extended abilities register (1.21). We detect these models based on
4508c2ecf20Sopenharmony_ci * the PMA device identifier, with a mask matching models known to have this
4518c2ecf20Sopenharmony_ci * issue
4528c2ecf20Sopenharmony_ci */
4538c2ecf20Sopenharmony_cistatic bool mv3310_has_pma_ngbaset_quirk(struct phy_device *phydev)
4548c2ecf20Sopenharmony_ci{
4558c2ecf20Sopenharmony_ci	if (!(phydev->c45_ids.devices_in_package & MDIO_DEVS_PMAPMD))
4568c2ecf20Sopenharmony_ci		return false;
4578c2ecf20Sopenharmony_ci
4588c2ecf20Sopenharmony_ci	/* Only some revisions of the 88X3310 family PMA seem to be impacted */
4598c2ecf20Sopenharmony_ci	return (phydev->c45_ids.device_ids[MDIO_MMD_PMAPMD] &
4608c2ecf20Sopenharmony_ci		MV_PHY_ALASKA_NBT_QUIRK_MASK) == MV_PHY_ALASKA_NBT_QUIRK_REV;
4618c2ecf20Sopenharmony_ci}
4628c2ecf20Sopenharmony_ci
4638c2ecf20Sopenharmony_cistatic int mv3310_config_init(struct phy_device *phydev)
4648c2ecf20Sopenharmony_ci{
4658c2ecf20Sopenharmony_ci	struct mv3310_priv *priv = dev_get_drvdata(&phydev->mdio.dev);
4668c2ecf20Sopenharmony_ci	int err;
4678c2ecf20Sopenharmony_ci	int val;
4688c2ecf20Sopenharmony_ci
4698c2ecf20Sopenharmony_ci	/* Check that the PHY interface type is compatible */
4708c2ecf20Sopenharmony_ci	if (phydev->interface != PHY_INTERFACE_MODE_SGMII &&
4718c2ecf20Sopenharmony_ci	    phydev->interface != PHY_INTERFACE_MODE_2500BASEX &&
4728c2ecf20Sopenharmony_ci	    phydev->interface != PHY_INTERFACE_MODE_XAUI &&
4738c2ecf20Sopenharmony_ci	    phydev->interface != PHY_INTERFACE_MODE_RXAUI &&
4748c2ecf20Sopenharmony_ci	    phydev->interface != PHY_INTERFACE_MODE_10GBASER)
4758c2ecf20Sopenharmony_ci		return -ENODEV;
4768c2ecf20Sopenharmony_ci
4778c2ecf20Sopenharmony_ci	phydev->mdix_ctrl = ETH_TP_MDI_AUTO;
4788c2ecf20Sopenharmony_ci
4798c2ecf20Sopenharmony_ci	/* Power up so reset works */
4808c2ecf20Sopenharmony_ci	err = mv3310_power_up(phydev);
4818c2ecf20Sopenharmony_ci	if (err)
4828c2ecf20Sopenharmony_ci		return err;
4838c2ecf20Sopenharmony_ci
4848c2ecf20Sopenharmony_ci	val = phy_read_mmd(phydev, MDIO_MMD_VEND2, MV_V2_PORT_CTRL);
4858c2ecf20Sopenharmony_ci	if (val < 0)
4868c2ecf20Sopenharmony_ci		return val;
4878c2ecf20Sopenharmony_ci	priv->rate_match = ((val & MV_V2_PORT_MAC_TYPE_MASK) ==
4888c2ecf20Sopenharmony_ci			MV_V2_PORT_MAC_TYPE_RATE_MATCH);
4898c2ecf20Sopenharmony_ci
4908c2ecf20Sopenharmony_ci	/* Enable EDPD mode - saving 600mW */
4918c2ecf20Sopenharmony_ci	return mv3310_set_edpd(phydev, ETHTOOL_PHY_EDPD_DFLT_TX_MSECS);
4928c2ecf20Sopenharmony_ci}
4938c2ecf20Sopenharmony_ci
4948c2ecf20Sopenharmony_cistatic int mv3310_get_features(struct phy_device *phydev)
4958c2ecf20Sopenharmony_ci{
4968c2ecf20Sopenharmony_ci	int ret, val;
4978c2ecf20Sopenharmony_ci
4988c2ecf20Sopenharmony_ci	ret = genphy_c45_pma_read_abilities(phydev);
4998c2ecf20Sopenharmony_ci	if (ret)
5008c2ecf20Sopenharmony_ci		return ret;
5018c2ecf20Sopenharmony_ci
5028c2ecf20Sopenharmony_ci	if (mv3310_has_pma_ngbaset_quirk(phydev)) {
5038c2ecf20Sopenharmony_ci		val = phy_read_mmd(phydev, MDIO_MMD_PMAPMD,
5048c2ecf20Sopenharmony_ci				   MDIO_PMA_NG_EXTABLE);
5058c2ecf20Sopenharmony_ci		if (val < 0)
5068c2ecf20Sopenharmony_ci			return val;
5078c2ecf20Sopenharmony_ci
5088c2ecf20Sopenharmony_ci		linkmode_mod_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT,
5098c2ecf20Sopenharmony_ci				 phydev->supported,
5108c2ecf20Sopenharmony_ci				 val & MDIO_PMA_NG_EXTABLE_2_5GBT);
5118c2ecf20Sopenharmony_ci
5128c2ecf20Sopenharmony_ci		linkmode_mod_bit(ETHTOOL_LINK_MODE_5000baseT_Full_BIT,
5138c2ecf20Sopenharmony_ci				 phydev->supported,
5148c2ecf20Sopenharmony_ci				 val & MDIO_PMA_NG_EXTABLE_5GBT);
5158c2ecf20Sopenharmony_ci	}
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_ci	return 0;
5188c2ecf20Sopenharmony_ci}
5198c2ecf20Sopenharmony_ci
5208c2ecf20Sopenharmony_cistatic int mv3310_config_mdix(struct phy_device *phydev)
5218c2ecf20Sopenharmony_ci{
5228c2ecf20Sopenharmony_ci	u16 val;
5238c2ecf20Sopenharmony_ci	int err;
5248c2ecf20Sopenharmony_ci
5258c2ecf20Sopenharmony_ci	switch (phydev->mdix_ctrl) {
5268c2ecf20Sopenharmony_ci	case ETH_TP_MDI_AUTO:
5278c2ecf20Sopenharmony_ci		val = MV_PCS_CSCR1_MDIX_AUTO;
5288c2ecf20Sopenharmony_ci		break;
5298c2ecf20Sopenharmony_ci	case ETH_TP_MDI_X:
5308c2ecf20Sopenharmony_ci		val = MV_PCS_CSCR1_MDIX_MDIX;
5318c2ecf20Sopenharmony_ci		break;
5328c2ecf20Sopenharmony_ci	case ETH_TP_MDI:
5338c2ecf20Sopenharmony_ci		val = MV_PCS_CSCR1_MDIX_MDI;
5348c2ecf20Sopenharmony_ci		break;
5358c2ecf20Sopenharmony_ci	default:
5368c2ecf20Sopenharmony_ci		return -EINVAL;
5378c2ecf20Sopenharmony_ci	}
5388c2ecf20Sopenharmony_ci
5398c2ecf20Sopenharmony_ci	err = phy_modify_mmd_changed(phydev, MDIO_MMD_PCS, MV_PCS_CSCR1,
5408c2ecf20Sopenharmony_ci				     MV_PCS_CSCR1_MDIX_MASK, val);
5418c2ecf20Sopenharmony_ci	if (err > 0)
5428c2ecf20Sopenharmony_ci		err = mv3310_reset(phydev, MV_PCS_BASE_T);
5438c2ecf20Sopenharmony_ci
5448c2ecf20Sopenharmony_ci	return err;
5458c2ecf20Sopenharmony_ci}
5468c2ecf20Sopenharmony_ci
5478c2ecf20Sopenharmony_cistatic int mv3310_config_aneg(struct phy_device *phydev)
5488c2ecf20Sopenharmony_ci{
5498c2ecf20Sopenharmony_ci	bool changed = false;
5508c2ecf20Sopenharmony_ci	u16 reg;
5518c2ecf20Sopenharmony_ci	int ret;
5528c2ecf20Sopenharmony_ci
5538c2ecf20Sopenharmony_ci	ret = mv3310_config_mdix(phydev);
5548c2ecf20Sopenharmony_ci	if (ret < 0)
5558c2ecf20Sopenharmony_ci		return ret;
5568c2ecf20Sopenharmony_ci
5578c2ecf20Sopenharmony_ci	if (phydev->autoneg == AUTONEG_DISABLE)
5588c2ecf20Sopenharmony_ci		return genphy_c45_pma_setup_forced(phydev);
5598c2ecf20Sopenharmony_ci
5608c2ecf20Sopenharmony_ci	ret = genphy_c45_an_config_aneg(phydev);
5618c2ecf20Sopenharmony_ci	if (ret < 0)
5628c2ecf20Sopenharmony_ci		return ret;
5638c2ecf20Sopenharmony_ci	if (ret > 0)
5648c2ecf20Sopenharmony_ci		changed = true;
5658c2ecf20Sopenharmony_ci
5668c2ecf20Sopenharmony_ci	/* Clause 45 has no standardized support for 1000BaseT, therefore
5678c2ecf20Sopenharmony_ci	 * use vendor registers for this mode.
5688c2ecf20Sopenharmony_ci	 */
5698c2ecf20Sopenharmony_ci	reg = linkmode_adv_to_mii_ctrl1000_t(phydev->advertising);
5708c2ecf20Sopenharmony_ci	ret = phy_modify_mmd_changed(phydev, MDIO_MMD_AN, MV_AN_CTRL1000,
5718c2ecf20Sopenharmony_ci			     ADVERTISE_1000FULL | ADVERTISE_1000HALF, reg);
5728c2ecf20Sopenharmony_ci	if (ret < 0)
5738c2ecf20Sopenharmony_ci		return ret;
5748c2ecf20Sopenharmony_ci	if (ret > 0)
5758c2ecf20Sopenharmony_ci		changed = true;
5768c2ecf20Sopenharmony_ci
5778c2ecf20Sopenharmony_ci	return genphy_c45_check_and_restart_aneg(phydev, changed);
5788c2ecf20Sopenharmony_ci}
5798c2ecf20Sopenharmony_ci
5808c2ecf20Sopenharmony_cistatic int mv3310_aneg_done(struct phy_device *phydev)
5818c2ecf20Sopenharmony_ci{
5828c2ecf20Sopenharmony_ci	int val;
5838c2ecf20Sopenharmony_ci
5848c2ecf20Sopenharmony_ci	val = phy_read_mmd(phydev, MDIO_MMD_PCS, MV_PCS_BASE_R + MDIO_STAT1);
5858c2ecf20Sopenharmony_ci	if (val < 0)
5868c2ecf20Sopenharmony_ci		return val;
5878c2ecf20Sopenharmony_ci
5888c2ecf20Sopenharmony_ci	if (val & MDIO_STAT1_LSTATUS)
5898c2ecf20Sopenharmony_ci		return 1;
5908c2ecf20Sopenharmony_ci
5918c2ecf20Sopenharmony_ci	return genphy_c45_aneg_done(phydev);
5928c2ecf20Sopenharmony_ci}
5938c2ecf20Sopenharmony_ci
5948c2ecf20Sopenharmony_cistatic void mv3310_update_interface(struct phy_device *phydev)
5958c2ecf20Sopenharmony_ci{
5968c2ecf20Sopenharmony_ci	struct mv3310_priv *priv = dev_get_drvdata(&phydev->mdio.dev);
5978c2ecf20Sopenharmony_ci
5988c2ecf20Sopenharmony_ci	/* In "XFI with Rate Matching" mode the PHY interface is fixed at
5998c2ecf20Sopenharmony_ci	 * 10Gb. The PHY adapts the rate to actual wire speed with help of
6008c2ecf20Sopenharmony_ci	 * internal 16KB buffer.
6018c2ecf20Sopenharmony_ci	 */
6028c2ecf20Sopenharmony_ci	if (priv->rate_match) {
6038c2ecf20Sopenharmony_ci		phydev->interface = PHY_INTERFACE_MODE_10GBASER;
6048c2ecf20Sopenharmony_ci		return;
6058c2ecf20Sopenharmony_ci	}
6068c2ecf20Sopenharmony_ci
6078c2ecf20Sopenharmony_ci	if ((phydev->interface == PHY_INTERFACE_MODE_SGMII ||
6088c2ecf20Sopenharmony_ci	     phydev->interface == PHY_INTERFACE_MODE_2500BASEX ||
6098c2ecf20Sopenharmony_ci	     phydev->interface == PHY_INTERFACE_MODE_10GBASER) &&
6108c2ecf20Sopenharmony_ci	    phydev->link) {
6118c2ecf20Sopenharmony_ci		/* The PHY automatically switches its serdes interface (and
6128c2ecf20Sopenharmony_ci		 * active PHYXS instance) between Cisco SGMII, 10GBase-R and
6138c2ecf20Sopenharmony_ci		 * 2500BaseX modes according to the speed.  Florian suggests
6148c2ecf20Sopenharmony_ci		 * setting phydev->interface to communicate this to the MAC.
6158c2ecf20Sopenharmony_ci		 * Only do this if we are already in one of the above modes.
6168c2ecf20Sopenharmony_ci		 */
6178c2ecf20Sopenharmony_ci		switch (phydev->speed) {
6188c2ecf20Sopenharmony_ci		case SPEED_10000:
6198c2ecf20Sopenharmony_ci			phydev->interface = PHY_INTERFACE_MODE_10GBASER;
6208c2ecf20Sopenharmony_ci			break;
6218c2ecf20Sopenharmony_ci		case SPEED_2500:
6228c2ecf20Sopenharmony_ci			phydev->interface = PHY_INTERFACE_MODE_2500BASEX;
6238c2ecf20Sopenharmony_ci			break;
6248c2ecf20Sopenharmony_ci		case SPEED_1000:
6258c2ecf20Sopenharmony_ci		case SPEED_100:
6268c2ecf20Sopenharmony_ci		case SPEED_10:
6278c2ecf20Sopenharmony_ci			phydev->interface = PHY_INTERFACE_MODE_SGMII;
6288c2ecf20Sopenharmony_ci			break;
6298c2ecf20Sopenharmony_ci		default:
6308c2ecf20Sopenharmony_ci			break;
6318c2ecf20Sopenharmony_ci		}
6328c2ecf20Sopenharmony_ci	}
6338c2ecf20Sopenharmony_ci}
6348c2ecf20Sopenharmony_ci
6358c2ecf20Sopenharmony_ci/* 10GBASE-ER,LR,LRM,SR do not support autonegotiation. */
6368c2ecf20Sopenharmony_cistatic int mv3310_read_status_10gbaser(struct phy_device *phydev)
6378c2ecf20Sopenharmony_ci{
6388c2ecf20Sopenharmony_ci	phydev->link = 1;
6398c2ecf20Sopenharmony_ci	phydev->speed = SPEED_10000;
6408c2ecf20Sopenharmony_ci	phydev->duplex = DUPLEX_FULL;
6418c2ecf20Sopenharmony_ci	phydev->port = PORT_FIBRE;
6428c2ecf20Sopenharmony_ci
6438c2ecf20Sopenharmony_ci	return 0;
6448c2ecf20Sopenharmony_ci}
6458c2ecf20Sopenharmony_ci
6468c2ecf20Sopenharmony_cistatic int mv3310_read_status_copper(struct phy_device *phydev)
6478c2ecf20Sopenharmony_ci{
6488c2ecf20Sopenharmony_ci	int cssr1, speed, val;
6498c2ecf20Sopenharmony_ci
6508c2ecf20Sopenharmony_ci	val = genphy_c45_read_link(phydev);
6518c2ecf20Sopenharmony_ci	if (val < 0)
6528c2ecf20Sopenharmony_ci		return val;
6538c2ecf20Sopenharmony_ci
6548c2ecf20Sopenharmony_ci	val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_STAT1);
6558c2ecf20Sopenharmony_ci	if (val < 0)
6568c2ecf20Sopenharmony_ci		return val;
6578c2ecf20Sopenharmony_ci
6588c2ecf20Sopenharmony_ci	cssr1 = phy_read_mmd(phydev, MDIO_MMD_PCS, MV_PCS_CSSR1);
6598c2ecf20Sopenharmony_ci	if (cssr1 < 0)
6608c2ecf20Sopenharmony_ci		return cssr1;
6618c2ecf20Sopenharmony_ci
6628c2ecf20Sopenharmony_ci	/* If the link settings are not resolved, mark the link down */
6638c2ecf20Sopenharmony_ci	if (!(cssr1 & MV_PCS_CSSR1_RESOLVED)) {
6648c2ecf20Sopenharmony_ci		phydev->link = 0;
6658c2ecf20Sopenharmony_ci		return 0;
6668c2ecf20Sopenharmony_ci	}
6678c2ecf20Sopenharmony_ci
6688c2ecf20Sopenharmony_ci	/* Read the copper link settings */
6698c2ecf20Sopenharmony_ci	speed = cssr1 & MV_PCS_CSSR1_SPD1_MASK;
6708c2ecf20Sopenharmony_ci	if (speed == MV_PCS_CSSR1_SPD1_SPD2)
6718c2ecf20Sopenharmony_ci		speed |= cssr1 & MV_PCS_CSSR1_SPD2_MASK;
6728c2ecf20Sopenharmony_ci
6738c2ecf20Sopenharmony_ci	switch (speed) {
6748c2ecf20Sopenharmony_ci	case MV_PCS_CSSR1_SPD1_SPD2 | MV_PCS_CSSR1_SPD2_10000:
6758c2ecf20Sopenharmony_ci		phydev->speed = SPEED_10000;
6768c2ecf20Sopenharmony_ci		break;
6778c2ecf20Sopenharmony_ci
6788c2ecf20Sopenharmony_ci	case MV_PCS_CSSR1_SPD1_SPD2 | MV_PCS_CSSR1_SPD2_5000:
6798c2ecf20Sopenharmony_ci		phydev->speed = SPEED_5000;
6808c2ecf20Sopenharmony_ci		break;
6818c2ecf20Sopenharmony_ci
6828c2ecf20Sopenharmony_ci	case MV_PCS_CSSR1_SPD1_SPD2 | MV_PCS_CSSR1_SPD2_2500:
6838c2ecf20Sopenharmony_ci		phydev->speed = SPEED_2500;
6848c2ecf20Sopenharmony_ci		break;
6858c2ecf20Sopenharmony_ci
6868c2ecf20Sopenharmony_ci	case MV_PCS_CSSR1_SPD1_1000:
6878c2ecf20Sopenharmony_ci		phydev->speed = SPEED_1000;
6888c2ecf20Sopenharmony_ci		break;
6898c2ecf20Sopenharmony_ci
6908c2ecf20Sopenharmony_ci	case MV_PCS_CSSR1_SPD1_100:
6918c2ecf20Sopenharmony_ci		phydev->speed = SPEED_100;
6928c2ecf20Sopenharmony_ci		break;
6938c2ecf20Sopenharmony_ci
6948c2ecf20Sopenharmony_ci	case MV_PCS_CSSR1_SPD1_10:
6958c2ecf20Sopenharmony_ci		phydev->speed = SPEED_10;
6968c2ecf20Sopenharmony_ci		break;
6978c2ecf20Sopenharmony_ci	}
6988c2ecf20Sopenharmony_ci
6998c2ecf20Sopenharmony_ci	phydev->duplex = cssr1 & MV_PCS_CSSR1_DUPLEX_FULL ?
7008c2ecf20Sopenharmony_ci			 DUPLEX_FULL : DUPLEX_HALF;
7018c2ecf20Sopenharmony_ci	phydev->port = PORT_TP;
7028c2ecf20Sopenharmony_ci	phydev->mdix = cssr1 & MV_PCS_CSSR1_MDIX ?
7038c2ecf20Sopenharmony_ci		       ETH_TP_MDI_X : ETH_TP_MDI;
7048c2ecf20Sopenharmony_ci
7058c2ecf20Sopenharmony_ci	if (val & MDIO_AN_STAT1_COMPLETE) {
7068c2ecf20Sopenharmony_ci		val = genphy_c45_read_lpa(phydev);
7078c2ecf20Sopenharmony_ci		if (val < 0)
7088c2ecf20Sopenharmony_ci			return val;
7098c2ecf20Sopenharmony_ci
7108c2ecf20Sopenharmony_ci		/* Read the link partner's 1G advertisement */
7118c2ecf20Sopenharmony_ci		val = phy_read_mmd(phydev, MDIO_MMD_AN, MV_AN_STAT1000);
7128c2ecf20Sopenharmony_ci		if (val < 0)
7138c2ecf20Sopenharmony_ci			return val;
7148c2ecf20Sopenharmony_ci
7158c2ecf20Sopenharmony_ci		mii_stat1000_mod_linkmode_lpa_t(phydev->lp_advertising, val);
7168c2ecf20Sopenharmony_ci
7178c2ecf20Sopenharmony_ci		/* Update the pause status */
7188c2ecf20Sopenharmony_ci		phy_resolve_aneg_pause(phydev);
7198c2ecf20Sopenharmony_ci	}
7208c2ecf20Sopenharmony_ci
7218c2ecf20Sopenharmony_ci	return 0;
7228c2ecf20Sopenharmony_ci}
7238c2ecf20Sopenharmony_ci
7248c2ecf20Sopenharmony_cistatic int mv3310_read_status(struct phy_device *phydev)
7258c2ecf20Sopenharmony_ci{
7268c2ecf20Sopenharmony_ci	int err, val;
7278c2ecf20Sopenharmony_ci
7288c2ecf20Sopenharmony_ci	phydev->speed = SPEED_UNKNOWN;
7298c2ecf20Sopenharmony_ci	phydev->duplex = DUPLEX_UNKNOWN;
7308c2ecf20Sopenharmony_ci	linkmode_zero(phydev->lp_advertising);
7318c2ecf20Sopenharmony_ci	phydev->link = 0;
7328c2ecf20Sopenharmony_ci	phydev->pause = 0;
7338c2ecf20Sopenharmony_ci	phydev->asym_pause = 0;
7348c2ecf20Sopenharmony_ci	phydev->mdix = ETH_TP_MDI_INVALID;
7358c2ecf20Sopenharmony_ci
7368c2ecf20Sopenharmony_ci	val = phy_read_mmd(phydev, MDIO_MMD_PCS, MV_PCS_BASE_R + MDIO_STAT1);
7378c2ecf20Sopenharmony_ci	if (val < 0)
7388c2ecf20Sopenharmony_ci		return val;
7398c2ecf20Sopenharmony_ci
7408c2ecf20Sopenharmony_ci	if (val & MDIO_STAT1_LSTATUS)
7418c2ecf20Sopenharmony_ci		err = mv3310_read_status_10gbaser(phydev);
7428c2ecf20Sopenharmony_ci	else
7438c2ecf20Sopenharmony_ci		err = mv3310_read_status_copper(phydev);
7448c2ecf20Sopenharmony_ci	if (err < 0)
7458c2ecf20Sopenharmony_ci		return err;
7468c2ecf20Sopenharmony_ci
7478c2ecf20Sopenharmony_ci	if (phydev->link)
7488c2ecf20Sopenharmony_ci		mv3310_update_interface(phydev);
7498c2ecf20Sopenharmony_ci
7508c2ecf20Sopenharmony_ci	return 0;
7518c2ecf20Sopenharmony_ci}
7528c2ecf20Sopenharmony_ci
7538c2ecf20Sopenharmony_cistatic int mv3310_get_tunable(struct phy_device *phydev,
7548c2ecf20Sopenharmony_ci			      struct ethtool_tunable *tuna, void *data)
7558c2ecf20Sopenharmony_ci{
7568c2ecf20Sopenharmony_ci	switch (tuna->id) {
7578c2ecf20Sopenharmony_ci	case ETHTOOL_PHY_EDPD:
7588c2ecf20Sopenharmony_ci		return mv3310_get_edpd(phydev, data);
7598c2ecf20Sopenharmony_ci	default:
7608c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
7618c2ecf20Sopenharmony_ci	}
7628c2ecf20Sopenharmony_ci}
7638c2ecf20Sopenharmony_ci
7648c2ecf20Sopenharmony_cistatic int mv3310_set_tunable(struct phy_device *phydev,
7658c2ecf20Sopenharmony_ci			      struct ethtool_tunable *tuna, const void *data)
7668c2ecf20Sopenharmony_ci{
7678c2ecf20Sopenharmony_ci	switch (tuna->id) {
7688c2ecf20Sopenharmony_ci	case ETHTOOL_PHY_EDPD:
7698c2ecf20Sopenharmony_ci		return mv3310_set_edpd(phydev, *(u16 *)data);
7708c2ecf20Sopenharmony_ci	default:
7718c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
7728c2ecf20Sopenharmony_ci	}
7738c2ecf20Sopenharmony_ci}
7748c2ecf20Sopenharmony_ci
7758c2ecf20Sopenharmony_cistatic struct phy_driver mv3310_drivers[] = {
7768c2ecf20Sopenharmony_ci	{
7778c2ecf20Sopenharmony_ci		.phy_id		= MARVELL_PHY_ID_88X3310,
7788c2ecf20Sopenharmony_ci		.phy_id_mask	= MARVELL_PHY_ID_MASK,
7798c2ecf20Sopenharmony_ci		.name		= "mv88x3310",
7808c2ecf20Sopenharmony_ci		.get_features	= mv3310_get_features,
7818c2ecf20Sopenharmony_ci		.config_init	= mv3310_config_init,
7828c2ecf20Sopenharmony_ci		.probe		= mv3310_probe,
7838c2ecf20Sopenharmony_ci		.suspend	= mv3310_suspend,
7848c2ecf20Sopenharmony_ci		.resume		= mv3310_resume,
7858c2ecf20Sopenharmony_ci		.config_aneg	= mv3310_config_aneg,
7868c2ecf20Sopenharmony_ci		.aneg_done	= mv3310_aneg_done,
7878c2ecf20Sopenharmony_ci		.read_status	= mv3310_read_status,
7888c2ecf20Sopenharmony_ci		.get_tunable	= mv3310_get_tunable,
7898c2ecf20Sopenharmony_ci		.set_tunable	= mv3310_set_tunable,
7908c2ecf20Sopenharmony_ci		.remove		= mv3310_remove,
7918c2ecf20Sopenharmony_ci	},
7928c2ecf20Sopenharmony_ci	{
7938c2ecf20Sopenharmony_ci		.phy_id		= MARVELL_PHY_ID_88E2110,
7948c2ecf20Sopenharmony_ci		.phy_id_mask	= MARVELL_PHY_ID_MASK,
7958c2ecf20Sopenharmony_ci		.name		= "mv88x2110",
7968c2ecf20Sopenharmony_ci		.probe		= mv3310_probe,
7978c2ecf20Sopenharmony_ci		.suspend	= mv3310_suspend,
7988c2ecf20Sopenharmony_ci		.resume		= mv3310_resume,
7998c2ecf20Sopenharmony_ci		.config_init	= mv3310_config_init,
8008c2ecf20Sopenharmony_ci		.config_aneg	= mv3310_config_aneg,
8018c2ecf20Sopenharmony_ci		.aneg_done	= mv3310_aneg_done,
8028c2ecf20Sopenharmony_ci		.read_status	= mv3310_read_status,
8038c2ecf20Sopenharmony_ci		.get_tunable	= mv3310_get_tunable,
8048c2ecf20Sopenharmony_ci		.set_tunable	= mv3310_set_tunable,
8058c2ecf20Sopenharmony_ci		.remove		= mv3310_remove,
8068c2ecf20Sopenharmony_ci	},
8078c2ecf20Sopenharmony_ci};
8088c2ecf20Sopenharmony_ci
8098c2ecf20Sopenharmony_cimodule_phy_driver(mv3310_drivers);
8108c2ecf20Sopenharmony_ci
8118c2ecf20Sopenharmony_cistatic struct mdio_device_id __maybe_unused mv3310_tbl[] = {
8128c2ecf20Sopenharmony_ci	{ MARVELL_PHY_ID_88X3310, MARVELL_PHY_ID_MASK },
8138c2ecf20Sopenharmony_ci	{ MARVELL_PHY_ID_88E2110, MARVELL_PHY_ID_MASK },
8148c2ecf20Sopenharmony_ci	{ },
8158c2ecf20Sopenharmony_ci};
8168c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(mdio, mv3310_tbl);
8178c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Marvell Alaska X 10Gigabit Ethernet PHY driver (MV88X3310)");
8188c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
819