xref: /openbmc/linux/drivers/net/phy/phy-c45.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
1457c8996SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
25acde34aSRussell King /*
35acde34aSRussell King  * Clause 45 PHY support
45acde34aSRussell King  */
55acde34aSRussell King #include <linux/ethtool.h>
65acde34aSRussell King #include <linux/export.h>
75acde34aSRussell King #include <linux/mdio.h>
85acde34aSRussell King #include <linux/mii.h>
95acde34aSRussell King #include <linux/phy.h>
105acde34aSRussell King 
1149332341SPiergiorgio Beruto #include "mdio-open-alliance.h"
1249332341SPiergiorgio Beruto 
135acde34aSRussell King /**
143da8ffd8SAlexandru Tachici  * genphy_c45_baset1_able - checks if the PMA has BASE-T1 extended abilities
153da8ffd8SAlexandru Tachici  * @phydev: target phy_device struct
163da8ffd8SAlexandru Tachici  */
genphy_c45_baset1_able(struct phy_device * phydev)173da8ffd8SAlexandru Tachici static bool genphy_c45_baset1_able(struct phy_device *phydev)
183da8ffd8SAlexandru Tachici {
193da8ffd8SAlexandru Tachici 	int val;
203da8ffd8SAlexandru Tachici 
213da8ffd8SAlexandru Tachici 	if (phydev->pma_extable == -ENODATA) {
223da8ffd8SAlexandru Tachici 		val = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_PMA_EXTABLE);
233da8ffd8SAlexandru Tachici 		if (val < 0)
243da8ffd8SAlexandru Tachici 			return false;
253da8ffd8SAlexandru Tachici 
263da8ffd8SAlexandru Tachici 		phydev->pma_extable = val;
273da8ffd8SAlexandru Tachici 	}
283da8ffd8SAlexandru Tachici 
293da8ffd8SAlexandru Tachici 	return !!(phydev->pma_extable & MDIO_PMA_EXTABLE_BT1);
303da8ffd8SAlexandru Tachici }
313da8ffd8SAlexandru Tachici 
323da8ffd8SAlexandru Tachici /**
33da702f34SRadu Pirea (NXP OSS)  * genphy_c45_pma_can_sleep - checks if the PMA have sleep support
34da702f34SRadu Pirea (NXP OSS)  * @phydev: target phy_device struct
35da702f34SRadu Pirea (NXP OSS)  */
genphy_c45_pma_can_sleep(struct phy_device * phydev)36da702f34SRadu Pirea (NXP OSS) static bool genphy_c45_pma_can_sleep(struct phy_device *phydev)
37da702f34SRadu Pirea (NXP OSS) {
38da702f34SRadu Pirea (NXP OSS) 	int stat1;
39da702f34SRadu Pirea (NXP OSS) 
40da702f34SRadu Pirea (NXP OSS) 	stat1 = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_STAT1);
41da702f34SRadu Pirea (NXP OSS) 	if (stat1 < 0)
42da702f34SRadu Pirea (NXP OSS) 		return false;
43da702f34SRadu Pirea (NXP OSS) 
44da702f34SRadu Pirea (NXP OSS) 	return !!(stat1 & MDIO_STAT1_LPOWERABLE);
45da702f34SRadu Pirea (NXP OSS) }
46da702f34SRadu Pirea (NXP OSS) 
47da702f34SRadu Pirea (NXP OSS) /**
48da702f34SRadu Pirea (NXP OSS)  * genphy_c45_pma_resume - wakes up the PMA module
49da702f34SRadu Pirea (NXP OSS)  * @phydev: target phy_device struct
50da702f34SRadu Pirea (NXP OSS)  */
genphy_c45_pma_resume(struct phy_device * phydev)51da702f34SRadu Pirea (NXP OSS) int genphy_c45_pma_resume(struct phy_device *phydev)
52da702f34SRadu Pirea (NXP OSS) {
53da702f34SRadu Pirea (NXP OSS) 	if (!genphy_c45_pma_can_sleep(phydev))
54da702f34SRadu Pirea (NXP OSS) 		return -EOPNOTSUPP;
55da702f34SRadu Pirea (NXP OSS) 
56da702f34SRadu Pirea (NXP OSS) 	return phy_clear_bits_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_CTRL1,
57da702f34SRadu Pirea (NXP OSS) 				  MDIO_CTRL1_LPOWER);
58da702f34SRadu Pirea (NXP OSS) }
59da702f34SRadu Pirea (NXP OSS) EXPORT_SYMBOL_GPL(genphy_c45_pma_resume);
60da702f34SRadu Pirea (NXP OSS) 
61da702f34SRadu Pirea (NXP OSS) /**
62da702f34SRadu Pirea (NXP OSS)  * genphy_c45_pma_suspend - suspends the PMA module
63da702f34SRadu Pirea (NXP OSS)  * @phydev: target phy_device struct
64da702f34SRadu Pirea (NXP OSS)  */
genphy_c45_pma_suspend(struct phy_device * phydev)65da702f34SRadu Pirea (NXP OSS) int genphy_c45_pma_suspend(struct phy_device *phydev)
66da702f34SRadu Pirea (NXP OSS) {
67da702f34SRadu Pirea (NXP OSS) 	if (!genphy_c45_pma_can_sleep(phydev))
68da702f34SRadu Pirea (NXP OSS) 		return -EOPNOTSUPP;
69da702f34SRadu Pirea (NXP OSS) 
70da702f34SRadu Pirea (NXP OSS) 	return phy_set_bits_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_CTRL1,
71da702f34SRadu Pirea (NXP OSS) 				MDIO_CTRL1_LPOWER);
72da702f34SRadu Pirea (NXP OSS) }
73da702f34SRadu Pirea (NXP OSS) EXPORT_SYMBOL_GPL(genphy_c45_pma_suspend);
74da702f34SRadu Pirea (NXP OSS) 
75da702f34SRadu Pirea (NXP OSS) /**
7690532850SOleksij Rempel  * genphy_c45_pma_baset1_setup_master_slave - configures forced master/slave
7790532850SOleksij Rempel  * role of BaseT1 devices.
7890532850SOleksij Rempel  * @phydev: target phy_device struct
7990532850SOleksij Rempel  */
genphy_c45_pma_baset1_setup_master_slave(struct phy_device * phydev)8090532850SOleksij Rempel int genphy_c45_pma_baset1_setup_master_slave(struct phy_device *phydev)
8190532850SOleksij Rempel {
8290532850SOleksij Rempel 	int ctl = 0;
8390532850SOleksij Rempel 
8490532850SOleksij Rempel 	switch (phydev->master_slave_set) {
8590532850SOleksij Rempel 	case MASTER_SLAVE_CFG_MASTER_PREFERRED:
8690532850SOleksij Rempel 	case MASTER_SLAVE_CFG_MASTER_FORCE:
8790532850SOleksij Rempel 		ctl = MDIO_PMA_PMD_BT1_CTRL_CFG_MST;
8890532850SOleksij Rempel 		break;
8990532850SOleksij Rempel 	case MASTER_SLAVE_CFG_SLAVE_FORCE:
9090532850SOleksij Rempel 	case MASTER_SLAVE_CFG_SLAVE_PREFERRED:
91a04dd88fSOleksij Rempel 		break;
9290532850SOleksij Rempel 	case MASTER_SLAVE_CFG_UNKNOWN:
9390532850SOleksij Rempel 	case MASTER_SLAVE_CFG_UNSUPPORTED:
94a04dd88fSOleksij Rempel 		return 0;
9590532850SOleksij Rempel 	default:
9690532850SOleksij Rempel 		phydev_warn(phydev, "Unsupported Master/Slave mode\n");
9790532850SOleksij Rempel 		return -EOPNOTSUPP;
9890532850SOleksij Rempel 	}
9990532850SOleksij Rempel 
10090532850SOleksij Rempel 	return phy_modify_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_PMA_PMD_BT1_CTRL,
10190532850SOleksij Rempel 			     MDIO_PMA_PMD_BT1_CTRL_CFG_MST, ctl);
10290532850SOleksij Rempel }
10390532850SOleksij Rempel EXPORT_SYMBOL_GPL(genphy_c45_pma_baset1_setup_master_slave);
10490532850SOleksij Rempel 
10590532850SOleksij Rempel /**
10669280228SMauro Carvalho Chehab  * genphy_c45_pma_setup_forced - configures a forced speed
1075acde34aSRussell King  * @phydev: target phy_device struct
1085acde34aSRussell King  */
genphy_c45_pma_setup_forced(struct phy_device * phydev)1095acde34aSRussell King int genphy_c45_pma_setup_forced(struct phy_device *phydev)
1105acde34aSRussell King {
11125108a83SStefan Eichenberger 	int bt1_ctrl, ctrl1, ctrl2, ret;
1125acde34aSRussell King 
1135acde34aSRussell King 	/* Half duplex is not supported */
1145acde34aSRussell King 	if (phydev->duplex != DUPLEX_FULL)
1155acde34aSRussell King 		return -EINVAL;
1165acde34aSRussell King 
1175acde34aSRussell King 	ctrl1 = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_CTRL1);
1185acde34aSRussell King 	if (ctrl1 < 0)
1195acde34aSRussell King 		return ctrl1;
1205acde34aSRussell King 
1215acde34aSRussell King 	ctrl2 = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_CTRL2);
1225acde34aSRussell King 	if (ctrl2 < 0)
1235acde34aSRussell King 		return ctrl2;
1245acde34aSRussell King 
1255acde34aSRussell King 	ctrl1 &= ~MDIO_CTRL1_SPEEDSEL;
1265acde34aSRussell King 	/*
1275acde34aSRussell King 	 * PMA/PMD type selection is 1.7.5:0 not 1.7.3:0.  See 45.2.1.6.1
1285acde34aSRussell King 	 * in 802.3-2012 and 802.3-2015.
1295acde34aSRussell King 	 */
1305acde34aSRussell King 	ctrl2 &= ~(MDIO_PMA_CTRL2_TYPE | 0x30);
1315acde34aSRussell King 
1325acde34aSRussell King 	switch (phydev->speed) {
1335acde34aSRussell King 	case SPEED_10:
1343da8ffd8SAlexandru Tachici 		if (genphy_c45_baset1_able(phydev))
1353da8ffd8SAlexandru Tachici 			ctrl2 |= MDIO_PMA_CTRL2_BASET1;
1363da8ffd8SAlexandru Tachici 		else
1375acde34aSRussell King 			ctrl2 |= MDIO_PMA_CTRL2_10BT;
1385acde34aSRussell King 		break;
1395acde34aSRussell King 	case SPEED_100:
1405acde34aSRussell King 		ctrl1 |= MDIO_PMA_CTRL1_SPEED100;
1415acde34aSRussell King 		ctrl2 |= MDIO_PMA_CTRL2_100BTX;
1425acde34aSRussell King 		break;
1435acde34aSRussell King 	case SPEED_1000:
1445acde34aSRussell King 		ctrl1 |= MDIO_PMA_CTRL1_SPEED1000;
1455acde34aSRussell King 		/* Assume 1000base-T */
1465acde34aSRussell King 		ctrl2 |= MDIO_PMA_CTRL2_1000BT;
1475acde34aSRussell King 		break;
1487fd8afa8SMaxime Chevallier 	case SPEED_2500:
1497fd8afa8SMaxime Chevallier 		ctrl1 |= MDIO_CTRL1_SPEED2_5G;
1507fd8afa8SMaxime Chevallier 		/* Assume 2.5Gbase-T */
1517fd8afa8SMaxime Chevallier 		ctrl2 |= MDIO_PMA_CTRL2_2_5GBT;
1527fd8afa8SMaxime Chevallier 		break;
1537fd8afa8SMaxime Chevallier 	case SPEED_5000:
1547fd8afa8SMaxime Chevallier 		ctrl1 |= MDIO_CTRL1_SPEED5G;
1557fd8afa8SMaxime Chevallier 		/* Assume 5Gbase-T */
1567fd8afa8SMaxime Chevallier 		ctrl2 |= MDIO_PMA_CTRL2_5GBT;
1577fd8afa8SMaxime Chevallier 		break;
1585acde34aSRussell King 	case SPEED_10000:
1595acde34aSRussell King 		ctrl1 |= MDIO_CTRL1_SPEED10G;
1605acde34aSRussell King 		/* Assume 10Gbase-T */
1615acde34aSRussell King 		ctrl2 |= MDIO_PMA_CTRL2_10GBT;
1625acde34aSRussell King 		break;
1635acde34aSRussell King 	default:
1645acde34aSRussell King 		return -EINVAL;
1655acde34aSRussell King 	}
1665acde34aSRussell King 
1675acde34aSRussell King 	ret = phy_write_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_CTRL1, ctrl1);
1685acde34aSRussell King 	if (ret < 0)
1695acde34aSRussell King 		return ret;
1705acde34aSRussell King 
17129f000f7SHeiner Kallweit 	ret = phy_write_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_CTRL2, ctrl2);
17229f000f7SHeiner Kallweit 	if (ret < 0)
17329f000f7SHeiner Kallweit 		return ret;
17429f000f7SHeiner Kallweit 
1753da8ffd8SAlexandru Tachici 	if (genphy_c45_baset1_able(phydev)) {
17690532850SOleksij Rempel 		ret = genphy_c45_pma_baset1_setup_master_slave(phydev);
1773da8ffd8SAlexandru Tachici 		if (ret < 0)
1783da8ffd8SAlexandru Tachici 			return ret;
17925108a83SStefan Eichenberger 
18025108a83SStefan Eichenberger 		bt1_ctrl = 0;
18125108a83SStefan Eichenberger 		if (phydev->speed == SPEED_1000)
18225108a83SStefan Eichenberger 			bt1_ctrl = MDIO_PMA_PMD_BT1_CTRL_STRAP_B1000;
18325108a83SStefan Eichenberger 
18425108a83SStefan Eichenberger 		ret = phy_modify_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_PMA_PMD_BT1_CTRL,
18525108a83SStefan Eichenberger 				     MDIO_PMA_PMD_BT1_CTRL_STRAP, bt1_ctrl);
18625108a83SStefan Eichenberger 		if (ret < 0)
18725108a83SStefan Eichenberger 			return ret;
1883da8ffd8SAlexandru Tachici 	}
1893da8ffd8SAlexandru Tachici 
19029f000f7SHeiner Kallweit 	return genphy_c45_an_disable_aneg(phydev);
1915acde34aSRussell King }
1925acde34aSRussell King EXPORT_SYMBOL_GPL(genphy_c45_pma_setup_forced);
1935acde34aSRussell King 
1943da8ffd8SAlexandru Tachici /* Sets master/slave preference and supported technologies.
1953da8ffd8SAlexandru Tachici  * The preference is set in the BIT(4) of BASE-T1 AN
1963da8ffd8SAlexandru Tachici  * advertisement register 7.515 and whether the status
1973da8ffd8SAlexandru Tachici  * is forced or not, it is set in the BIT(12) of BASE-T1
1983da8ffd8SAlexandru Tachici  * AN advertisement register 7.514.
1993da8ffd8SAlexandru Tachici  * Sets 10BASE-T1L Ability BIT(14) in BASE-T1 autonegotiation
2003da8ffd8SAlexandru Tachici  * advertisement register [31:16] if supported.
2013da8ffd8SAlexandru Tachici  */
genphy_c45_baset1_an_config_aneg(struct phy_device * phydev)2023da8ffd8SAlexandru Tachici static int genphy_c45_baset1_an_config_aneg(struct phy_device *phydev)
2033da8ffd8SAlexandru Tachici {
2043702e404SOleksij Rempel 	u16 adv_l_mask, adv_l = 0;
2053702e404SOleksij Rempel 	u16 adv_m_mask, adv_m = 0;
2063da8ffd8SAlexandru Tachici 	int changed = 0;
2073da8ffd8SAlexandru Tachici 	int ret;
2083da8ffd8SAlexandru Tachici 
2093702e404SOleksij Rempel 	adv_l_mask = MDIO_AN_T1_ADV_L_FORCE_MS | MDIO_AN_T1_ADV_L_PAUSE_CAP |
2103702e404SOleksij Rempel 		MDIO_AN_T1_ADV_L_PAUSE_ASYM;
2113702e404SOleksij Rempel 	adv_m_mask = MDIO_AN_T1_ADV_M_MST | MDIO_AN_T1_ADV_M_B10L;
2123702e404SOleksij Rempel 
2133da8ffd8SAlexandru Tachici 	switch (phydev->master_slave_set) {
2143da8ffd8SAlexandru Tachici 	case MASTER_SLAVE_CFG_MASTER_FORCE:
2153702e404SOleksij Rempel 		adv_m |= MDIO_AN_T1_ADV_M_MST;
2163702e404SOleksij Rempel 		fallthrough;
2173da8ffd8SAlexandru Tachici 	case MASTER_SLAVE_CFG_SLAVE_FORCE:
2183da8ffd8SAlexandru Tachici 		adv_l |= MDIO_AN_T1_ADV_L_FORCE_MS;
2193da8ffd8SAlexandru Tachici 		break;
2203da8ffd8SAlexandru Tachici 	case MASTER_SLAVE_CFG_MASTER_PREFERRED:
2213702e404SOleksij Rempel 		adv_m |= MDIO_AN_T1_ADV_M_MST;
2223702e404SOleksij Rempel 		fallthrough;
2233da8ffd8SAlexandru Tachici 	case MASTER_SLAVE_CFG_SLAVE_PREFERRED:
2243da8ffd8SAlexandru Tachici 		break;
225a7f0e4beSOleksij Rempel 	case MASTER_SLAVE_CFG_UNKNOWN:
226a7f0e4beSOleksij Rempel 	case MASTER_SLAVE_CFG_UNSUPPORTED:
2273702e404SOleksij Rempel 		/* if master/slave role is not specified, do not overwrite it */
2283702e404SOleksij Rempel 		adv_l_mask &= ~MDIO_AN_T1_ADV_L_FORCE_MS;
2293702e404SOleksij Rempel 		adv_m_mask &= ~MDIO_AN_T1_ADV_M_MST;
2303702e404SOleksij Rempel 		break;
2313da8ffd8SAlexandru Tachici 	default:
232a7f0e4beSOleksij Rempel 		phydev_warn(phydev, "Unsupported Master/Slave mode\n");
233a7f0e4beSOleksij Rempel 		return -EOPNOTSUPP;
2343da8ffd8SAlexandru Tachici 	}
2353da8ffd8SAlexandru Tachici 
2363da8ffd8SAlexandru Tachici 	adv_l |= linkmode_adv_to_mii_t1_adv_l_t(phydev->advertising);
2373da8ffd8SAlexandru Tachici 
2383da8ffd8SAlexandru Tachici 	ret = phy_modify_mmd_changed(phydev, MDIO_MMD_AN, MDIO_AN_T1_ADV_L,
2393702e404SOleksij Rempel 				     adv_l_mask, adv_l);
2403da8ffd8SAlexandru Tachici 	if (ret < 0)
2413da8ffd8SAlexandru Tachici 		return ret;
2423da8ffd8SAlexandru Tachici 	if (ret > 0)
2433da8ffd8SAlexandru Tachici 		changed = 1;
2443da8ffd8SAlexandru Tachici 
2453da8ffd8SAlexandru Tachici 	adv_m |= linkmode_adv_to_mii_t1_adv_m_t(phydev->advertising);
2463da8ffd8SAlexandru Tachici 
2473da8ffd8SAlexandru Tachici 	ret = phy_modify_mmd_changed(phydev, MDIO_MMD_AN, MDIO_AN_T1_ADV_M,
2483702e404SOleksij Rempel 				     adv_m_mask, adv_m);
2493da8ffd8SAlexandru Tachici 	if (ret < 0)
2503da8ffd8SAlexandru Tachici 		return ret;
2513da8ffd8SAlexandru Tachici 	if (ret > 0)
2523da8ffd8SAlexandru Tachici 		changed = 1;
2533da8ffd8SAlexandru Tachici 
2543da8ffd8SAlexandru Tachici 	return changed;
2553da8ffd8SAlexandru Tachici }
2563da8ffd8SAlexandru Tachici 
2575acde34aSRussell King /**
2589a5dc8afSAndrew Lunn  * genphy_c45_an_config_aneg - configure advertisement registers
2599a5dc8afSAndrew Lunn  * @phydev: target phy_device struct
2609a5dc8afSAndrew Lunn  *
2619a5dc8afSAndrew Lunn  * Configure advertisement registers based on modes set in phydev->advertising
2629a5dc8afSAndrew Lunn  *
2639a5dc8afSAndrew Lunn  * Returns negative errno code on failure, 0 if advertisement didn't change,
2649a5dc8afSAndrew Lunn  * or 1 if advertised modes changed.
2659a5dc8afSAndrew Lunn  */
genphy_c45_an_config_aneg(struct phy_device * phydev)2669a5dc8afSAndrew Lunn int genphy_c45_an_config_aneg(struct phy_device *phydev)
2679a5dc8afSAndrew Lunn {
268c24a34f5SOleksij Rempel 	int changed = 0, ret;
2699a5dc8afSAndrew Lunn 	u32 adv;
2709a5dc8afSAndrew Lunn 
2719a5dc8afSAndrew Lunn 	linkmode_and(phydev->advertising, phydev->advertising,
2729a5dc8afSAndrew Lunn 		     phydev->supported);
2739a5dc8afSAndrew Lunn 
274b6478b8cSOleksij Rempel 	ret = genphy_c45_an_config_eee_aneg(phydev);
2755827b168SOleksij Rempel 	if (ret < 0)
2765827b168SOleksij Rempel 		return ret;
2775827b168SOleksij Rempel 	else if (ret)
2785827b168SOleksij Rempel 		changed = true;
279cc429d52SHeiner Kallweit 
2803da8ffd8SAlexandru Tachici 	if (genphy_c45_baset1_able(phydev))
2813da8ffd8SAlexandru Tachici 		return genphy_c45_baset1_an_config_aneg(phydev);
2823da8ffd8SAlexandru Tachici 
2839a5dc8afSAndrew Lunn 	adv = linkmode_adv_to_mii_adv_t(phydev->advertising);
2849a5dc8afSAndrew Lunn 
2859731ea06SHeiner Kallweit 	ret = phy_modify_mmd_changed(phydev, MDIO_MMD_AN, MDIO_AN_ADVERTISE,
2869a5dc8afSAndrew Lunn 				     ADVERTISE_ALL | ADVERTISE_100BASE4 |
2879a5dc8afSAndrew Lunn 				     ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM,
2889a5dc8afSAndrew Lunn 				     adv);
2899a5dc8afSAndrew Lunn 	if (ret < 0)
2909a5dc8afSAndrew Lunn 		return ret;
2919a5dc8afSAndrew Lunn 	if (ret > 0)
2929a5dc8afSAndrew Lunn 		changed = 1;
2939a5dc8afSAndrew Lunn 
2949a5dc8afSAndrew Lunn 	adv = linkmode_adv_to_mii_10gbt_adv_t(phydev->advertising);
2959a5dc8afSAndrew Lunn 
2969731ea06SHeiner Kallweit 	ret = phy_modify_mmd_changed(phydev, MDIO_MMD_AN, MDIO_AN_10GBT_CTRL,
2979a5dc8afSAndrew Lunn 				     MDIO_AN_10GBT_CTRL_ADV10G |
2989a5dc8afSAndrew Lunn 				     MDIO_AN_10GBT_CTRL_ADV5G |
2999731ea06SHeiner Kallweit 				     MDIO_AN_10GBT_CTRL_ADV2_5G, adv);
3009a5dc8afSAndrew Lunn 	if (ret < 0)
3019a5dc8afSAndrew Lunn 		return ret;
3029a5dc8afSAndrew Lunn 	if (ret > 0)
3039a5dc8afSAndrew Lunn 		changed = 1;
3049a5dc8afSAndrew Lunn 
3059a5dc8afSAndrew Lunn 	return changed;
3069a5dc8afSAndrew Lunn }
3079a5dc8afSAndrew Lunn EXPORT_SYMBOL_GPL(genphy_c45_an_config_aneg);
3089a5dc8afSAndrew Lunn 
3099a5dc8afSAndrew Lunn /**
3105acde34aSRussell King  * genphy_c45_an_disable_aneg - disable auto-negotiation
3115acde34aSRussell King  * @phydev: target phy_device struct
3125acde34aSRussell King  *
3135acde34aSRussell King  * Disable auto-negotiation in the Clause 45 PHY. The link parameters
314e1f82127SWenpeng Liang  * are controlled through the PMA/PMD MMD registers.
3155acde34aSRussell King  *
3165acde34aSRussell King  * Returns zero on success, negative errno code on failure.
3175acde34aSRussell King  */
genphy_c45_an_disable_aneg(struct phy_device * phydev)3185acde34aSRussell King int genphy_c45_an_disable_aneg(struct phy_device *phydev)
3195acde34aSRussell King {
3203da8ffd8SAlexandru Tachici 	u16 reg = MDIO_CTRL1;
3215acde34aSRussell King 
3223da8ffd8SAlexandru Tachici 	if (genphy_c45_baset1_able(phydev))
3233da8ffd8SAlexandru Tachici 		reg = MDIO_AN_T1_CTRL;
3243da8ffd8SAlexandru Tachici 
3253da8ffd8SAlexandru Tachici 	return phy_clear_bits_mmd(phydev, MDIO_MMD_AN, reg,
326b52c018dSHeiner Kallweit 				  MDIO_AN_CTRL1_ENABLE | MDIO_AN_CTRL1_RESTART);
3275acde34aSRussell King }
3285acde34aSRussell King EXPORT_SYMBOL_GPL(genphy_c45_an_disable_aneg);
3295acde34aSRussell King 
3305acde34aSRussell King /**
3315acde34aSRussell King  * genphy_c45_restart_aneg - Enable and restart auto-negotiation
3325acde34aSRussell King  * @phydev: target phy_device struct
3335acde34aSRussell King  *
3345acde34aSRussell King  * This assumes that the auto-negotiation MMD is present.
3355acde34aSRussell King  *
3365acde34aSRussell King  * Enable and restart auto-negotiation.
3375acde34aSRussell King  */
genphy_c45_restart_aneg(struct phy_device * phydev)3385acde34aSRussell King int genphy_c45_restart_aneg(struct phy_device *phydev)
3395acde34aSRussell King {
3403da8ffd8SAlexandru Tachici 	u16 reg = MDIO_CTRL1;
3413da8ffd8SAlexandru Tachici 
3423da8ffd8SAlexandru Tachici 	if (genphy_c45_baset1_able(phydev))
3433da8ffd8SAlexandru Tachici 		reg = MDIO_AN_T1_CTRL;
3443da8ffd8SAlexandru Tachici 
3453da8ffd8SAlexandru Tachici 	return phy_set_bits_mmd(phydev, MDIO_MMD_AN, reg,
346b52c018dSHeiner Kallweit 				MDIO_AN_CTRL1_ENABLE | MDIO_AN_CTRL1_RESTART);
3475acde34aSRussell King }
3485acde34aSRussell King EXPORT_SYMBOL_GPL(genphy_c45_restart_aneg);
3495acde34aSRussell King 
3505acde34aSRussell King /**
3511af9f168SHeiner Kallweit  * genphy_c45_check_and_restart_aneg - Enable and restart auto-negotiation
3521af9f168SHeiner Kallweit  * @phydev: target phy_device struct
3531af9f168SHeiner Kallweit  * @restart: whether aneg restart is requested
3541af9f168SHeiner Kallweit  *
3551af9f168SHeiner Kallweit  * This assumes that the auto-negotiation MMD is present.
3561af9f168SHeiner Kallweit  *
3571af9f168SHeiner Kallweit  * Check, and restart auto-negotiation if needed.
3581af9f168SHeiner Kallweit  */
genphy_c45_check_and_restart_aneg(struct phy_device * phydev,bool restart)3591af9f168SHeiner Kallweit int genphy_c45_check_and_restart_aneg(struct phy_device *phydev, bool restart)
3601af9f168SHeiner Kallweit {
3613da8ffd8SAlexandru Tachici 	u16 reg = MDIO_CTRL1;
3624f31c532SSudheesh Mavila 	int ret;
3631af9f168SHeiner Kallweit 
3643da8ffd8SAlexandru Tachici 	if (genphy_c45_baset1_able(phydev))
3653da8ffd8SAlexandru Tachici 		reg = MDIO_AN_T1_CTRL;
3663da8ffd8SAlexandru Tachici 
3671af9f168SHeiner Kallweit 	if (!restart) {
3681af9f168SHeiner Kallweit 		/* Configure and restart aneg if it wasn't set before */
3693da8ffd8SAlexandru Tachici 		ret = phy_read_mmd(phydev, MDIO_MMD_AN, reg);
3701af9f168SHeiner Kallweit 		if (ret < 0)
3711af9f168SHeiner Kallweit 			return ret;
3721af9f168SHeiner Kallweit 
3731af9f168SHeiner Kallweit 		if (!(ret & MDIO_AN_CTRL1_ENABLE))
3741af9f168SHeiner Kallweit 			restart = true;
3751af9f168SHeiner Kallweit 	}
3761af9f168SHeiner Kallweit 
3771af9f168SHeiner Kallweit 	if (restart)
3784f31c532SSudheesh Mavila 		return genphy_c45_restart_aneg(phydev);
3791af9f168SHeiner Kallweit 
3804f31c532SSudheesh Mavila 	return 0;
3811af9f168SHeiner Kallweit }
3821af9f168SHeiner Kallweit EXPORT_SYMBOL_GPL(genphy_c45_check_and_restart_aneg);
3831af9f168SHeiner Kallweit 
3841af9f168SHeiner Kallweit /**
3855acde34aSRussell King  * genphy_c45_aneg_done - return auto-negotiation complete status
3865acde34aSRussell King  * @phydev: target phy_device struct
3875acde34aSRussell King  *
3885acde34aSRussell King  * This assumes that the auto-negotiation MMD is present.
3895acde34aSRussell King  *
3905acde34aSRussell King  * Reads the status register from the auto-negotiation MMD, returning:
3915acde34aSRussell King  * - positive if auto-negotiation is complete
3925acde34aSRussell King  * - negative errno code on error
3935acde34aSRussell King  * - zero otherwise
3945acde34aSRussell King  */
genphy_c45_aneg_done(struct phy_device * phydev)3955acde34aSRussell King int genphy_c45_aneg_done(struct phy_device *phydev)
3965acde34aSRussell King {
3973da8ffd8SAlexandru Tachici 	int reg = MDIO_STAT1;
3983da8ffd8SAlexandru Tachici 	int val;
3993da8ffd8SAlexandru Tachici 
4003da8ffd8SAlexandru Tachici 	if (genphy_c45_baset1_able(phydev))
4013da8ffd8SAlexandru Tachici 		reg = MDIO_AN_T1_STAT;
4023da8ffd8SAlexandru Tachici 
4033da8ffd8SAlexandru Tachici 	val = phy_read_mmd(phydev, MDIO_MMD_AN, reg);
4045acde34aSRussell King 
4055acde34aSRussell King 	return val < 0 ? val : val & MDIO_AN_STAT1_COMPLETE ? 1 : 0;
4065acde34aSRussell King }
4075acde34aSRussell King EXPORT_SYMBOL_GPL(genphy_c45_aneg_done);
4085acde34aSRussell King 
4095acde34aSRussell King /**
4105acde34aSRussell King  * genphy_c45_read_link - read the overall link status from the MMDs
4115acde34aSRussell King  * @phydev: target phy_device struct
4125acde34aSRussell King  *
4135acde34aSRussell King  * Read the link status from the specified MMDs, and if they all indicate
414a6e11f6bSHeiner Kallweit  * that the link is up, set phydev->link to 1.  If an error is encountered,
4155acde34aSRussell King  * a negative errno will be returned, otherwise zero.
4165acde34aSRussell King  */
genphy_c45_read_link(struct phy_device * phydev)417998a8a83SHeiner Kallweit int genphy_c45_read_link(struct phy_device *phydev)
4185acde34aSRussell King {
419c1164bb1SHeiner Kallweit 	u32 mmd_mask = MDIO_DEVS_PMAPMD;
4205acde34aSRussell King 	int val, devad;
4215acde34aSRussell King 	bool link = true;
4225acde34aSRussell King 
423320ed3bfSRussell King 	if (phydev->c45_ids.mmds_present & MDIO_DEVS_AN) {
424c36757ebSHeiner Kallweit 		val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_CTRL1);
425c36757ebSHeiner Kallweit 		if (val < 0)
426c36757ebSHeiner Kallweit 			return val;
427c36757ebSHeiner Kallweit 
428c36757ebSHeiner Kallweit 		/* Autoneg is being started, therefore disregard current
429c36757ebSHeiner Kallweit 		 * link status and report link as down.
430c36757ebSHeiner Kallweit 		 */
431c36757ebSHeiner Kallweit 		if (val & MDIO_AN_CTRL1_RESTART) {
432c36757ebSHeiner Kallweit 			phydev->link = 0;
433c36757ebSHeiner Kallweit 			return 0;
434c36757ebSHeiner Kallweit 		}
435c36757ebSHeiner Kallweit 	}
436c36757ebSHeiner Kallweit 
437a6e11f6bSHeiner Kallweit 	while (mmd_mask && link) {
4385acde34aSRussell King 		devad = __ffs(mmd_mask);
4395acde34aSRussell King 		mmd_mask &= ~BIT(devad);
4405acde34aSRussell King 
4415acde34aSRussell King 		/* The link state is latched low so that momentary link
4425acde34aSRussell King 		 * drops can be detected. Do not double-read the status
443e96bd2d3SPetr Oros 		 * in polling mode to detect such short link drops except
444e96bd2d3SPetr Oros 		 * the link was already down.
4455acde34aSRussell King 		 */
446e96bd2d3SPetr Oros 		if (!phy_polling_mode(phydev) || !phydev->link) {
44793c09704SHeiner Kallweit 			val = phy_read_mmd(phydev, devad, MDIO_STAT1);
44893c09704SHeiner Kallweit 			if (val < 0)
44993c09704SHeiner Kallweit 				return val;
450c397ab21SHeiner Kallweit 			else if (val & MDIO_STAT1_LSTATUS)
451c397ab21SHeiner Kallweit 				continue;
45293c09704SHeiner Kallweit 		}
45393c09704SHeiner Kallweit 
4545acde34aSRussell King 		val = phy_read_mmd(phydev, devad, MDIO_STAT1);
4555acde34aSRussell King 		if (val < 0)
4565acde34aSRussell King 			return val;
4575acde34aSRussell King 
4585acde34aSRussell King 		if (!(val & MDIO_STAT1_LSTATUS))
4595acde34aSRussell King 			link = false;
4605acde34aSRussell King 	}
4615acde34aSRussell King 
462a6e11f6bSHeiner Kallweit 	phydev->link = link;
463a6e11f6bSHeiner Kallweit 
464a6e11f6bSHeiner Kallweit 	return 0;
4655acde34aSRussell King }
4665acde34aSRussell King EXPORT_SYMBOL_GPL(genphy_c45_read_link);
4675acde34aSRussell King 
4683da8ffd8SAlexandru Tachici /* Read the Clause 45 defined BASE-T1 AN (7.513) status register to check
4693da8ffd8SAlexandru Tachici  * if autoneg is complete. If so read the BASE-T1 Autonegotiation
4703da8ffd8SAlexandru Tachici  * Advertisement registers filling in the link partner advertisement,
4713da8ffd8SAlexandru Tachici  * pause and asym_pause members in phydev.
4723da8ffd8SAlexandru Tachici  */
genphy_c45_baset1_read_lpa(struct phy_device * phydev)4733da8ffd8SAlexandru Tachici static int genphy_c45_baset1_read_lpa(struct phy_device *phydev)
4743da8ffd8SAlexandru Tachici {
4753da8ffd8SAlexandru Tachici 	int val;
4763da8ffd8SAlexandru Tachici 
4773da8ffd8SAlexandru Tachici 	val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_T1_STAT);
4783da8ffd8SAlexandru Tachici 	if (val < 0)
4793da8ffd8SAlexandru Tachici 		return val;
4803da8ffd8SAlexandru Tachici 
4813da8ffd8SAlexandru Tachici 	if (!(val & MDIO_AN_STAT1_COMPLETE)) {
4823da8ffd8SAlexandru Tachici 		linkmode_clear_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, phydev->lp_advertising);
4833da8ffd8SAlexandru Tachici 		mii_t1_adv_l_mod_linkmode_t(phydev->lp_advertising, 0);
4843da8ffd8SAlexandru Tachici 		mii_t1_adv_m_mod_linkmode_t(phydev->lp_advertising, 0);
4853da8ffd8SAlexandru Tachici 
4863da8ffd8SAlexandru Tachici 		phydev->pause = 0;
4873da8ffd8SAlexandru Tachici 		phydev->asym_pause = 0;
4883da8ffd8SAlexandru Tachici 
4893da8ffd8SAlexandru Tachici 		return 0;
4903da8ffd8SAlexandru Tachici 	}
4913da8ffd8SAlexandru Tachici 
4923da8ffd8SAlexandru Tachici 	linkmode_mod_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, phydev->lp_advertising, 1);
4933da8ffd8SAlexandru Tachici 
4943da8ffd8SAlexandru Tachici 	val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_T1_LP_L);
4953da8ffd8SAlexandru Tachici 	if (val < 0)
4963da8ffd8SAlexandru Tachici 		return val;
4973da8ffd8SAlexandru Tachici 
4983da8ffd8SAlexandru Tachici 	mii_t1_adv_l_mod_linkmode_t(phydev->lp_advertising, val);
4993da8ffd8SAlexandru Tachici 	phydev->pause = val & MDIO_AN_T1_ADV_L_PAUSE_CAP ? 1 : 0;
5003da8ffd8SAlexandru Tachici 	phydev->asym_pause = val & MDIO_AN_T1_ADV_L_PAUSE_ASYM ? 1 : 0;
5013da8ffd8SAlexandru Tachici 
5023da8ffd8SAlexandru Tachici 	val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_T1_LP_M);
5033da8ffd8SAlexandru Tachici 	if (val < 0)
5043da8ffd8SAlexandru Tachici 		return val;
5053da8ffd8SAlexandru Tachici 
5063da8ffd8SAlexandru Tachici 	mii_t1_adv_m_mod_linkmode_t(phydev->lp_advertising, val);
5073da8ffd8SAlexandru Tachici 
5083da8ffd8SAlexandru Tachici 	return 0;
5093da8ffd8SAlexandru Tachici }
5103da8ffd8SAlexandru Tachici 
5115acde34aSRussell King /**
512cc1122b0SColin Ian King  * genphy_c45_read_lpa - read the link partner advertisement and pause
5135acde34aSRussell King  * @phydev: target phy_device struct
5145acde34aSRussell King  *
5155acde34aSRussell King  * Read the Clause 45 defined base (7.19) and 10G (7.33) status registers,
516cc1122b0SColin Ian King  * filling in the link partner advertisement, pause and asym_pause members
5175acde34aSRussell King  * in @phydev.  This assumes that the auto-negotiation MMD is present, and
5185acde34aSRussell King  * the backplane bit (7.48.0) is clear.  Clause 45 PHY drivers are expected
5195acde34aSRussell King  * to fill in the remainder of the link partner advert from vendor registers.
5205acde34aSRussell King  */
genphy_c45_read_lpa(struct phy_device * phydev)5215acde34aSRussell King int genphy_c45_read_lpa(struct phy_device *phydev)
5225acde34aSRussell King {
5235acde34aSRussell King 	int val;
5245acde34aSRussell King 
5253da8ffd8SAlexandru Tachici 	if (genphy_c45_baset1_able(phydev))
5263da8ffd8SAlexandru Tachici 		return genphy_c45_baset1_read_lpa(phydev);
5273da8ffd8SAlexandru Tachici 
5285d237a07SHeiner Kallweit 	val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_STAT1);
5295d237a07SHeiner Kallweit 	if (val < 0)
5305d237a07SHeiner Kallweit 		return val;
5315d237a07SHeiner Kallweit 
532372fcc1bSHeiner Kallweit 	if (!(val & MDIO_AN_STAT1_COMPLETE)) {
533372fcc1bSHeiner Kallweit 		linkmode_clear_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
534372fcc1bSHeiner Kallweit 				   phydev->lp_advertising);
535372fcc1bSHeiner Kallweit 		mii_10gbt_stat_mod_linkmode_lpa_t(phydev->lp_advertising, 0);
536372fcc1bSHeiner Kallweit 		mii_adv_mod_linkmode_adv_t(phydev->lp_advertising, 0);
537372fcc1bSHeiner Kallweit 		phydev->pause = 0;
538372fcc1bSHeiner Kallweit 		phydev->asym_pause = 0;
539372fcc1bSHeiner Kallweit 
540372fcc1bSHeiner Kallweit 		return 0;
541372fcc1bSHeiner Kallweit 	}
542372fcc1bSHeiner Kallweit 
5435d237a07SHeiner Kallweit 	linkmode_mod_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, phydev->lp_advertising,
5445d237a07SHeiner Kallweit 			 val & MDIO_AN_STAT1_LPABLE);
5455d237a07SHeiner Kallweit 
546cc1122b0SColin Ian King 	/* Read the link partner's base page advertisement */
5475acde34aSRussell King 	val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_LPA);
5485acde34aSRussell King 	if (val < 0)
5495acde34aSRussell King 		return val;
5505acde34aSRussell King 
5515d237a07SHeiner Kallweit 	mii_adv_mod_linkmode_adv_t(phydev->lp_advertising, val);
5525acde34aSRussell King 	phydev->pause = val & LPA_PAUSE_CAP ? 1 : 0;
5535acde34aSRussell King 	phydev->asym_pause = val & LPA_PAUSE_ASYM ? 1 : 0;
5545acde34aSRussell King 
555cc1122b0SColin Ian King 	/* Read the link partner's 10G advertisement */
5565acde34aSRussell King 	val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_10GBT_STAT);
5575acde34aSRussell King 	if (val < 0)
5585acde34aSRussell King 		return val;
5595acde34aSRussell King 
56096c2be34SHeiner Kallweit 	mii_10gbt_stat_mod_linkmode_lpa_t(phydev->lp_advertising, val);
5615acde34aSRussell King 
5625acde34aSRussell King 	return 0;
5635acde34aSRussell King }
5645acde34aSRussell King EXPORT_SYMBOL_GPL(genphy_c45_read_lpa);
5655acde34aSRussell King 
5665acde34aSRussell King /**
567b9a366f3SOleksij Rempel  * genphy_c45_pma_baset1_read_master_slave - read forced master/slave
568b9a366f3SOleksij Rempel  * configuration
569b9a366f3SOleksij Rempel  * @phydev: target phy_device struct
570b9a366f3SOleksij Rempel  */
genphy_c45_pma_baset1_read_master_slave(struct phy_device * phydev)571b9a366f3SOleksij Rempel int genphy_c45_pma_baset1_read_master_slave(struct phy_device *phydev)
572b9a366f3SOleksij Rempel {
573b9a366f3SOleksij Rempel 	int val;
574b9a366f3SOleksij Rempel 
575b9a366f3SOleksij Rempel 	phydev->master_slave_state = MASTER_SLAVE_STATE_UNKNOWN;
576acb8c5aeSOleksij Rempel 	phydev->master_slave_get = MASTER_SLAVE_CFG_UNKNOWN;
577b9a366f3SOleksij Rempel 
578b9a366f3SOleksij Rempel 	val = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_PMA_PMD_BT1_CTRL);
579b9a366f3SOleksij Rempel 	if (val < 0)
580b9a366f3SOleksij Rempel 		return val;
581b9a366f3SOleksij Rempel 
582acb8c5aeSOleksij Rempel 	if (val & MDIO_PMA_PMD_BT1_CTRL_CFG_MST) {
583acb8c5aeSOleksij Rempel 		phydev->master_slave_get = MASTER_SLAVE_CFG_MASTER_FORCE;
584b9a366f3SOleksij Rempel 		phydev->master_slave_state = MASTER_SLAVE_STATE_MASTER;
585acb8c5aeSOleksij Rempel 	} else {
586acb8c5aeSOleksij Rempel 		phydev->master_slave_get = MASTER_SLAVE_CFG_SLAVE_FORCE;
587b9a366f3SOleksij Rempel 		phydev->master_slave_state = MASTER_SLAVE_STATE_SLAVE;
588acb8c5aeSOleksij Rempel 	}
589b9a366f3SOleksij Rempel 
590b9a366f3SOleksij Rempel 	return 0;
591b9a366f3SOleksij Rempel }
592b9a366f3SOleksij Rempel EXPORT_SYMBOL_GPL(genphy_c45_pma_baset1_read_master_slave);
593b9a366f3SOleksij Rempel 
594b9a366f3SOleksij Rempel /**
5955acde34aSRussell King  * genphy_c45_read_pma - read link speed etc from PMA
5965acde34aSRussell King  * @phydev: target phy_device struct
5975acde34aSRussell King  */
genphy_c45_read_pma(struct phy_device * phydev)5985acde34aSRussell King int genphy_c45_read_pma(struct phy_device *phydev)
5995acde34aSRussell King {
6005acde34aSRussell King 	int val;
6015acde34aSRussell King 
6023de5ae54SYonglong Liu 	linkmode_zero(phydev->lp_advertising);
6033de5ae54SYonglong Liu 
6045acde34aSRussell King 	val = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_CTRL1);
6055acde34aSRussell King 	if (val < 0)
6065acde34aSRussell King 		return val;
6075acde34aSRussell King 
6085acde34aSRussell King 	switch (val & MDIO_CTRL1_SPEEDSEL) {
6095acde34aSRussell King 	case 0:
6105acde34aSRussell King 		phydev->speed = SPEED_10;
6115acde34aSRussell King 		break;
6125acde34aSRussell King 	case MDIO_PMA_CTRL1_SPEED100:
6135acde34aSRussell King 		phydev->speed = SPEED_100;
6145acde34aSRussell King 		break;
6155acde34aSRussell King 	case MDIO_PMA_CTRL1_SPEED1000:
6165acde34aSRussell King 		phydev->speed = SPEED_1000;
6175acde34aSRussell King 		break;
6187fd8afa8SMaxime Chevallier 	case MDIO_CTRL1_SPEED2_5G:
6197fd8afa8SMaxime Chevallier 		phydev->speed = SPEED_2500;
6207fd8afa8SMaxime Chevallier 		break;
6217fd8afa8SMaxime Chevallier 	case MDIO_CTRL1_SPEED5G:
6227fd8afa8SMaxime Chevallier 		phydev->speed = SPEED_5000;
6237fd8afa8SMaxime Chevallier 		break;
6245acde34aSRussell King 	case MDIO_CTRL1_SPEED10G:
6255acde34aSRussell King 		phydev->speed = SPEED_10000;
6265acde34aSRussell King 		break;
6275acde34aSRussell King 	default:
6285acde34aSRussell King 		phydev->speed = SPEED_UNKNOWN;
6295acde34aSRussell King 		break;
6305acde34aSRussell King 	}
6315acde34aSRussell King 
6325acde34aSRussell King 	phydev->duplex = DUPLEX_FULL;
6335acde34aSRussell King 
6343da8ffd8SAlexandru Tachici 	if (genphy_c45_baset1_able(phydev)) {
635b9a366f3SOleksij Rempel 		val = genphy_c45_pma_baset1_read_master_slave(phydev);
6363da8ffd8SAlexandru Tachici 		if (val < 0)
6373da8ffd8SAlexandru Tachici 			return val;
6383da8ffd8SAlexandru Tachici 	}
6393da8ffd8SAlexandru Tachici 
6405acde34aSRussell King 	return 0;
6415acde34aSRussell King }
6425acde34aSRussell King EXPORT_SYMBOL_GPL(genphy_c45_read_pma);
643921690f2SRussell King 
644ea4efe25SRussell King /**
645ea4efe25SRussell King  * genphy_c45_read_mdix - read mdix status from PMA
646ea4efe25SRussell King  * @phydev: target phy_device struct
647ea4efe25SRussell King  */
genphy_c45_read_mdix(struct phy_device * phydev)648ea4efe25SRussell King int genphy_c45_read_mdix(struct phy_device *phydev)
649ea4efe25SRussell King {
650ea4efe25SRussell King 	int val;
651ea4efe25SRussell King 
652ea4efe25SRussell King 	if (phydev->speed == SPEED_10000) {
653ea4efe25SRussell King 		val = phy_read_mmd(phydev, MDIO_MMD_PMAPMD,
654ea4efe25SRussell King 				   MDIO_PMA_10GBT_SWAPPOL);
655ea4efe25SRussell King 		if (val < 0)
656ea4efe25SRussell King 			return val;
657ea4efe25SRussell King 
658ea4efe25SRussell King 		switch (val) {
659ea4efe25SRussell King 		case MDIO_PMA_10GBT_SWAPPOL_ABNX | MDIO_PMA_10GBT_SWAPPOL_CDNX:
660ea4efe25SRussell King 			phydev->mdix = ETH_TP_MDI;
661ea4efe25SRussell King 			break;
662ea4efe25SRussell King 
663ea4efe25SRussell King 		case 0:
664ea4efe25SRussell King 			phydev->mdix = ETH_TP_MDI_X;
665ea4efe25SRussell King 			break;
666ea4efe25SRussell King 
667ea4efe25SRussell King 		default:
668ea4efe25SRussell King 			phydev->mdix = ETH_TP_MDI_INVALID;
669ea4efe25SRussell King 			break;
670ea4efe25SRussell King 		}
671ea4efe25SRussell King 	}
672ea4efe25SRussell King 
673ea4efe25SRussell King 	return 0;
674ea4efe25SRussell King }
675ea4efe25SRussell King EXPORT_SYMBOL_GPL(genphy_c45_read_mdix);
676ea4efe25SRussell King 
677ac3f5533SMaxime Chevallier /**
678022c3f87SOleksij Rempel  * genphy_c45_write_eee_adv - write advertised EEE link modes
679022c3f87SOleksij Rempel  * @phydev: target phy_device struct
680022c3f87SOleksij Rempel  * @adv: the linkmode advertisement settings
681022c3f87SOleksij Rempel  */
genphy_c45_write_eee_adv(struct phy_device * phydev,unsigned long * adv)682022c3f87SOleksij Rempel int genphy_c45_write_eee_adv(struct phy_device *phydev, unsigned long *adv)
683022c3f87SOleksij Rempel {
684972074eaSOleksij Rempel 	int val, changed = 0;
685022c3f87SOleksij Rempel 
686e209519bSOleksij Rempel 	if (linkmode_intersects(phydev->supported_eee, PHY_EEE_CAP1_FEATURES)) {
687022c3f87SOleksij Rempel 		val = linkmode_to_mii_eee_cap1_t(adv);
688022c3f87SOleksij Rempel 
689022c3f87SOleksij Rempel 		/* In eee_broken_modes are stored MDIO_AN_EEE_ADV specific raw
690022c3f87SOleksij Rempel 		 * register values.
691022c3f87SOleksij Rempel 		 */
692022c3f87SOleksij Rempel 		val &= ~phydev->eee_broken_modes;
693022c3f87SOleksij Rempel 
694022c3f87SOleksij Rempel 		/* IEEE 802.3-2018 45.2.7.13 EEE advertisement 1
695022c3f87SOleksij Rempel 		 * (Register 7.60)
696022c3f87SOleksij Rempel 		 */
697022c3f87SOleksij Rempel 		val = phy_modify_mmd_changed(phydev, MDIO_MMD_AN,
698022c3f87SOleksij Rempel 					     MDIO_AN_EEE_ADV,
699022c3f87SOleksij Rempel 					     MDIO_EEE_100TX | MDIO_EEE_1000T |
700022c3f87SOleksij Rempel 					     MDIO_EEE_10GT | MDIO_EEE_1000KX |
701022c3f87SOleksij Rempel 					     MDIO_EEE_10GKX4 | MDIO_EEE_10GKR,
702022c3f87SOleksij Rempel 					     val);
703022c3f87SOleksij Rempel 		if (val < 0)
704022c3f87SOleksij Rempel 			return val;
705022c3f87SOleksij Rempel 		if (val > 0)
706022c3f87SOleksij Rempel 			changed = 1;
707022c3f87SOleksij Rempel 	}
708022c3f87SOleksij Rempel 
709022c3f87SOleksij Rempel 	if (linkmode_test_bit(ETHTOOL_LINK_MODE_10baseT1L_Full_BIT,
710022c3f87SOleksij Rempel 			      phydev->supported_eee)) {
711022c3f87SOleksij Rempel 		val = linkmode_adv_to_mii_10base_t1_t(adv);
712022c3f87SOleksij Rempel 		/* IEEE 802.3cg-2019 45.2.7.25 10BASE-T1 AN control register
713022c3f87SOleksij Rempel 		 * (Register 7.526)
714022c3f87SOleksij Rempel 		 */
715022c3f87SOleksij Rempel 		val = phy_modify_mmd_changed(phydev, MDIO_MMD_AN,
716022c3f87SOleksij Rempel 					     MDIO_AN_10BT1_AN_CTRL,
717022c3f87SOleksij Rempel 					     MDIO_AN_10BT1_AN_CTRL_ADV_EEE_T1L,
718022c3f87SOleksij Rempel 					     val);
719022c3f87SOleksij Rempel 		if (val < 0)
720022c3f87SOleksij Rempel 			return val;
721022c3f87SOleksij Rempel 		if (val > 0)
722022c3f87SOleksij Rempel 			changed = 1;
723022c3f87SOleksij Rempel 	}
724022c3f87SOleksij Rempel 
725022c3f87SOleksij Rempel 	return changed;
726022c3f87SOleksij Rempel }
727022c3f87SOleksij Rempel 
728022c3f87SOleksij Rempel /**
729022c3f87SOleksij Rempel  * genphy_c45_read_eee_adv - read advertised EEE link modes
730022c3f87SOleksij Rempel  * @phydev: target phy_device struct
731022c3f87SOleksij Rempel  * @adv: the linkmode advertisement status
732022c3f87SOleksij Rempel  */
genphy_c45_read_eee_adv(struct phy_device * phydev,unsigned long * adv)7333eeca4e1SOleksij Rempel int genphy_c45_read_eee_adv(struct phy_device *phydev, unsigned long *adv)
734022c3f87SOleksij Rempel {
735022c3f87SOleksij Rempel 	int val;
736022c3f87SOleksij Rempel 
737e209519bSOleksij Rempel 	if (linkmode_intersects(phydev->supported_eee, PHY_EEE_CAP1_FEATURES)) {
738022c3f87SOleksij Rempel 		/* IEEE 802.3-2018 45.2.7.13 EEE advertisement 1
739022c3f87SOleksij Rempel 		 * (Register 7.60)
740022c3f87SOleksij Rempel 		 */
741022c3f87SOleksij Rempel 		val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_ADV);
742022c3f87SOleksij Rempel 		if (val < 0)
743022c3f87SOleksij Rempel 			return val;
744022c3f87SOleksij Rempel 
745022c3f87SOleksij Rempel 		mii_eee_cap1_mod_linkmode_t(adv, val);
746022c3f87SOleksij Rempel 	}
747022c3f87SOleksij Rempel 
748022c3f87SOleksij Rempel 	if (linkmode_test_bit(ETHTOOL_LINK_MODE_10baseT1L_Full_BIT,
749022c3f87SOleksij Rempel 			      phydev->supported_eee)) {
750022c3f87SOleksij Rempel 		/* IEEE 802.3cg-2019 45.2.7.25 10BASE-T1 AN control register
751022c3f87SOleksij Rempel 		 * (Register 7.526)
752022c3f87SOleksij Rempel 		 */
753022c3f87SOleksij Rempel 		val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_10BT1_AN_CTRL);
754022c3f87SOleksij Rempel 		if (val < 0)
755022c3f87SOleksij Rempel 			return val;
756022c3f87SOleksij Rempel 
757022c3f87SOleksij Rempel 		mii_10base_t1_adv_mod_linkmode_t(adv, val);
758022c3f87SOleksij Rempel 	}
759022c3f87SOleksij Rempel 
760022c3f87SOleksij Rempel 	return 0;
761022c3f87SOleksij Rempel }
762022c3f87SOleksij Rempel 
763022c3f87SOleksij Rempel /**
764022c3f87SOleksij Rempel  * genphy_c45_read_eee_lpa - read advertised LP EEE link modes
765022c3f87SOleksij Rempel  * @phydev: target phy_device struct
766022c3f87SOleksij Rempel  * @lpa: the linkmode LP advertisement status
767022c3f87SOleksij Rempel  */
genphy_c45_read_eee_lpa(struct phy_device * phydev,unsigned long * lpa)768022c3f87SOleksij Rempel static int genphy_c45_read_eee_lpa(struct phy_device *phydev,
769022c3f87SOleksij Rempel 				   unsigned long *lpa)
770022c3f87SOleksij Rempel {
771022c3f87SOleksij Rempel 	int val;
772022c3f87SOleksij Rempel 
773e209519bSOleksij Rempel 	if (linkmode_intersects(phydev->supported_eee, PHY_EEE_CAP1_FEATURES)) {
774022c3f87SOleksij Rempel 		/* IEEE 802.3-2018 45.2.7.14 EEE link partner ability 1
775022c3f87SOleksij Rempel 		 * (Register 7.61)
776022c3f87SOleksij Rempel 		 */
777022c3f87SOleksij Rempel 		val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_LPABLE);
778022c3f87SOleksij Rempel 		if (val < 0)
779022c3f87SOleksij Rempel 			return val;
780022c3f87SOleksij Rempel 
781022c3f87SOleksij Rempel 		mii_eee_cap1_mod_linkmode_t(lpa, val);
782022c3f87SOleksij Rempel 	}
783022c3f87SOleksij Rempel 
784022c3f87SOleksij Rempel 	if (linkmode_test_bit(ETHTOOL_LINK_MODE_10baseT1L_Full_BIT,
785022c3f87SOleksij Rempel 			      phydev->supported_eee)) {
786022c3f87SOleksij Rempel 		/* IEEE 802.3cg-2019 45.2.7.26 10BASE-T1 AN status register
787022c3f87SOleksij Rempel 		 * (Register 7.527)
788022c3f87SOleksij Rempel 		 */
789022c3f87SOleksij Rempel 		val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_10BT1_AN_STAT);
790022c3f87SOleksij Rempel 		if (val < 0)
791022c3f87SOleksij Rempel 			return val;
792022c3f87SOleksij Rempel 
793022c3f87SOleksij Rempel 		mii_10base_t1_adv_mod_linkmode_t(lpa, val);
794022c3f87SOleksij Rempel 	}
795022c3f87SOleksij Rempel 
796022c3f87SOleksij Rempel 	return 0;
797022c3f87SOleksij Rempel }
798022c3f87SOleksij Rempel 
799022c3f87SOleksij Rempel /**
80014e47d1fSOleksij Rempel  * genphy_c45_read_eee_cap1 - read supported EEE link modes from register 3.20
80114e47d1fSOleksij Rempel  * @phydev: target phy_device struct
80214e47d1fSOleksij Rempel  */
genphy_c45_read_eee_cap1(struct phy_device * phydev)80314e47d1fSOleksij Rempel static int genphy_c45_read_eee_cap1(struct phy_device *phydev)
80414e47d1fSOleksij Rempel {
80514e47d1fSOleksij Rempel 	int val;
80614e47d1fSOleksij Rempel 
80714e47d1fSOleksij Rempel 	/* IEEE 802.3-2018 45.2.3.10 EEE control and capability 1
80814e47d1fSOleksij Rempel 	 * (Register 3.20)
80914e47d1fSOleksij Rempel 	 */
81014e47d1fSOleksij Rempel 	val = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_PCS_EEE_ABLE);
81114e47d1fSOleksij Rempel 	if (val < 0)
81214e47d1fSOleksij Rempel 		return val;
81314e47d1fSOleksij Rempel 
81414e47d1fSOleksij Rempel 	/* The 802.3 2018 standard says the top 2 bits are reserved and should
81514e47d1fSOleksij Rempel 	 * read as 0. Also, it seems unlikely anybody will build a PHY which
81614e47d1fSOleksij Rempel 	 * supports 100GBASE-R deep sleep all the way down to 100BASE-TX EEE.
81714e47d1fSOleksij Rempel 	 * If MDIO_PCS_EEE_ABLE is 0xffff assume EEE is not supported.
81814e47d1fSOleksij Rempel 	 */
81914e47d1fSOleksij Rempel 	if (val == 0xffff)
82014e47d1fSOleksij Rempel 		return 0;
82114e47d1fSOleksij Rempel 
82214e47d1fSOleksij Rempel 	mii_eee_cap1_mod_linkmode_t(phydev->supported_eee, val);
82314e47d1fSOleksij Rempel 
82414e47d1fSOleksij Rempel 	/* Some buggy devices indicate EEE link modes in MDIO_PCS_EEE_ABLE
82514e47d1fSOleksij Rempel 	 * which they don't support as indicated by BMSR, ESTATUS etc.
82614e47d1fSOleksij Rempel 	 */
82714e47d1fSOleksij Rempel 	linkmode_and(phydev->supported_eee, phydev->supported_eee,
82814e47d1fSOleksij Rempel 		     phydev->supported);
82914e47d1fSOleksij Rempel 
83014e47d1fSOleksij Rempel 	return 0;
83114e47d1fSOleksij Rempel }
83214e47d1fSOleksij Rempel 
83314e47d1fSOleksij Rempel /**
83414e47d1fSOleksij Rempel  * genphy_c45_read_eee_abilities - read supported EEE link modes
83514e47d1fSOleksij Rempel  * @phydev: target phy_device struct
83614e47d1fSOleksij Rempel  */
genphy_c45_read_eee_abilities(struct phy_device * phydev)83714e47d1fSOleksij Rempel int genphy_c45_read_eee_abilities(struct phy_device *phydev)
83814e47d1fSOleksij Rempel {
83914e47d1fSOleksij Rempel 	int val;
84014e47d1fSOleksij Rempel 
84114e47d1fSOleksij Rempel 	/* There is not indicator whether optional register
84214e47d1fSOleksij Rempel 	 * "EEE control and capability 1" (3.20) is supported. Read it only
84314e47d1fSOleksij Rempel 	 * on devices with appropriate linkmodes.
84414e47d1fSOleksij Rempel 	 */
84514e47d1fSOleksij Rempel 	if (linkmode_intersects(phydev->supported, PHY_EEE_CAP1_FEATURES)) {
84614e47d1fSOleksij Rempel 		val = genphy_c45_read_eee_cap1(phydev);
84714e47d1fSOleksij Rempel 		if (val)
84814e47d1fSOleksij Rempel 			return val;
84914e47d1fSOleksij Rempel 	}
85014e47d1fSOleksij Rempel 
85114e47d1fSOleksij Rempel 	if (linkmode_test_bit(ETHTOOL_LINK_MODE_10baseT1L_Full_BIT,
85214e47d1fSOleksij Rempel 			      phydev->supported)) {
85314e47d1fSOleksij Rempel 		/* IEEE 802.3cg-2019 45.2.1.186b 10BASE-T1L PMA status register
85414e47d1fSOleksij Rempel 		 * (Register 1.2295)
85514e47d1fSOleksij Rempel 		 */
85614e47d1fSOleksij Rempel 		val = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_PMA_10T1L_STAT);
85714e47d1fSOleksij Rempel 		if (val < 0)
85814e47d1fSOleksij Rempel 			return val;
85914e47d1fSOleksij Rempel 
86014e47d1fSOleksij Rempel 		linkmode_mod_bit(ETHTOOL_LINK_MODE_10baseT1L_Full_BIT,
86114e47d1fSOleksij Rempel 				 phydev->supported_eee,
86214e47d1fSOleksij Rempel 				 val & MDIO_PMA_10T1L_STAT_EEE);
86314e47d1fSOleksij Rempel 	}
86414e47d1fSOleksij Rempel 
86514e47d1fSOleksij Rempel 	return 0;
86614e47d1fSOleksij Rempel }
86714e47d1fSOleksij Rempel EXPORT_SYMBOL_GPL(genphy_c45_read_eee_abilities);
86814e47d1fSOleksij Rempel 
86914e47d1fSOleksij Rempel /**
870b6478b8cSOleksij Rempel  * genphy_c45_an_config_eee_aneg - configure EEE advertisement
871b6478b8cSOleksij Rempel  * @phydev: target phy_device struct
872b6478b8cSOleksij Rempel  */
genphy_c45_an_config_eee_aneg(struct phy_device * phydev)873b6478b8cSOleksij Rempel int genphy_c45_an_config_eee_aneg(struct phy_device *phydev)
874b6478b8cSOleksij Rempel {
8753eeca4e1SOleksij Rempel 	if (!phydev->eee_enabled) {
8763eeca4e1SOleksij Rempel 		__ETHTOOL_DECLARE_LINK_MODE_MASK(adv) = {};
8773eeca4e1SOleksij Rempel 
8783eeca4e1SOleksij Rempel 		return genphy_c45_write_eee_adv(phydev, adv);
8793eeca4e1SOleksij Rempel 	}
8803eeca4e1SOleksij Rempel 
8813eeca4e1SOleksij Rempel 	return genphy_c45_write_eee_adv(phydev, phydev->advertising_eee);
882b6478b8cSOleksij Rempel }
883b6478b8cSOleksij Rempel 
884b6478b8cSOleksij Rempel /**
885eba2e4c2SStefan Eichenberger  * genphy_c45_pma_baset1_read_abilities - read supported baset1 link modes from PMA
886eba2e4c2SStefan Eichenberger  * @phydev: target phy_device struct
887eba2e4c2SStefan Eichenberger  *
888eba2e4c2SStefan Eichenberger  * Read the supported link modes from the extended BASE-T1 ability register
889eba2e4c2SStefan Eichenberger  */
genphy_c45_pma_baset1_read_abilities(struct phy_device * phydev)890eba2e4c2SStefan Eichenberger int genphy_c45_pma_baset1_read_abilities(struct phy_device *phydev)
891eba2e4c2SStefan Eichenberger {
892eba2e4c2SStefan Eichenberger 	int val;
893eba2e4c2SStefan Eichenberger 
894eba2e4c2SStefan Eichenberger 	val = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_PMA_PMD_BT1);
895eba2e4c2SStefan Eichenberger 	if (val < 0)
896eba2e4c2SStefan Eichenberger 		return val;
897eba2e4c2SStefan Eichenberger 
898eba2e4c2SStefan Eichenberger 	linkmode_mod_bit(ETHTOOL_LINK_MODE_10baseT1L_Full_BIT,
899eba2e4c2SStefan Eichenberger 			 phydev->supported,
900eba2e4c2SStefan Eichenberger 			 val & MDIO_PMA_PMD_BT1_B10L_ABLE);
901eba2e4c2SStefan Eichenberger 
902*a60eb720SStefan Eichenberger 	linkmode_mod_bit(ETHTOOL_LINK_MODE_100baseT1_Full_BIT,
903*a60eb720SStefan Eichenberger 			 phydev->supported,
904*a60eb720SStefan Eichenberger 			 val & MDIO_PMA_PMD_BT1_B100_ABLE);
905*a60eb720SStefan Eichenberger 
906*a60eb720SStefan Eichenberger 	linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseT1_Full_BIT,
907*a60eb720SStefan Eichenberger 			 phydev->supported,
908*a60eb720SStefan Eichenberger 			 val & MDIO_PMA_PMD_BT1_B1000_ABLE);
909*a60eb720SStefan Eichenberger 
910eba2e4c2SStefan Eichenberger 	val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_T1_STAT);
911eba2e4c2SStefan Eichenberger 	if (val < 0)
912eba2e4c2SStefan Eichenberger 		return val;
913eba2e4c2SStefan Eichenberger 
914eba2e4c2SStefan Eichenberger 	linkmode_mod_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
915eba2e4c2SStefan Eichenberger 			 phydev->supported,
916eba2e4c2SStefan Eichenberger 			 val & MDIO_AN_STAT1_ABLE);
917eba2e4c2SStefan Eichenberger 
918eba2e4c2SStefan Eichenberger 	return 0;
919eba2e4c2SStefan Eichenberger }
920eba2e4c2SStefan Eichenberger EXPORT_SYMBOL_GPL(genphy_c45_pma_baset1_read_abilities);
921eba2e4c2SStefan Eichenberger 
922eba2e4c2SStefan Eichenberger /**
923ac3f5533SMaxime Chevallier  * genphy_c45_pma_read_abilities - read supported link modes from PMA
924ac3f5533SMaxime Chevallier  * @phydev: target phy_device struct
925ac3f5533SMaxime Chevallier  *
926ac3f5533SMaxime Chevallier  * Read the supported link modes from the PMA Status 2 (1.8) register. If bit
927ac3f5533SMaxime Chevallier  * 1.8.9 is set, the list of supported modes is build using the values in the
928ac3f5533SMaxime Chevallier  * PMA Extended Abilities (1.11) register, indicating 1000BASET an 10G related
929ac3f5533SMaxime Chevallier  * modes. If bit 1.11.14 is set, then the list is also extended with the modes
930ac3f5533SMaxime Chevallier  * in the 2.5G/5G PMA Extended register (1.21), indicating if 2.5GBASET and
931ac3f5533SMaxime Chevallier  * 5GBASET are supported.
932ac3f5533SMaxime Chevallier  */
genphy_c45_pma_read_abilities(struct phy_device * phydev)933ac3f5533SMaxime Chevallier int genphy_c45_pma_read_abilities(struct phy_device *phydev)
934ac3f5533SMaxime Chevallier {
935ac3f5533SMaxime Chevallier 	int val;
936ac3f5533SMaxime Chevallier 
937b6a4119dSHeiner Kallweit 	linkmode_clear_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, phydev->supported);
938320ed3bfSRussell King 	if (phydev->c45_ids.mmds_present & MDIO_DEVS_AN) {
939b6a4119dSHeiner Kallweit 		val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_STAT1);
940b6a4119dSHeiner Kallweit 		if (val < 0)
941b6a4119dSHeiner Kallweit 			return val;
942b6a4119dSHeiner Kallweit 
943b6a4119dSHeiner Kallweit 		if (val & MDIO_AN_STAT1_ABLE)
944b6a4119dSHeiner Kallweit 			linkmode_set_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
945b6a4119dSHeiner Kallweit 					 phydev->supported);
946b6a4119dSHeiner Kallweit 	}
947b6a4119dSHeiner Kallweit 
948ac3f5533SMaxime Chevallier 	val = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_STAT2);
949ac3f5533SMaxime Chevallier 	if (val < 0)
950ac3f5533SMaxime Chevallier 		return val;
951ac3f5533SMaxime Chevallier 
952ac3f5533SMaxime Chevallier 	linkmode_mod_bit(ETHTOOL_LINK_MODE_10000baseSR_Full_BIT,
953ac3f5533SMaxime Chevallier 			 phydev->supported,
954ac3f5533SMaxime Chevallier 			 val & MDIO_PMA_STAT2_10GBSR);
955ac3f5533SMaxime Chevallier 
956ac3f5533SMaxime Chevallier 	linkmode_mod_bit(ETHTOOL_LINK_MODE_10000baseLR_Full_BIT,
957ac3f5533SMaxime Chevallier 			 phydev->supported,
958ac3f5533SMaxime Chevallier 			 val & MDIO_PMA_STAT2_10GBLR);
959ac3f5533SMaxime Chevallier 
960ac3f5533SMaxime Chevallier 	linkmode_mod_bit(ETHTOOL_LINK_MODE_10000baseER_Full_BIT,
961ac3f5533SMaxime Chevallier 			 phydev->supported,
962ac3f5533SMaxime Chevallier 			 val & MDIO_PMA_STAT2_10GBER);
963ac3f5533SMaxime Chevallier 
964ac3f5533SMaxime Chevallier 	if (val & MDIO_PMA_STAT2_EXTABLE) {
965ac3f5533SMaxime Chevallier 		val = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_PMA_EXTABLE);
966ac3f5533SMaxime Chevallier 		if (val < 0)
967ac3f5533SMaxime Chevallier 			return val;
968ac3f5533SMaxime Chevallier 
969ac3f5533SMaxime Chevallier 		linkmode_mod_bit(ETHTOOL_LINK_MODE_10000baseLRM_Full_BIT,
970ac3f5533SMaxime Chevallier 				 phydev->supported,
971ac3f5533SMaxime Chevallier 				 val & MDIO_PMA_EXTABLE_10GBLRM);
972ac3f5533SMaxime Chevallier 		linkmode_mod_bit(ETHTOOL_LINK_MODE_10000baseT_Full_BIT,
973ac3f5533SMaxime Chevallier 				 phydev->supported,
974ac3f5533SMaxime Chevallier 				 val & MDIO_PMA_EXTABLE_10GBT);
975ac3f5533SMaxime Chevallier 		linkmode_mod_bit(ETHTOOL_LINK_MODE_10000baseKX4_Full_BIT,
976ac3f5533SMaxime Chevallier 				 phydev->supported,
977ac3f5533SMaxime Chevallier 				 val & MDIO_PMA_EXTABLE_10GBKX4);
978ac3f5533SMaxime Chevallier 		linkmode_mod_bit(ETHTOOL_LINK_MODE_10000baseKR_Full_BIT,
979ac3f5533SMaxime Chevallier 				 phydev->supported,
980ac3f5533SMaxime Chevallier 				 val & MDIO_PMA_EXTABLE_10GBKR);
981ac3f5533SMaxime Chevallier 		linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
982ac3f5533SMaxime Chevallier 				 phydev->supported,
983ac3f5533SMaxime Chevallier 				 val & MDIO_PMA_EXTABLE_1000BT);
984ac3f5533SMaxime Chevallier 		linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseKX_Full_BIT,
985ac3f5533SMaxime Chevallier 				 phydev->supported,
986ac3f5533SMaxime Chevallier 				 val & MDIO_PMA_EXTABLE_1000BKX);
987ac3f5533SMaxime Chevallier 
988ac3f5533SMaxime Chevallier 		linkmode_mod_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT,
989ac3f5533SMaxime Chevallier 				 phydev->supported,
990ac3f5533SMaxime Chevallier 				 val & MDIO_PMA_EXTABLE_100BTX);
991ac3f5533SMaxime Chevallier 		linkmode_mod_bit(ETHTOOL_LINK_MODE_100baseT_Half_BIT,
992ac3f5533SMaxime Chevallier 				 phydev->supported,
993ac3f5533SMaxime Chevallier 				 val & MDIO_PMA_EXTABLE_100BTX);
994ac3f5533SMaxime Chevallier 
995ac3f5533SMaxime Chevallier 		linkmode_mod_bit(ETHTOOL_LINK_MODE_10baseT_Full_BIT,
996ac3f5533SMaxime Chevallier 				 phydev->supported,
997ac3f5533SMaxime Chevallier 				 val & MDIO_PMA_EXTABLE_10BT);
998ac3f5533SMaxime Chevallier 		linkmode_mod_bit(ETHTOOL_LINK_MODE_10baseT_Half_BIT,
999ac3f5533SMaxime Chevallier 				 phydev->supported,
1000ac3f5533SMaxime Chevallier 				 val & MDIO_PMA_EXTABLE_10BT);
10017fd8afa8SMaxime Chevallier 
10027fd8afa8SMaxime Chevallier 		if (val & MDIO_PMA_EXTABLE_NBT) {
10037fd8afa8SMaxime Chevallier 			val = phy_read_mmd(phydev, MDIO_MMD_PMAPMD,
10047fd8afa8SMaxime Chevallier 					   MDIO_PMA_NG_EXTABLE);
10057fd8afa8SMaxime Chevallier 			if (val < 0)
10067fd8afa8SMaxime Chevallier 				return val;
10077fd8afa8SMaxime Chevallier 
10087fd8afa8SMaxime Chevallier 			linkmode_mod_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT,
10097fd8afa8SMaxime Chevallier 					 phydev->supported,
10107fd8afa8SMaxime Chevallier 					 val & MDIO_PMA_NG_EXTABLE_2_5GBT);
10117fd8afa8SMaxime Chevallier 
10127fd8afa8SMaxime Chevallier 			linkmode_mod_bit(ETHTOOL_LINK_MODE_5000baseT_Full_BIT,
10137fd8afa8SMaxime Chevallier 					 phydev->supported,
10147fd8afa8SMaxime Chevallier 					 val & MDIO_PMA_NG_EXTABLE_5GBT);
10157fd8afa8SMaxime Chevallier 		}
10163da8ffd8SAlexandru Tachici 
10173da8ffd8SAlexandru Tachici 		if (val & MDIO_PMA_EXTABLE_BT1) {
1018eba2e4c2SStefan Eichenberger 			val = genphy_c45_pma_baset1_read_abilities(phydev);
10193da8ffd8SAlexandru Tachici 			if (val < 0)
10203da8ffd8SAlexandru Tachici 				return val;
10213da8ffd8SAlexandru Tachici 		}
1022ac3f5533SMaxime Chevallier 	}
1023ac3f5533SMaxime Chevallier 
10245827b168SOleksij Rempel 	/* This is optional functionality. If not supported, we may get an error
10255827b168SOleksij Rempel 	 * which should be ignored.
10265827b168SOleksij Rempel 	 */
10275827b168SOleksij Rempel 	genphy_c45_read_eee_abilities(phydev);
10285827b168SOleksij Rempel 
1029ac3f5533SMaxime Chevallier 	return 0;
1030ac3f5533SMaxime Chevallier }
1031ac3f5533SMaxime Chevallier EXPORT_SYMBOL_GPL(genphy_c45_pma_read_abilities);
1032ac3f5533SMaxime Chevallier 
10333da8ffd8SAlexandru Tachici /* Read master/slave preference from registers.
10343da8ffd8SAlexandru Tachici  * The preference is read from the BIT(4) of BASE-T1 AN
10353da8ffd8SAlexandru Tachici  * advertisement register 7.515 and whether the preference
10363da8ffd8SAlexandru Tachici  * is forced or not, it is read from BASE-T1 AN advertisement
10373da8ffd8SAlexandru Tachici  * register 7.514.
10383da8ffd8SAlexandru Tachici  */
genphy_c45_baset1_read_status(struct phy_device * phydev)10392013ad88SOleksij Rempel int genphy_c45_baset1_read_status(struct phy_device *phydev)
10403da8ffd8SAlexandru Tachici {
10413da8ffd8SAlexandru Tachici 	int ret;
10423da8ffd8SAlexandru Tachici 	int cfg;
10433da8ffd8SAlexandru Tachici 
10443da8ffd8SAlexandru Tachici 	phydev->master_slave_get = MASTER_SLAVE_CFG_UNKNOWN;
10453da8ffd8SAlexandru Tachici 	phydev->master_slave_state = MASTER_SLAVE_STATE_UNKNOWN;
10463da8ffd8SAlexandru Tachici 
10473da8ffd8SAlexandru Tachici 	ret = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_T1_ADV_L);
10483da8ffd8SAlexandru Tachici 	if (ret < 0)
10493da8ffd8SAlexandru Tachici 		return ret;
10503da8ffd8SAlexandru Tachici 
10513da8ffd8SAlexandru Tachici 	cfg = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_T1_ADV_M);
10523da8ffd8SAlexandru Tachici 	if (cfg < 0)
10533da8ffd8SAlexandru Tachici 		return cfg;
10543da8ffd8SAlexandru Tachici 
10553da8ffd8SAlexandru Tachici 	if (ret & MDIO_AN_T1_ADV_L_FORCE_MS) {
10563da8ffd8SAlexandru Tachici 		if (cfg & MDIO_AN_T1_ADV_M_MST)
10573da8ffd8SAlexandru Tachici 			phydev->master_slave_get = MASTER_SLAVE_CFG_MASTER_FORCE;
10583da8ffd8SAlexandru Tachici 		else
10593da8ffd8SAlexandru Tachici 			phydev->master_slave_get = MASTER_SLAVE_CFG_SLAVE_FORCE;
10603da8ffd8SAlexandru Tachici 	} else {
10613da8ffd8SAlexandru Tachici 		if (cfg & MDIO_AN_T1_ADV_M_MST)
10623da8ffd8SAlexandru Tachici 			phydev->master_slave_get = MASTER_SLAVE_CFG_MASTER_PREFERRED;
10633da8ffd8SAlexandru Tachici 		else
10643da8ffd8SAlexandru Tachici 			phydev->master_slave_get = MASTER_SLAVE_CFG_SLAVE_PREFERRED;
10653da8ffd8SAlexandru Tachici 	}
10663da8ffd8SAlexandru Tachici 
10673da8ffd8SAlexandru Tachici 	return 0;
10683da8ffd8SAlexandru Tachici }
10692013ad88SOleksij Rempel EXPORT_SYMBOL_GPL(genphy_c45_baset1_read_status);
10703da8ffd8SAlexandru Tachici 
107170fa3a96SHeiner Kallweit /**
107270fa3a96SHeiner Kallweit  * genphy_c45_read_status - read PHY status
107370fa3a96SHeiner Kallweit  * @phydev: target phy_device struct
107470fa3a96SHeiner Kallweit  *
107570fa3a96SHeiner Kallweit  * Reads status from PHY and sets phy_device members accordingly.
107670fa3a96SHeiner Kallweit  */
genphy_c45_read_status(struct phy_device * phydev)107770fa3a96SHeiner Kallweit int genphy_c45_read_status(struct phy_device *phydev)
107870fa3a96SHeiner Kallweit {
107970fa3a96SHeiner Kallweit 	int ret;
108070fa3a96SHeiner Kallweit 
108170fa3a96SHeiner Kallweit 	ret = genphy_c45_read_link(phydev);
108270fa3a96SHeiner Kallweit 	if (ret)
108370fa3a96SHeiner Kallweit 		return ret;
108470fa3a96SHeiner Kallweit 
108570fa3a96SHeiner Kallweit 	phydev->speed = SPEED_UNKNOWN;
108670fa3a96SHeiner Kallweit 	phydev->duplex = DUPLEX_UNKNOWN;
108770fa3a96SHeiner Kallweit 	phydev->pause = 0;
108870fa3a96SHeiner Kallweit 	phydev->asym_pause = 0;
108970fa3a96SHeiner Kallweit 
109070fa3a96SHeiner Kallweit 	if (phydev->autoneg == AUTONEG_ENABLE) {
109170fa3a96SHeiner Kallweit 		ret = genphy_c45_read_lpa(phydev);
109270fa3a96SHeiner Kallweit 		if (ret)
109370fa3a96SHeiner Kallweit 			return ret;
109470fa3a96SHeiner Kallweit 
10953da8ffd8SAlexandru Tachici 		if (genphy_c45_baset1_able(phydev)) {
10963da8ffd8SAlexandru Tachici 			ret = genphy_c45_baset1_read_status(phydev);
10973da8ffd8SAlexandru Tachici 			if (ret < 0)
10983da8ffd8SAlexandru Tachici 				return ret;
10993da8ffd8SAlexandru Tachici 		}
11003da8ffd8SAlexandru Tachici 
110170fa3a96SHeiner Kallweit 		phy_resolve_aneg_linkmode(phydev);
110270fa3a96SHeiner Kallweit 	} else {
110370fa3a96SHeiner Kallweit 		ret = genphy_c45_read_pma(phydev);
110470fa3a96SHeiner Kallweit 	}
110570fa3a96SHeiner Kallweit 
110670fa3a96SHeiner Kallweit 	return ret;
110770fa3a96SHeiner Kallweit }
110870fa3a96SHeiner Kallweit EXPORT_SYMBOL_GPL(genphy_c45_read_status);
110970fa3a96SHeiner Kallweit 
111094acaeb5SMarco Hartmann /**
111194acaeb5SMarco Hartmann  * genphy_c45_config_aneg - restart auto-negotiation or forced setup
111294acaeb5SMarco Hartmann  * @phydev: target phy_device struct
111394acaeb5SMarco Hartmann  *
111494acaeb5SMarco Hartmann  * Description: If auto-negotiation is enabled, we configure the
111594acaeb5SMarco Hartmann  *   advertising, and then restart auto-negotiation.  If it is not
111694acaeb5SMarco Hartmann  *   enabled, then we force a configuration.
111794acaeb5SMarco Hartmann  */
genphy_c45_config_aneg(struct phy_device * phydev)111894acaeb5SMarco Hartmann int genphy_c45_config_aneg(struct phy_device *phydev)
111994acaeb5SMarco Hartmann {
112094acaeb5SMarco Hartmann 	bool changed = false;
112194acaeb5SMarco Hartmann 	int ret;
112294acaeb5SMarco Hartmann 
112394acaeb5SMarco Hartmann 	if (phydev->autoneg == AUTONEG_DISABLE)
112494acaeb5SMarco Hartmann 		return genphy_c45_pma_setup_forced(phydev);
112594acaeb5SMarco Hartmann 
112694acaeb5SMarco Hartmann 	ret = genphy_c45_an_config_aneg(phydev);
112794acaeb5SMarco Hartmann 	if (ret < 0)
112894acaeb5SMarco Hartmann 		return ret;
112994acaeb5SMarco Hartmann 	if (ret > 0)
113094acaeb5SMarco Hartmann 		changed = true;
113194acaeb5SMarco Hartmann 
113294acaeb5SMarco Hartmann 	return genphy_c45_check_and_restart_aneg(phydev, changed);
113394acaeb5SMarco Hartmann }
113494acaeb5SMarco Hartmann EXPORT_SYMBOL_GPL(genphy_c45_config_aneg);
113594acaeb5SMarco Hartmann 
1136921690f2SRussell King /* The gen10g_* functions are the old Clause 45 stub */
1137921690f2SRussell King 
gen10g_config_aneg(struct phy_device * phydev)1138e8a714e0SFlorian Fainelli int gen10g_config_aneg(struct phy_device *phydev)
1139921690f2SRussell King {
1140921690f2SRussell King 	return 0;
1141921690f2SRussell King }
1142e8a714e0SFlorian Fainelli EXPORT_SYMBOL_GPL(gen10g_config_aneg);
1143921690f2SRussell King 
genphy_c45_loopback(struct phy_device * phydev,bool enable)11440ef25ed1SWong Vee Khee int genphy_c45_loopback(struct phy_device *phydev, bool enable)
11450ef25ed1SWong Vee Khee {
11460ef25ed1SWong Vee Khee 	return phy_modify_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL1,
11470ef25ed1SWong Vee Khee 			      MDIO_PCS_CTRL1_LOOPBACK,
11480ef25ed1SWong Vee Khee 			      enable ? MDIO_PCS_CTRL1_LOOPBACK : 0);
11490ef25ed1SWong Vee Khee }
11500ef25ed1SWong Vee Khee EXPORT_SYMBOL_GPL(genphy_c45_loopback);
11510ef25ed1SWong Vee Khee 
115263c67f52SLuo Jie /**
115363c67f52SLuo Jie  * genphy_c45_fast_retrain - configure fast retrain registers
115463c67f52SLuo Jie  * @phydev: target phy_device struct
115506338cefSLuo Jie  * @enable: enable fast retrain or not
115663c67f52SLuo Jie  *
115763c67f52SLuo Jie  * Description: If fast-retrain is enabled, we configure PHY as
115863c67f52SLuo Jie  *   advertising fast retrain capable and THP Bypass Request, then
115963c67f52SLuo Jie  *   enable fast retrain. If it is not enabled, we configure fast
116063c67f52SLuo Jie  *   retrain disabled.
116163c67f52SLuo Jie  */
genphy_c45_fast_retrain(struct phy_device * phydev,bool enable)116263c67f52SLuo Jie int genphy_c45_fast_retrain(struct phy_device *phydev, bool enable)
116363c67f52SLuo Jie {
116463c67f52SLuo Jie 	int ret;
116563c67f52SLuo Jie 
116663c67f52SLuo Jie 	if (!enable)
116763c67f52SLuo Jie 		return phy_clear_bits_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_PMA_10GBR_FSRT_CSR,
116863c67f52SLuo Jie 				MDIO_PMA_10GBR_FSRT_ENABLE);
116963c67f52SLuo Jie 
117063c67f52SLuo Jie 	if (linkmode_test_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->supported)) {
117163c67f52SLuo Jie 		ret = phy_set_bits_mmd(phydev, MDIO_MMD_AN, MDIO_AN_10GBT_CTRL,
117263c67f52SLuo Jie 				MDIO_AN_10GBT_CTRL_ADVFSRT2_5G);
117363c67f52SLuo Jie 		if (ret)
117463c67f52SLuo Jie 			return ret;
117563c67f52SLuo Jie 
117663c67f52SLuo Jie 		ret = phy_set_bits_mmd(phydev, MDIO_MMD_AN, MDIO_AN_CTRL2,
117763c67f52SLuo Jie 				MDIO_AN_THP_BP2_5GT);
117863c67f52SLuo Jie 		if (ret)
117963c67f52SLuo Jie 			return ret;
118063c67f52SLuo Jie 	}
118163c67f52SLuo Jie 
118263c67f52SLuo Jie 	return phy_set_bits_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_PMA_10GBR_FSRT_CSR,
118363c67f52SLuo Jie 			MDIO_PMA_10GBR_FSRT_ENABLE);
118463c67f52SLuo Jie }
118563c67f52SLuo Jie EXPORT_SYMBOL_GPL(genphy_c45_fast_retrain);
118663c67f52SLuo Jie 
118749332341SPiergiorgio Beruto /**
118849332341SPiergiorgio Beruto  * genphy_c45_plca_get_cfg - get PLCA configuration from standard registers
118949332341SPiergiorgio Beruto  * @phydev: target phy_device struct
119049332341SPiergiorgio Beruto  * @plca_cfg: output structure to store the PLCA configuration
119149332341SPiergiorgio Beruto  *
119249332341SPiergiorgio Beruto  * Description: if the PHY complies to the Open Alliance TC14 10BASE-T1S PLCA
119349332341SPiergiorgio Beruto  *   Management Registers specifications, this function can be used to retrieve
119449332341SPiergiorgio Beruto  *   the current PLCA configuration from the standard registers in MMD 31.
119549332341SPiergiorgio Beruto  */
genphy_c45_plca_get_cfg(struct phy_device * phydev,struct phy_plca_cfg * plca_cfg)119649332341SPiergiorgio Beruto int genphy_c45_plca_get_cfg(struct phy_device *phydev,
119749332341SPiergiorgio Beruto 			    struct phy_plca_cfg *plca_cfg)
119849332341SPiergiorgio Beruto {
119949332341SPiergiorgio Beruto 	int ret;
120049332341SPiergiorgio Beruto 
120149332341SPiergiorgio Beruto 	ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, MDIO_OATC14_PLCA_IDVER);
120249332341SPiergiorgio Beruto 	if (ret < 0)
120349332341SPiergiorgio Beruto 		return ret;
120449332341SPiergiorgio Beruto 
120549332341SPiergiorgio Beruto 	if ((ret & MDIO_OATC14_PLCA_IDM) != OATC14_IDM)
120649332341SPiergiorgio Beruto 		return -ENODEV;
120749332341SPiergiorgio Beruto 
120849332341SPiergiorgio Beruto 	plca_cfg->version = ret & ~MDIO_OATC14_PLCA_IDM;
120949332341SPiergiorgio Beruto 
121049332341SPiergiorgio Beruto 	ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, MDIO_OATC14_PLCA_CTRL0);
121149332341SPiergiorgio Beruto 	if (ret < 0)
121249332341SPiergiorgio Beruto 		return ret;
121349332341SPiergiorgio Beruto 
121449332341SPiergiorgio Beruto 	plca_cfg->enabled = !!(ret & MDIO_OATC14_PLCA_EN);
121549332341SPiergiorgio Beruto 
121649332341SPiergiorgio Beruto 	ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, MDIO_OATC14_PLCA_CTRL1);
121749332341SPiergiorgio Beruto 	if (ret < 0)
121849332341SPiergiorgio Beruto 		return ret;
121949332341SPiergiorgio Beruto 
122049332341SPiergiorgio Beruto 	plca_cfg->node_cnt = (ret & MDIO_OATC14_PLCA_NCNT) >> 8;
122149332341SPiergiorgio Beruto 	plca_cfg->node_id = (ret & MDIO_OATC14_PLCA_ID);
122249332341SPiergiorgio Beruto 
122349332341SPiergiorgio Beruto 	ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, MDIO_OATC14_PLCA_TOTMR);
122449332341SPiergiorgio Beruto 	if (ret < 0)
122549332341SPiergiorgio Beruto 		return ret;
122649332341SPiergiorgio Beruto 
122749332341SPiergiorgio Beruto 	plca_cfg->to_tmr = ret & MDIO_OATC14_PLCA_TOT;
122849332341SPiergiorgio Beruto 
122949332341SPiergiorgio Beruto 	ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, MDIO_OATC14_PLCA_BURST);
123049332341SPiergiorgio Beruto 	if (ret < 0)
123149332341SPiergiorgio Beruto 		return ret;
123249332341SPiergiorgio Beruto 
123349332341SPiergiorgio Beruto 	plca_cfg->burst_cnt = (ret & MDIO_OATC14_PLCA_MAXBC) >> 8;
123449332341SPiergiorgio Beruto 	plca_cfg->burst_tmr = (ret & MDIO_OATC14_PLCA_BTMR);
123549332341SPiergiorgio Beruto 
123649332341SPiergiorgio Beruto 	return 0;
123749332341SPiergiorgio Beruto }
123849332341SPiergiorgio Beruto EXPORT_SYMBOL_GPL(genphy_c45_plca_get_cfg);
123949332341SPiergiorgio Beruto 
124049332341SPiergiorgio Beruto /**
124149332341SPiergiorgio Beruto  * genphy_c45_plca_set_cfg - set PLCA configuration using standard registers
124249332341SPiergiorgio Beruto  * @phydev: target phy_device struct
124349332341SPiergiorgio Beruto  * @plca_cfg: structure containing the PLCA configuration. Fields set to -1 are
124449332341SPiergiorgio Beruto  * not to be changed.
124549332341SPiergiorgio Beruto  *
124649332341SPiergiorgio Beruto  * Description: if the PHY complies to the Open Alliance TC14 10BASE-T1S PLCA
124749332341SPiergiorgio Beruto  *   Management Registers specifications, this function can be used to modify
124849332341SPiergiorgio Beruto  *   the PLCA configuration using the standard registers in MMD 31.
124949332341SPiergiorgio Beruto  */
genphy_c45_plca_set_cfg(struct phy_device * phydev,const struct phy_plca_cfg * plca_cfg)125049332341SPiergiorgio Beruto int genphy_c45_plca_set_cfg(struct phy_device *phydev,
125149332341SPiergiorgio Beruto 			    const struct phy_plca_cfg *plca_cfg)
125249332341SPiergiorgio Beruto {
12531038bfb2SPiergiorgio Beruto 	u16 val = 0;
125449332341SPiergiorgio Beruto 	int ret;
125549332341SPiergiorgio Beruto 
125649332341SPiergiorgio Beruto 	// PLCA IDVER is read-only
125749332341SPiergiorgio Beruto 	if (plca_cfg->version >= 0)
125849332341SPiergiorgio Beruto 		return -EINVAL;
125949332341SPiergiorgio Beruto 
126049332341SPiergiorgio Beruto 	// first of all, disable PLCA if required
126149332341SPiergiorgio Beruto 	if (plca_cfg->enabled == 0) {
126249332341SPiergiorgio Beruto 		ret = phy_clear_bits_mmd(phydev, MDIO_MMD_VEND2,
126349332341SPiergiorgio Beruto 					 MDIO_OATC14_PLCA_CTRL0,
126449332341SPiergiorgio Beruto 					 MDIO_OATC14_PLCA_EN);
126549332341SPiergiorgio Beruto 
126649332341SPiergiorgio Beruto 		if (ret < 0)
126749332341SPiergiorgio Beruto 			return ret;
126849332341SPiergiorgio Beruto 	}
126949332341SPiergiorgio Beruto 
127049332341SPiergiorgio Beruto 	// check if we need to set the PLCA node count, node ID, or both
127149332341SPiergiorgio Beruto 	if (plca_cfg->node_cnt >= 0 || plca_cfg->node_id >= 0) {
127249332341SPiergiorgio Beruto 		/* if one between node count and node ID is -not- to be
127349332341SPiergiorgio Beruto 		 * changed, read the register to later perform merge/purge of
127449332341SPiergiorgio Beruto 		 * the configuration as appropriate
127549332341SPiergiorgio Beruto 		 */
127649332341SPiergiorgio Beruto 		if (plca_cfg->node_cnt < 0 || plca_cfg->node_id < 0) {
127749332341SPiergiorgio Beruto 			ret = phy_read_mmd(phydev, MDIO_MMD_VEND2,
127849332341SPiergiorgio Beruto 					   MDIO_OATC14_PLCA_CTRL1);
127949332341SPiergiorgio Beruto 
128049332341SPiergiorgio Beruto 			if (ret < 0)
128149332341SPiergiorgio Beruto 				return ret;
128249332341SPiergiorgio Beruto 
128349332341SPiergiorgio Beruto 			val = ret;
128449332341SPiergiorgio Beruto 		}
128549332341SPiergiorgio Beruto 
128649332341SPiergiorgio Beruto 		if (plca_cfg->node_cnt >= 0)
128749332341SPiergiorgio Beruto 			val = (val & ~MDIO_OATC14_PLCA_NCNT) |
128849332341SPiergiorgio Beruto 			      (plca_cfg->node_cnt << 8);
128949332341SPiergiorgio Beruto 
129049332341SPiergiorgio Beruto 		if (plca_cfg->node_id >= 0)
129149332341SPiergiorgio Beruto 			val = (val & ~MDIO_OATC14_PLCA_ID) |
129249332341SPiergiorgio Beruto 			      (plca_cfg->node_id);
129349332341SPiergiorgio Beruto 
129449332341SPiergiorgio Beruto 		ret = phy_write_mmd(phydev, MDIO_MMD_VEND2,
129549332341SPiergiorgio Beruto 				    MDIO_OATC14_PLCA_CTRL1, val);
129649332341SPiergiorgio Beruto 
129749332341SPiergiorgio Beruto 		if (ret < 0)
129849332341SPiergiorgio Beruto 			return ret;
129949332341SPiergiorgio Beruto 	}
130049332341SPiergiorgio Beruto 
130149332341SPiergiorgio Beruto 	if (plca_cfg->to_tmr >= 0) {
130249332341SPiergiorgio Beruto 		ret = phy_write_mmd(phydev, MDIO_MMD_VEND2,
130349332341SPiergiorgio Beruto 				    MDIO_OATC14_PLCA_TOTMR,
130449332341SPiergiorgio Beruto 				    plca_cfg->to_tmr);
130549332341SPiergiorgio Beruto 
130649332341SPiergiorgio Beruto 		if (ret < 0)
130749332341SPiergiorgio Beruto 			return ret;
130849332341SPiergiorgio Beruto 	}
130949332341SPiergiorgio Beruto 
131049332341SPiergiorgio Beruto 	// check if we need to set the PLCA burst count, burst timer, or both
131149332341SPiergiorgio Beruto 	if (plca_cfg->burst_cnt >= 0 || plca_cfg->burst_tmr >= 0) {
131249332341SPiergiorgio Beruto 		/* if one between burst count and burst timer is -not- to be
131349332341SPiergiorgio Beruto 		 * changed, read the register to later perform merge/purge of
131449332341SPiergiorgio Beruto 		 * the configuration as appropriate
131549332341SPiergiorgio Beruto 		 */
131649332341SPiergiorgio Beruto 		if (plca_cfg->burst_cnt < 0 || plca_cfg->burst_tmr < 0) {
131749332341SPiergiorgio Beruto 			ret = phy_read_mmd(phydev, MDIO_MMD_VEND2,
131849332341SPiergiorgio Beruto 					   MDIO_OATC14_PLCA_BURST);
131949332341SPiergiorgio Beruto 
132049332341SPiergiorgio Beruto 			if (ret < 0)
132149332341SPiergiorgio Beruto 				return ret;
132249332341SPiergiorgio Beruto 
132349332341SPiergiorgio Beruto 			val = ret;
132449332341SPiergiorgio Beruto 		}
132549332341SPiergiorgio Beruto 
132649332341SPiergiorgio Beruto 		if (plca_cfg->burst_cnt >= 0)
132749332341SPiergiorgio Beruto 			val = (val & ~MDIO_OATC14_PLCA_MAXBC) |
132849332341SPiergiorgio Beruto 			      (plca_cfg->burst_cnt << 8);
132949332341SPiergiorgio Beruto 
133049332341SPiergiorgio Beruto 		if (plca_cfg->burst_tmr >= 0)
133149332341SPiergiorgio Beruto 			val = (val & ~MDIO_OATC14_PLCA_BTMR) |
133249332341SPiergiorgio Beruto 			      (plca_cfg->burst_tmr);
133349332341SPiergiorgio Beruto 
133449332341SPiergiorgio Beruto 		ret = phy_write_mmd(phydev, MDIO_MMD_VEND2,
133549332341SPiergiorgio Beruto 				    MDIO_OATC14_PLCA_BURST, val);
133649332341SPiergiorgio Beruto 
133749332341SPiergiorgio Beruto 		if (ret < 0)
133849332341SPiergiorgio Beruto 			return ret;
133949332341SPiergiorgio Beruto 	}
134049332341SPiergiorgio Beruto 
134149332341SPiergiorgio Beruto 	// if we need to enable PLCA, do it at the end
134249332341SPiergiorgio Beruto 	if (plca_cfg->enabled > 0) {
134349332341SPiergiorgio Beruto 		ret = phy_set_bits_mmd(phydev, MDIO_MMD_VEND2,
134449332341SPiergiorgio Beruto 				       MDIO_OATC14_PLCA_CTRL0,
134549332341SPiergiorgio Beruto 				       MDIO_OATC14_PLCA_EN);
134649332341SPiergiorgio Beruto 
134749332341SPiergiorgio Beruto 		if (ret < 0)
134849332341SPiergiorgio Beruto 			return ret;
134949332341SPiergiorgio Beruto 	}
135049332341SPiergiorgio Beruto 
135149332341SPiergiorgio Beruto 	return 0;
135249332341SPiergiorgio Beruto }
135349332341SPiergiorgio Beruto EXPORT_SYMBOL_GPL(genphy_c45_plca_set_cfg);
135449332341SPiergiorgio Beruto 
135549332341SPiergiorgio Beruto /**
135649332341SPiergiorgio Beruto  * genphy_c45_plca_get_status - get PLCA status from standard registers
135749332341SPiergiorgio Beruto  * @phydev: target phy_device struct
135849332341SPiergiorgio Beruto  * @plca_st: output structure to store the PLCA status
135949332341SPiergiorgio Beruto  *
136049332341SPiergiorgio Beruto  * Description: if the PHY complies to the Open Alliance TC14 10BASE-T1S PLCA
136149332341SPiergiorgio Beruto  *   Management Registers specifications, this function can be used to retrieve
136249332341SPiergiorgio Beruto  *   the current PLCA status information from the standard registers in MMD 31.
136349332341SPiergiorgio Beruto  */
genphy_c45_plca_get_status(struct phy_device * phydev,struct phy_plca_status * plca_st)136449332341SPiergiorgio Beruto int genphy_c45_plca_get_status(struct phy_device *phydev,
136549332341SPiergiorgio Beruto 			       struct phy_plca_status *plca_st)
136649332341SPiergiorgio Beruto {
136749332341SPiergiorgio Beruto 	int ret;
136849332341SPiergiorgio Beruto 
136949332341SPiergiorgio Beruto 	ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, MDIO_OATC14_PLCA_STATUS);
137049332341SPiergiorgio Beruto 	if (ret < 0)
137149332341SPiergiorgio Beruto 		return ret;
137249332341SPiergiorgio Beruto 
137349332341SPiergiorgio Beruto 	plca_st->pst = !!(ret & MDIO_OATC14_PLCA_PST);
137449332341SPiergiorgio Beruto 	return 0;
137549332341SPiergiorgio Beruto }
137649332341SPiergiorgio Beruto EXPORT_SYMBOL_GPL(genphy_c45_plca_get_status);
137749332341SPiergiorgio Beruto 
1378022c3f87SOleksij Rempel /**
1379022c3f87SOleksij Rempel  * genphy_c45_eee_is_active - get EEE status
1380022c3f87SOleksij Rempel  * @phydev: target phy_device struct
1381022c3f87SOleksij Rempel  * @adv: variable to store advertised linkmodes
1382022c3f87SOleksij Rempel  * @lp: variable to store LP advertised linkmodes
1383022c3f87SOleksij Rempel  * @is_enabled: variable to store EEE enabled/disabled configuration value
1384022c3f87SOleksij Rempel  *
1385022c3f87SOleksij Rempel  * Description: this function will read local and link partner PHY
1386022c3f87SOleksij Rempel  * advertisements. Compare them return current EEE state.
1387022c3f87SOleksij Rempel  */
genphy_c45_eee_is_active(struct phy_device * phydev,unsigned long * adv,unsigned long * lp,bool * is_enabled)1388022c3f87SOleksij Rempel int genphy_c45_eee_is_active(struct phy_device *phydev, unsigned long *adv,
1389022c3f87SOleksij Rempel 			     unsigned long *lp, bool *is_enabled)
1390022c3f87SOleksij Rempel {
1391022c3f87SOleksij Rempel 	__ETHTOOL_DECLARE_LINK_MODE_MASK(tmp_adv) = {};
1392022c3f87SOleksij Rempel 	__ETHTOOL_DECLARE_LINK_MODE_MASK(tmp_lp) = {};
1393022c3f87SOleksij Rempel 	__ETHTOOL_DECLARE_LINK_MODE_MASK(common);
1394022c3f87SOleksij Rempel 	bool eee_enabled, eee_active;
1395022c3f87SOleksij Rempel 	int ret;
1396022c3f87SOleksij Rempel 
1397022c3f87SOleksij Rempel 	ret = genphy_c45_read_eee_adv(phydev, tmp_adv);
1398022c3f87SOleksij Rempel 	if (ret)
1399022c3f87SOleksij Rempel 		return ret;
1400022c3f87SOleksij Rempel 
1401022c3f87SOleksij Rempel 	ret = genphy_c45_read_eee_lpa(phydev, tmp_lp);
1402022c3f87SOleksij Rempel 	if (ret)
1403022c3f87SOleksij Rempel 		return ret;
1404022c3f87SOleksij Rempel 
1405022c3f87SOleksij Rempel 	eee_enabled = !linkmode_empty(tmp_adv);
1406022c3f87SOleksij Rempel 	linkmode_and(common, tmp_adv, tmp_lp);
1407022c3f87SOleksij Rempel 	if (eee_enabled && !linkmode_empty(common))
1408022c3f87SOleksij Rempel 		eee_active = phy_check_valid(phydev->speed, phydev->duplex,
1409022c3f87SOleksij Rempel 					     common);
1410022c3f87SOleksij Rempel 	else
1411022c3f87SOleksij Rempel 		eee_active = false;
1412022c3f87SOleksij Rempel 
1413022c3f87SOleksij Rempel 	if (adv)
1414022c3f87SOleksij Rempel 		linkmode_copy(adv, tmp_adv);
1415022c3f87SOleksij Rempel 	if (lp)
1416022c3f87SOleksij Rempel 		linkmode_copy(lp, tmp_lp);
1417022c3f87SOleksij Rempel 	if (is_enabled)
1418022c3f87SOleksij Rempel 		*is_enabled = eee_enabled;
1419022c3f87SOleksij Rempel 
1420022c3f87SOleksij Rempel 	return eee_active;
1421022c3f87SOleksij Rempel }
1422022c3f87SOleksij Rempel EXPORT_SYMBOL(genphy_c45_eee_is_active);
1423022c3f87SOleksij Rempel 
1424022c3f87SOleksij Rempel /**
1425022c3f87SOleksij Rempel  * genphy_c45_ethtool_get_eee - get EEE supported and status
1426022c3f87SOleksij Rempel  * @phydev: target phy_device struct
1427022c3f87SOleksij Rempel  * @data: ethtool_eee data
1428022c3f87SOleksij Rempel  *
1429022c3f87SOleksij Rempel  * Description: it reports the Supported/Advertisement/LP Advertisement
1430022c3f87SOleksij Rempel  * capabilities.
1431022c3f87SOleksij Rempel  */
genphy_c45_ethtool_get_eee(struct phy_device * phydev,struct ethtool_eee * data)1432022c3f87SOleksij Rempel int genphy_c45_ethtool_get_eee(struct phy_device *phydev,
1433022c3f87SOleksij Rempel 			       struct ethtool_eee *data)
1434022c3f87SOleksij Rempel {
1435022c3f87SOleksij Rempel 	__ETHTOOL_DECLARE_LINK_MODE_MASK(adv) = {};
1436022c3f87SOleksij Rempel 	__ETHTOOL_DECLARE_LINK_MODE_MASK(lp) = {};
1437022c3f87SOleksij Rempel 	bool overflow = false, is_enabled;
1438022c3f87SOleksij Rempel 	int ret;
1439022c3f87SOleksij Rempel 
1440022c3f87SOleksij Rempel 	ret = genphy_c45_eee_is_active(phydev, adv, lp, &is_enabled);
1441022c3f87SOleksij Rempel 	if (ret < 0)
1442022c3f87SOleksij Rempel 		return ret;
1443022c3f87SOleksij Rempel 
1444022c3f87SOleksij Rempel 	data->eee_enabled = is_enabled;
1445022c3f87SOleksij Rempel 	data->eee_active = ret;
1446022c3f87SOleksij Rempel 
1447022c3f87SOleksij Rempel 	if (!ethtool_convert_link_mode_to_legacy_u32(&data->supported,
1448022c3f87SOleksij Rempel 						     phydev->supported_eee))
1449022c3f87SOleksij Rempel 		overflow = true;
1450022c3f87SOleksij Rempel 	if (!ethtool_convert_link_mode_to_legacy_u32(&data->advertised, adv))
1451022c3f87SOleksij Rempel 		overflow = true;
1452022c3f87SOleksij Rempel 	if (!ethtool_convert_link_mode_to_legacy_u32(&data->lp_advertised, lp))
1453022c3f87SOleksij Rempel 		overflow = true;
1454022c3f87SOleksij Rempel 
1455022c3f87SOleksij Rempel 	if (overflow)
1456022c3f87SOleksij Rempel 		phydev_warn(phydev, "Not all supported or advertised EEE link modes were passed to the user space\n");
1457022c3f87SOleksij Rempel 
1458022c3f87SOleksij Rempel 	return 0;
1459022c3f87SOleksij Rempel }
1460022c3f87SOleksij Rempel EXPORT_SYMBOL(genphy_c45_ethtool_get_eee);
1461022c3f87SOleksij Rempel 
1462022c3f87SOleksij Rempel /**
1463b7c31ccdSAndrew Lunn  * genphy_c45_ethtool_set_eee - set EEE supported and status
1464022c3f87SOleksij Rempel  * @phydev: target phy_device struct
1465022c3f87SOleksij Rempel  * @data: ethtool_eee data
1466022c3f87SOleksij Rempel  *
1467b7c31ccdSAndrew Lunn  * Description: sets the Supported/Advertisement/LP Advertisement
1468b7c31ccdSAndrew Lunn  * capabilities. If eee_enabled is false, no links modes are
1469b7c31ccdSAndrew Lunn  * advertised, but the previously advertised link modes are
1470b7c31ccdSAndrew Lunn  * retained. This allows EEE to be enabled/disabled in a
1471b7c31ccdSAndrew Lunn  * non-destructive way.
1472022c3f87SOleksij Rempel  */
genphy_c45_ethtool_set_eee(struct phy_device * phydev,struct ethtool_eee * data)1473022c3f87SOleksij Rempel int genphy_c45_ethtool_set_eee(struct phy_device *phydev,
1474022c3f87SOleksij Rempel 			       struct ethtool_eee *data)
1475022c3f87SOleksij Rempel {
1476022c3f87SOleksij Rempel 	int ret;
1477022c3f87SOleksij Rempel 
1478022c3f87SOleksij Rempel 	if (data->eee_enabled) {
1479186b1da7SOleksij Rempel 		if (data->advertised) {
1480186b1da7SOleksij Rempel 			__ETHTOOL_DECLARE_LINK_MODE_MASK(adv);
1481186b1da7SOleksij Rempel 
1482186b1da7SOleksij Rempel 			ethtool_convert_legacy_u32_to_link_mode(adv,
1483186b1da7SOleksij Rempel 								data->advertised);
1484186b1da7SOleksij Rempel 			linkmode_andnot(adv, adv, phydev->supported_eee);
1485186b1da7SOleksij Rempel 			if (!linkmode_empty(adv)) {
1486186b1da7SOleksij Rempel 				phydev_warn(phydev, "At least some EEE link modes are not supported.\n");
1487186b1da7SOleksij Rempel 				return -EINVAL;
1488186b1da7SOleksij Rempel 			}
1489186b1da7SOleksij Rempel 
14903eeca4e1SOleksij Rempel 			ethtool_convert_legacy_u32_to_link_mode(phydev->advertising_eee,
14913eeca4e1SOleksij Rempel 								data->advertised);
1492186b1da7SOleksij Rempel 		} else {
14933eeca4e1SOleksij Rempel 			linkmode_copy(phydev->advertising_eee,
14943eeca4e1SOleksij Rempel 				      phydev->supported_eee);
1495186b1da7SOleksij Rempel 		}
14963eeca4e1SOleksij Rempel 
14973eeca4e1SOleksij Rempel 		phydev->eee_enabled = true;
14983eeca4e1SOleksij Rempel 	} else {
14993eeca4e1SOleksij Rempel 		phydev->eee_enabled = false;
1500022c3f87SOleksij Rempel 	}
1501022c3f87SOleksij Rempel 
15023eeca4e1SOleksij Rempel 	ret = genphy_c45_an_config_eee_aneg(phydev);
1503022c3f87SOleksij Rempel 	if (ret < 0)
1504022c3f87SOleksij Rempel 		return ret;
1505022c3f87SOleksij Rempel 	if (ret > 0)
1506022c3f87SOleksij Rempel 		return phy_restart_aneg(phydev);
1507022c3f87SOleksij Rempel 
1508022c3f87SOleksij Rempel 	return 0;
1509022c3f87SOleksij Rempel }
1510022c3f87SOleksij Rempel EXPORT_SYMBOL(genphy_c45_ethtool_set_eee);
1511022c3f87SOleksij Rempel 
151222b56e82SHeiner Kallweit struct phy_driver genphy_c45_driver = {
1513921690f2SRussell King 	.phy_id         = 0xffffffff,
1514921690f2SRussell King 	.phy_id_mask    = 0xffffffff,
151522b56e82SHeiner Kallweit 	.name           = "Generic Clause 45 PHY",
151622b56e82SHeiner Kallweit 	.read_status    = genphy_c45_read_status,
1517921690f2SRussell King };
1518