18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Clause 45 PHY support 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci#include <linux/ethtool.h> 68c2ecf20Sopenharmony_ci#include <linux/export.h> 78c2ecf20Sopenharmony_ci#include <linux/mdio.h> 88c2ecf20Sopenharmony_ci#include <linux/mii.h> 98c2ecf20Sopenharmony_ci#include <linux/phy.h> 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci/** 128c2ecf20Sopenharmony_ci * genphy_c45_setup_forced - configures a forced speed 138c2ecf20Sopenharmony_ci * @phydev: target phy_device struct 148c2ecf20Sopenharmony_ci */ 158c2ecf20Sopenharmony_ciint genphy_c45_pma_setup_forced(struct phy_device *phydev) 168c2ecf20Sopenharmony_ci{ 178c2ecf20Sopenharmony_ci int ctrl1, ctrl2, ret; 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci /* Half duplex is not supported */ 208c2ecf20Sopenharmony_ci if (phydev->duplex != DUPLEX_FULL) 218c2ecf20Sopenharmony_ci return -EINVAL; 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci ctrl1 = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_CTRL1); 248c2ecf20Sopenharmony_ci if (ctrl1 < 0) 258c2ecf20Sopenharmony_ci return ctrl1; 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci ctrl2 = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_CTRL2); 288c2ecf20Sopenharmony_ci if (ctrl2 < 0) 298c2ecf20Sopenharmony_ci return ctrl2; 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci ctrl1 &= ~MDIO_CTRL1_SPEEDSEL; 328c2ecf20Sopenharmony_ci /* 338c2ecf20Sopenharmony_ci * PMA/PMD type selection is 1.7.5:0 not 1.7.3:0. See 45.2.1.6.1 348c2ecf20Sopenharmony_ci * in 802.3-2012 and 802.3-2015. 358c2ecf20Sopenharmony_ci */ 368c2ecf20Sopenharmony_ci ctrl2 &= ~(MDIO_PMA_CTRL2_TYPE | 0x30); 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci switch (phydev->speed) { 398c2ecf20Sopenharmony_ci case SPEED_10: 408c2ecf20Sopenharmony_ci ctrl2 |= MDIO_PMA_CTRL2_10BT; 418c2ecf20Sopenharmony_ci break; 428c2ecf20Sopenharmony_ci case SPEED_100: 438c2ecf20Sopenharmony_ci ctrl1 |= MDIO_PMA_CTRL1_SPEED100; 448c2ecf20Sopenharmony_ci ctrl2 |= MDIO_PMA_CTRL2_100BTX; 458c2ecf20Sopenharmony_ci break; 468c2ecf20Sopenharmony_ci case SPEED_1000: 478c2ecf20Sopenharmony_ci ctrl1 |= MDIO_PMA_CTRL1_SPEED1000; 488c2ecf20Sopenharmony_ci /* Assume 1000base-T */ 498c2ecf20Sopenharmony_ci ctrl2 |= MDIO_PMA_CTRL2_1000BT; 508c2ecf20Sopenharmony_ci break; 518c2ecf20Sopenharmony_ci case SPEED_2500: 528c2ecf20Sopenharmony_ci ctrl1 |= MDIO_CTRL1_SPEED2_5G; 538c2ecf20Sopenharmony_ci /* Assume 2.5Gbase-T */ 548c2ecf20Sopenharmony_ci ctrl2 |= MDIO_PMA_CTRL2_2_5GBT; 558c2ecf20Sopenharmony_ci break; 568c2ecf20Sopenharmony_ci case SPEED_5000: 578c2ecf20Sopenharmony_ci ctrl1 |= MDIO_CTRL1_SPEED5G; 588c2ecf20Sopenharmony_ci /* Assume 5Gbase-T */ 598c2ecf20Sopenharmony_ci ctrl2 |= MDIO_PMA_CTRL2_5GBT; 608c2ecf20Sopenharmony_ci break; 618c2ecf20Sopenharmony_ci case SPEED_10000: 628c2ecf20Sopenharmony_ci ctrl1 |= MDIO_CTRL1_SPEED10G; 638c2ecf20Sopenharmony_ci /* Assume 10Gbase-T */ 648c2ecf20Sopenharmony_ci ctrl2 |= MDIO_PMA_CTRL2_10GBT; 658c2ecf20Sopenharmony_ci break; 668c2ecf20Sopenharmony_ci default: 678c2ecf20Sopenharmony_ci return -EINVAL; 688c2ecf20Sopenharmony_ci } 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci ret = phy_write_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_CTRL1, ctrl1); 718c2ecf20Sopenharmony_ci if (ret < 0) 728c2ecf20Sopenharmony_ci return ret; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci ret = phy_write_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_CTRL2, ctrl2); 758c2ecf20Sopenharmony_ci if (ret < 0) 768c2ecf20Sopenharmony_ci return ret; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci return genphy_c45_an_disable_aneg(phydev); 798c2ecf20Sopenharmony_ci} 808c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(genphy_c45_pma_setup_forced); 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci/** 838c2ecf20Sopenharmony_ci * genphy_c45_an_config_aneg - configure advertisement registers 848c2ecf20Sopenharmony_ci * @phydev: target phy_device struct 858c2ecf20Sopenharmony_ci * 868c2ecf20Sopenharmony_ci * Configure advertisement registers based on modes set in phydev->advertising 878c2ecf20Sopenharmony_ci * 888c2ecf20Sopenharmony_ci * Returns negative errno code on failure, 0 if advertisement didn't change, 898c2ecf20Sopenharmony_ci * or 1 if advertised modes changed. 908c2ecf20Sopenharmony_ci */ 918c2ecf20Sopenharmony_ciint genphy_c45_an_config_aneg(struct phy_device *phydev) 928c2ecf20Sopenharmony_ci{ 938c2ecf20Sopenharmony_ci int changed, ret; 948c2ecf20Sopenharmony_ci u32 adv; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci linkmode_and(phydev->advertising, phydev->advertising, 978c2ecf20Sopenharmony_ci phydev->supported); 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci changed = genphy_config_eee_advert(phydev); 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci adv = linkmode_adv_to_mii_adv_t(phydev->advertising); 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci ret = phy_modify_mmd_changed(phydev, MDIO_MMD_AN, MDIO_AN_ADVERTISE, 1048c2ecf20Sopenharmony_ci ADVERTISE_ALL | ADVERTISE_100BASE4 | 1058c2ecf20Sopenharmony_ci ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM, 1068c2ecf20Sopenharmony_ci adv); 1078c2ecf20Sopenharmony_ci if (ret < 0) 1088c2ecf20Sopenharmony_ci return ret; 1098c2ecf20Sopenharmony_ci if (ret > 0) 1108c2ecf20Sopenharmony_ci changed = 1; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci adv = linkmode_adv_to_mii_10gbt_adv_t(phydev->advertising); 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci ret = phy_modify_mmd_changed(phydev, MDIO_MMD_AN, MDIO_AN_10GBT_CTRL, 1158c2ecf20Sopenharmony_ci MDIO_AN_10GBT_CTRL_ADV10G | 1168c2ecf20Sopenharmony_ci MDIO_AN_10GBT_CTRL_ADV5G | 1178c2ecf20Sopenharmony_ci MDIO_AN_10GBT_CTRL_ADV2_5G, adv); 1188c2ecf20Sopenharmony_ci if (ret < 0) 1198c2ecf20Sopenharmony_ci return ret; 1208c2ecf20Sopenharmony_ci if (ret > 0) 1218c2ecf20Sopenharmony_ci changed = 1; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci return changed; 1248c2ecf20Sopenharmony_ci} 1258c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(genphy_c45_an_config_aneg); 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci/** 1288c2ecf20Sopenharmony_ci * genphy_c45_an_disable_aneg - disable auto-negotiation 1298c2ecf20Sopenharmony_ci * @phydev: target phy_device struct 1308c2ecf20Sopenharmony_ci * 1318c2ecf20Sopenharmony_ci * Disable auto-negotiation in the Clause 45 PHY. The link parameters 1328c2ecf20Sopenharmony_ci * parameters are controlled through the PMA/PMD MMD registers. 1338c2ecf20Sopenharmony_ci * 1348c2ecf20Sopenharmony_ci * Returns zero on success, negative errno code on failure. 1358c2ecf20Sopenharmony_ci */ 1368c2ecf20Sopenharmony_ciint genphy_c45_an_disable_aneg(struct phy_device *phydev) 1378c2ecf20Sopenharmony_ci{ 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci return phy_clear_bits_mmd(phydev, MDIO_MMD_AN, MDIO_CTRL1, 1408c2ecf20Sopenharmony_ci MDIO_AN_CTRL1_ENABLE | MDIO_AN_CTRL1_RESTART); 1418c2ecf20Sopenharmony_ci} 1428c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(genphy_c45_an_disable_aneg); 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci/** 1458c2ecf20Sopenharmony_ci * genphy_c45_restart_aneg - Enable and restart auto-negotiation 1468c2ecf20Sopenharmony_ci * @phydev: target phy_device struct 1478c2ecf20Sopenharmony_ci * 1488c2ecf20Sopenharmony_ci * This assumes that the auto-negotiation MMD is present. 1498c2ecf20Sopenharmony_ci * 1508c2ecf20Sopenharmony_ci * Enable and restart auto-negotiation. 1518c2ecf20Sopenharmony_ci */ 1528c2ecf20Sopenharmony_ciint genphy_c45_restart_aneg(struct phy_device *phydev) 1538c2ecf20Sopenharmony_ci{ 1548c2ecf20Sopenharmony_ci return phy_set_bits_mmd(phydev, MDIO_MMD_AN, MDIO_CTRL1, 1558c2ecf20Sopenharmony_ci MDIO_AN_CTRL1_ENABLE | MDIO_AN_CTRL1_RESTART); 1568c2ecf20Sopenharmony_ci} 1578c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(genphy_c45_restart_aneg); 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci/** 1608c2ecf20Sopenharmony_ci * genphy_c45_check_and_restart_aneg - Enable and restart auto-negotiation 1618c2ecf20Sopenharmony_ci * @phydev: target phy_device struct 1628c2ecf20Sopenharmony_ci * @restart: whether aneg restart is requested 1638c2ecf20Sopenharmony_ci * 1648c2ecf20Sopenharmony_ci * This assumes that the auto-negotiation MMD is present. 1658c2ecf20Sopenharmony_ci * 1668c2ecf20Sopenharmony_ci * Check, and restart auto-negotiation if needed. 1678c2ecf20Sopenharmony_ci */ 1688c2ecf20Sopenharmony_ciint genphy_c45_check_and_restart_aneg(struct phy_device *phydev, bool restart) 1698c2ecf20Sopenharmony_ci{ 1708c2ecf20Sopenharmony_ci int ret; 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci if (!restart) { 1738c2ecf20Sopenharmony_ci /* Configure and restart aneg if it wasn't set before */ 1748c2ecf20Sopenharmony_ci ret = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_CTRL1); 1758c2ecf20Sopenharmony_ci if (ret < 0) 1768c2ecf20Sopenharmony_ci return ret; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci if (!(ret & MDIO_AN_CTRL1_ENABLE)) 1798c2ecf20Sopenharmony_ci restart = true; 1808c2ecf20Sopenharmony_ci } 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci if (restart) 1838c2ecf20Sopenharmony_ci return genphy_c45_restart_aneg(phydev); 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci return 0; 1868c2ecf20Sopenharmony_ci} 1878c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(genphy_c45_check_and_restart_aneg); 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci/** 1908c2ecf20Sopenharmony_ci * genphy_c45_aneg_done - return auto-negotiation complete status 1918c2ecf20Sopenharmony_ci * @phydev: target phy_device struct 1928c2ecf20Sopenharmony_ci * 1938c2ecf20Sopenharmony_ci * This assumes that the auto-negotiation MMD is present. 1948c2ecf20Sopenharmony_ci * 1958c2ecf20Sopenharmony_ci * Reads the status register from the auto-negotiation MMD, returning: 1968c2ecf20Sopenharmony_ci * - positive if auto-negotiation is complete 1978c2ecf20Sopenharmony_ci * - negative errno code on error 1988c2ecf20Sopenharmony_ci * - zero otherwise 1998c2ecf20Sopenharmony_ci */ 2008c2ecf20Sopenharmony_ciint genphy_c45_aneg_done(struct phy_device *phydev) 2018c2ecf20Sopenharmony_ci{ 2028c2ecf20Sopenharmony_ci int val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_STAT1); 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci return val < 0 ? val : val & MDIO_AN_STAT1_COMPLETE ? 1 : 0; 2058c2ecf20Sopenharmony_ci} 2068c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(genphy_c45_aneg_done); 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci/** 2098c2ecf20Sopenharmony_ci * genphy_c45_read_link - read the overall link status from the MMDs 2108c2ecf20Sopenharmony_ci * @phydev: target phy_device struct 2118c2ecf20Sopenharmony_ci * 2128c2ecf20Sopenharmony_ci * Read the link status from the specified MMDs, and if they all indicate 2138c2ecf20Sopenharmony_ci * that the link is up, set phydev->link to 1. If an error is encountered, 2148c2ecf20Sopenharmony_ci * a negative errno will be returned, otherwise zero. 2158c2ecf20Sopenharmony_ci */ 2168c2ecf20Sopenharmony_ciint genphy_c45_read_link(struct phy_device *phydev) 2178c2ecf20Sopenharmony_ci{ 2188c2ecf20Sopenharmony_ci u32 mmd_mask = MDIO_DEVS_PMAPMD; 2198c2ecf20Sopenharmony_ci int val, devad; 2208c2ecf20Sopenharmony_ci bool link = true; 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci if (phydev->c45_ids.mmds_present & MDIO_DEVS_AN) { 2238c2ecf20Sopenharmony_ci val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_CTRL1); 2248c2ecf20Sopenharmony_ci if (val < 0) 2258c2ecf20Sopenharmony_ci return val; 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci /* Autoneg is being started, therefore disregard current 2288c2ecf20Sopenharmony_ci * link status and report link as down. 2298c2ecf20Sopenharmony_ci */ 2308c2ecf20Sopenharmony_ci if (val & MDIO_AN_CTRL1_RESTART) { 2318c2ecf20Sopenharmony_ci phydev->link = 0; 2328c2ecf20Sopenharmony_ci return 0; 2338c2ecf20Sopenharmony_ci } 2348c2ecf20Sopenharmony_ci } 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci while (mmd_mask && link) { 2378c2ecf20Sopenharmony_ci devad = __ffs(mmd_mask); 2388c2ecf20Sopenharmony_ci mmd_mask &= ~BIT(devad); 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci /* The link state is latched low so that momentary link 2418c2ecf20Sopenharmony_ci * drops can be detected. Do not double-read the status 2428c2ecf20Sopenharmony_ci * in polling mode to detect such short link drops except 2438c2ecf20Sopenharmony_ci * the link was already down. 2448c2ecf20Sopenharmony_ci */ 2458c2ecf20Sopenharmony_ci if (!phy_polling_mode(phydev) || !phydev->link) { 2468c2ecf20Sopenharmony_ci val = phy_read_mmd(phydev, devad, MDIO_STAT1); 2478c2ecf20Sopenharmony_ci if (val < 0) 2488c2ecf20Sopenharmony_ci return val; 2498c2ecf20Sopenharmony_ci else if (val & MDIO_STAT1_LSTATUS) 2508c2ecf20Sopenharmony_ci continue; 2518c2ecf20Sopenharmony_ci } 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci val = phy_read_mmd(phydev, devad, MDIO_STAT1); 2548c2ecf20Sopenharmony_ci if (val < 0) 2558c2ecf20Sopenharmony_ci return val; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci if (!(val & MDIO_STAT1_LSTATUS)) 2588c2ecf20Sopenharmony_ci link = false; 2598c2ecf20Sopenharmony_ci } 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci phydev->link = link; 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci return 0; 2648c2ecf20Sopenharmony_ci} 2658c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(genphy_c45_read_link); 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci/** 2688c2ecf20Sopenharmony_ci * genphy_c45_read_lpa - read the link partner advertisement and pause 2698c2ecf20Sopenharmony_ci * @phydev: target phy_device struct 2708c2ecf20Sopenharmony_ci * 2718c2ecf20Sopenharmony_ci * Read the Clause 45 defined base (7.19) and 10G (7.33) status registers, 2728c2ecf20Sopenharmony_ci * filling in the link partner advertisement, pause and asym_pause members 2738c2ecf20Sopenharmony_ci * in @phydev. This assumes that the auto-negotiation MMD is present, and 2748c2ecf20Sopenharmony_ci * the backplane bit (7.48.0) is clear. Clause 45 PHY drivers are expected 2758c2ecf20Sopenharmony_ci * to fill in the remainder of the link partner advert from vendor registers. 2768c2ecf20Sopenharmony_ci */ 2778c2ecf20Sopenharmony_ciint genphy_c45_read_lpa(struct phy_device *phydev) 2788c2ecf20Sopenharmony_ci{ 2798c2ecf20Sopenharmony_ci int val; 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_STAT1); 2828c2ecf20Sopenharmony_ci if (val < 0) 2838c2ecf20Sopenharmony_ci return val; 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci if (!(val & MDIO_AN_STAT1_COMPLETE)) { 2868c2ecf20Sopenharmony_ci linkmode_clear_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, 2878c2ecf20Sopenharmony_ci phydev->lp_advertising); 2888c2ecf20Sopenharmony_ci mii_10gbt_stat_mod_linkmode_lpa_t(phydev->lp_advertising, 0); 2898c2ecf20Sopenharmony_ci mii_adv_mod_linkmode_adv_t(phydev->lp_advertising, 0); 2908c2ecf20Sopenharmony_ci phydev->pause = 0; 2918c2ecf20Sopenharmony_ci phydev->asym_pause = 0; 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci return 0; 2948c2ecf20Sopenharmony_ci } 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci linkmode_mod_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, phydev->lp_advertising, 2978c2ecf20Sopenharmony_ci val & MDIO_AN_STAT1_LPABLE); 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci /* Read the link partner's base page advertisement */ 3008c2ecf20Sopenharmony_ci val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_LPA); 3018c2ecf20Sopenharmony_ci if (val < 0) 3028c2ecf20Sopenharmony_ci return val; 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci mii_adv_mod_linkmode_adv_t(phydev->lp_advertising, val); 3058c2ecf20Sopenharmony_ci phydev->pause = val & LPA_PAUSE_CAP ? 1 : 0; 3068c2ecf20Sopenharmony_ci phydev->asym_pause = val & LPA_PAUSE_ASYM ? 1 : 0; 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci /* Read the link partner's 10G advertisement */ 3098c2ecf20Sopenharmony_ci val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_10GBT_STAT); 3108c2ecf20Sopenharmony_ci if (val < 0) 3118c2ecf20Sopenharmony_ci return val; 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci mii_10gbt_stat_mod_linkmode_lpa_t(phydev->lp_advertising, val); 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci return 0; 3168c2ecf20Sopenharmony_ci} 3178c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(genphy_c45_read_lpa); 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci/** 3208c2ecf20Sopenharmony_ci * genphy_c45_read_pma - read link speed etc from PMA 3218c2ecf20Sopenharmony_ci * @phydev: target phy_device struct 3228c2ecf20Sopenharmony_ci */ 3238c2ecf20Sopenharmony_ciint genphy_c45_read_pma(struct phy_device *phydev) 3248c2ecf20Sopenharmony_ci{ 3258c2ecf20Sopenharmony_ci int val; 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci linkmode_zero(phydev->lp_advertising); 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci val = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_CTRL1); 3308c2ecf20Sopenharmony_ci if (val < 0) 3318c2ecf20Sopenharmony_ci return val; 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci switch (val & MDIO_CTRL1_SPEEDSEL) { 3348c2ecf20Sopenharmony_ci case 0: 3358c2ecf20Sopenharmony_ci phydev->speed = SPEED_10; 3368c2ecf20Sopenharmony_ci break; 3378c2ecf20Sopenharmony_ci case MDIO_PMA_CTRL1_SPEED100: 3388c2ecf20Sopenharmony_ci phydev->speed = SPEED_100; 3398c2ecf20Sopenharmony_ci break; 3408c2ecf20Sopenharmony_ci case MDIO_PMA_CTRL1_SPEED1000: 3418c2ecf20Sopenharmony_ci phydev->speed = SPEED_1000; 3428c2ecf20Sopenharmony_ci break; 3438c2ecf20Sopenharmony_ci case MDIO_CTRL1_SPEED2_5G: 3448c2ecf20Sopenharmony_ci phydev->speed = SPEED_2500; 3458c2ecf20Sopenharmony_ci break; 3468c2ecf20Sopenharmony_ci case MDIO_CTRL1_SPEED5G: 3478c2ecf20Sopenharmony_ci phydev->speed = SPEED_5000; 3488c2ecf20Sopenharmony_ci break; 3498c2ecf20Sopenharmony_ci case MDIO_CTRL1_SPEED10G: 3508c2ecf20Sopenharmony_ci phydev->speed = SPEED_10000; 3518c2ecf20Sopenharmony_ci break; 3528c2ecf20Sopenharmony_ci default: 3538c2ecf20Sopenharmony_ci phydev->speed = SPEED_UNKNOWN; 3548c2ecf20Sopenharmony_ci break; 3558c2ecf20Sopenharmony_ci } 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci phydev->duplex = DUPLEX_FULL; 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci return 0; 3608c2ecf20Sopenharmony_ci} 3618c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(genphy_c45_read_pma); 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci/** 3648c2ecf20Sopenharmony_ci * genphy_c45_read_mdix - read mdix status from PMA 3658c2ecf20Sopenharmony_ci * @phydev: target phy_device struct 3668c2ecf20Sopenharmony_ci */ 3678c2ecf20Sopenharmony_ciint genphy_c45_read_mdix(struct phy_device *phydev) 3688c2ecf20Sopenharmony_ci{ 3698c2ecf20Sopenharmony_ci int val; 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci if (phydev->speed == SPEED_10000) { 3728c2ecf20Sopenharmony_ci val = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, 3738c2ecf20Sopenharmony_ci MDIO_PMA_10GBT_SWAPPOL); 3748c2ecf20Sopenharmony_ci if (val < 0) 3758c2ecf20Sopenharmony_ci return val; 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci switch (val) { 3788c2ecf20Sopenharmony_ci case MDIO_PMA_10GBT_SWAPPOL_ABNX | MDIO_PMA_10GBT_SWAPPOL_CDNX: 3798c2ecf20Sopenharmony_ci phydev->mdix = ETH_TP_MDI; 3808c2ecf20Sopenharmony_ci break; 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci case 0: 3838c2ecf20Sopenharmony_ci phydev->mdix = ETH_TP_MDI_X; 3848c2ecf20Sopenharmony_ci break; 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci default: 3878c2ecf20Sopenharmony_ci phydev->mdix = ETH_TP_MDI_INVALID; 3888c2ecf20Sopenharmony_ci break; 3898c2ecf20Sopenharmony_ci } 3908c2ecf20Sopenharmony_ci } 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci return 0; 3938c2ecf20Sopenharmony_ci} 3948c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(genphy_c45_read_mdix); 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci/** 3978c2ecf20Sopenharmony_ci * genphy_c45_pma_read_abilities - read supported link modes from PMA 3988c2ecf20Sopenharmony_ci * @phydev: target phy_device struct 3998c2ecf20Sopenharmony_ci * 4008c2ecf20Sopenharmony_ci * Read the supported link modes from the PMA Status 2 (1.8) register. If bit 4018c2ecf20Sopenharmony_ci * 1.8.9 is set, the list of supported modes is build using the values in the 4028c2ecf20Sopenharmony_ci * PMA Extended Abilities (1.11) register, indicating 1000BASET an 10G related 4038c2ecf20Sopenharmony_ci * modes. If bit 1.11.14 is set, then the list is also extended with the modes 4048c2ecf20Sopenharmony_ci * in the 2.5G/5G PMA Extended register (1.21), indicating if 2.5GBASET and 4058c2ecf20Sopenharmony_ci * 5GBASET are supported. 4068c2ecf20Sopenharmony_ci */ 4078c2ecf20Sopenharmony_ciint genphy_c45_pma_read_abilities(struct phy_device *phydev) 4088c2ecf20Sopenharmony_ci{ 4098c2ecf20Sopenharmony_ci int val; 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci linkmode_clear_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, phydev->supported); 4128c2ecf20Sopenharmony_ci if (phydev->c45_ids.mmds_present & MDIO_DEVS_AN) { 4138c2ecf20Sopenharmony_ci val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_STAT1); 4148c2ecf20Sopenharmony_ci if (val < 0) 4158c2ecf20Sopenharmony_ci return val; 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci if (val & MDIO_AN_STAT1_ABLE) 4188c2ecf20Sopenharmony_ci linkmode_set_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, 4198c2ecf20Sopenharmony_ci phydev->supported); 4208c2ecf20Sopenharmony_ci } 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci val = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_STAT2); 4238c2ecf20Sopenharmony_ci if (val < 0) 4248c2ecf20Sopenharmony_ci return val; 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci linkmode_mod_bit(ETHTOOL_LINK_MODE_10000baseSR_Full_BIT, 4278c2ecf20Sopenharmony_ci phydev->supported, 4288c2ecf20Sopenharmony_ci val & MDIO_PMA_STAT2_10GBSR); 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci linkmode_mod_bit(ETHTOOL_LINK_MODE_10000baseLR_Full_BIT, 4318c2ecf20Sopenharmony_ci phydev->supported, 4328c2ecf20Sopenharmony_ci val & MDIO_PMA_STAT2_10GBLR); 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci linkmode_mod_bit(ETHTOOL_LINK_MODE_10000baseER_Full_BIT, 4358c2ecf20Sopenharmony_ci phydev->supported, 4368c2ecf20Sopenharmony_ci val & MDIO_PMA_STAT2_10GBER); 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci if (val & MDIO_PMA_STAT2_EXTABLE) { 4398c2ecf20Sopenharmony_ci val = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_PMA_EXTABLE); 4408c2ecf20Sopenharmony_ci if (val < 0) 4418c2ecf20Sopenharmony_ci return val; 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci linkmode_mod_bit(ETHTOOL_LINK_MODE_10000baseLRM_Full_BIT, 4448c2ecf20Sopenharmony_ci phydev->supported, 4458c2ecf20Sopenharmony_ci val & MDIO_PMA_EXTABLE_10GBLRM); 4468c2ecf20Sopenharmony_ci linkmode_mod_bit(ETHTOOL_LINK_MODE_10000baseT_Full_BIT, 4478c2ecf20Sopenharmony_ci phydev->supported, 4488c2ecf20Sopenharmony_ci val & MDIO_PMA_EXTABLE_10GBT); 4498c2ecf20Sopenharmony_ci linkmode_mod_bit(ETHTOOL_LINK_MODE_10000baseKX4_Full_BIT, 4508c2ecf20Sopenharmony_ci phydev->supported, 4518c2ecf20Sopenharmony_ci val & MDIO_PMA_EXTABLE_10GBKX4); 4528c2ecf20Sopenharmony_ci linkmode_mod_bit(ETHTOOL_LINK_MODE_10000baseKR_Full_BIT, 4538c2ecf20Sopenharmony_ci phydev->supported, 4548c2ecf20Sopenharmony_ci val & MDIO_PMA_EXTABLE_10GBKR); 4558c2ecf20Sopenharmony_ci linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, 4568c2ecf20Sopenharmony_ci phydev->supported, 4578c2ecf20Sopenharmony_ci val & MDIO_PMA_EXTABLE_1000BT); 4588c2ecf20Sopenharmony_ci linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseKX_Full_BIT, 4598c2ecf20Sopenharmony_ci phydev->supported, 4608c2ecf20Sopenharmony_ci val & MDIO_PMA_EXTABLE_1000BKX); 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci linkmode_mod_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT, 4638c2ecf20Sopenharmony_ci phydev->supported, 4648c2ecf20Sopenharmony_ci val & MDIO_PMA_EXTABLE_100BTX); 4658c2ecf20Sopenharmony_ci linkmode_mod_bit(ETHTOOL_LINK_MODE_100baseT_Half_BIT, 4668c2ecf20Sopenharmony_ci phydev->supported, 4678c2ecf20Sopenharmony_ci val & MDIO_PMA_EXTABLE_100BTX); 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci linkmode_mod_bit(ETHTOOL_LINK_MODE_10baseT_Full_BIT, 4708c2ecf20Sopenharmony_ci phydev->supported, 4718c2ecf20Sopenharmony_ci val & MDIO_PMA_EXTABLE_10BT); 4728c2ecf20Sopenharmony_ci linkmode_mod_bit(ETHTOOL_LINK_MODE_10baseT_Half_BIT, 4738c2ecf20Sopenharmony_ci phydev->supported, 4748c2ecf20Sopenharmony_ci val & MDIO_PMA_EXTABLE_10BT); 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci if (val & MDIO_PMA_EXTABLE_NBT) { 4778c2ecf20Sopenharmony_ci val = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, 4788c2ecf20Sopenharmony_ci MDIO_PMA_NG_EXTABLE); 4798c2ecf20Sopenharmony_ci if (val < 0) 4808c2ecf20Sopenharmony_ci return val; 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci linkmode_mod_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, 4838c2ecf20Sopenharmony_ci phydev->supported, 4848c2ecf20Sopenharmony_ci val & MDIO_PMA_NG_EXTABLE_2_5GBT); 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci linkmode_mod_bit(ETHTOOL_LINK_MODE_5000baseT_Full_BIT, 4878c2ecf20Sopenharmony_ci phydev->supported, 4888c2ecf20Sopenharmony_ci val & MDIO_PMA_NG_EXTABLE_5GBT); 4898c2ecf20Sopenharmony_ci } 4908c2ecf20Sopenharmony_ci } 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci return 0; 4938c2ecf20Sopenharmony_ci} 4948c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(genphy_c45_pma_read_abilities); 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci/** 4978c2ecf20Sopenharmony_ci * genphy_c45_read_status - read PHY status 4988c2ecf20Sopenharmony_ci * @phydev: target phy_device struct 4998c2ecf20Sopenharmony_ci * 5008c2ecf20Sopenharmony_ci * Reads status from PHY and sets phy_device members accordingly. 5018c2ecf20Sopenharmony_ci */ 5028c2ecf20Sopenharmony_ciint genphy_c45_read_status(struct phy_device *phydev) 5038c2ecf20Sopenharmony_ci{ 5048c2ecf20Sopenharmony_ci int ret; 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci ret = genphy_c45_read_link(phydev); 5078c2ecf20Sopenharmony_ci if (ret) 5088c2ecf20Sopenharmony_ci return ret; 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci phydev->speed = SPEED_UNKNOWN; 5118c2ecf20Sopenharmony_ci phydev->duplex = DUPLEX_UNKNOWN; 5128c2ecf20Sopenharmony_ci phydev->pause = 0; 5138c2ecf20Sopenharmony_ci phydev->asym_pause = 0; 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci if (phydev->autoneg == AUTONEG_ENABLE) { 5168c2ecf20Sopenharmony_ci ret = genphy_c45_read_lpa(phydev); 5178c2ecf20Sopenharmony_ci if (ret) 5188c2ecf20Sopenharmony_ci return ret; 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci phy_resolve_aneg_linkmode(phydev); 5218c2ecf20Sopenharmony_ci } else { 5228c2ecf20Sopenharmony_ci ret = genphy_c45_read_pma(phydev); 5238c2ecf20Sopenharmony_ci } 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci return ret; 5268c2ecf20Sopenharmony_ci} 5278c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(genphy_c45_read_status); 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci/** 5308c2ecf20Sopenharmony_ci * genphy_c45_config_aneg - restart auto-negotiation or forced setup 5318c2ecf20Sopenharmony_ci * @phydev: target phy_device struct 5328c2ecf20Sopenharmony_ci * 5338c2ecf20Sopenharmony_ci * Description: If auto-negotiation is enabled, we configure the 5348c2ecf20Sopenharmony_ci * advertising, and then restart auto-negotiation. If it is not 5358c2ecf20Sopenharmony_ci * enabled, then we force a configuration. 5368c2ecf20Sopenharmony_ci */ 5378c2ecf20Sopenharmony_ciint genphy_c45_config_aneg(struct phy_device *phydev) 5388c2ecf20Sopenharmony_ci{ 5398c2ecf20Sopenharmony_ci bool changed = false; 5408c2ecf20Sopenharmony_ci int ret; 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci if (phydev->autoneg == AUTONEG_DISABLE) 5438c2ecf20Sopenharmony_ci return genphy_c45_pma_setup_forced(phydev); 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_ci ret = genphy_c45_an_config_aneg(phydev); 5468c2ecf20Sopenharmony_ci if (ret < 0) 5478c2ecf20Sopenharmony_ci return ret; 5488c2ecf20Sopenharmony_ci if (ret > 0) 5498c2ecf20Sopenharmony_ci changed = true; 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci return genphy_c45_check_and_restart_aneg(phydev, changed); 5528c2ecf20Sopenharmony_ci} 5538c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(genphy_c45_config_aneg); 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_ci/* The gen10g_* functions are the old Clause 45 stub */ 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_ciint gen10g_config_aneg(struct phy_device *phydev) 5588c2ecf20Sopenharmony_ci{ 5598c2ecf20Sopenharmony_ci return 0; 5608c2ecf20Sopenharmony_ci} 5618c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(gen10g_config_aneg); 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_cistruct phy_driver genphy_c45_driver = { 5648c2ecf20Sopenharmony_ci .phy_id = 0xffffffff, 5658c2ecf20Sopenharmony_ci .phy_id_mask = 0xffffffff, 5668c2ecf20Sopenharmony_ci .name = "Generic Clause 45 PHY", 5678c2ecf20Sopenharmony_ci .read_status = genphy_c45_read_status, 5688c2ecf20Sopenharmony_ci}; 569