Settings for eth0: Supported ports: [ TP MII ] Supported link modes: 10baseT/Half 10baseT/Full 100baseT/Half 100baseT/Full 1000baseT/Full Supported pause frame use: Symmetric Receive-only Supports auto-negotiation: Yes Supported FEC modes: Not reported Advertised link modes: 10baseT/Half 10baseT/Full 100baseT/Half 100baseT/Full 1000baseT/Full Advertised pause frame use: No Advertised auto-negotiation: Yes Advertised FEC modes: Not reported Link partner advertised link modes: 10baseT/Half 10baseT/Full 100baseT/Half 100baseT/Full 1000baseT/Full Link partner advertised pause frame use: Symmetric Receive-only Link partner advertised auto-negotiation: Yes Link partner advertised FEC modes: Not reported Speed: 100Mb/s Duplex: Full Port: MII PHYAD: 0 Transceiver: external Auto-negotiation: on Supports Wake-on: ug Wake-on: d Current message level: 0x0000003f (63) drv probe link timer ifdown ifup Link detected: yes
可以明显发现的一点是,Linux4.19的系统中的Link partner advertised link modes:中,也是存在1000baseT/Full,也就是说,它认为对端设备是有千兆的能力。而且Advertised link modes:中,也是存在1000baseT/Full的,它也认为自己有千兆的能力。但是它却没有使用千兆的速度。
/** * genphy_read_status - check the link status and update current link state * @phydev: target phy_device struct * * Description: Check the link, then figure out the current state * by comparing what we advertise with what the link partner * advertises. Start by checking the gigabit possibilities, * then move on to 10/100. */ intgenphy_read_status(struct phy_device *phydev) { int adv; int err; int lpa; int lpagb = 0; int common_adv; int common_adv_gb = 0;
/* Update the link, but return if there was an error */ err = genphy_update_link(phydev); if (err) return err;
phydev->lp_advertising = 0;
if (AUTONEG_ENABLE == phydev->autoneg) { if (phydev->supported & (SUPPORTED_1000baseT_Half | SUPPORTED_1000baseT_Full)) { lpagb = phy_read(phydev, MII_STAT1000); if (lpagb < 0) return lpagb;
adv = phy_read(phydev, MII_CTRL1000); if (adv < 0) return adv;
/** * genphy_read_status - check the link status and update current link state * @phydev: target phy_device struct * * Description: Check the link, then figure out the current state * by comparing what we advertise with what the link partner * advertises. Start by checking the gigabit possibilities, * then move on to 10/100. */ intgenphy_read_status(struct phy_device *phydev) { int err, old_link = phydev->link;
/* Update the link, but return if there was an error */ err = genphy_update_link(phydev); if (err) return err;
/* why bother the PHY if nothing can have changed */ if (phydev->autoneg == AUTONEG_ENABLE && old_link && phydev->link) return0;
/** * phy_resolve_aneg_linkmode - resolve the advertisements into PHY settings * @phydev: The phy_device struct * * Resolve our and the link partner advertisements into their corresponding * speed and duplex. If full duplex was negotiated, extract the pause mode * from the link partner mask. */ voidphy_resolve_aneg_linkmode(struct phy_device *phydev) { __ETHTOOL_DECLARE_LINK_MODE_MASK(common); int i;
for (i = 0; i < ARRAY_SIZE(settings); i++) if (test_bit(settings[i].bit, common)) { phydev->speed = settings[i].speed; phydev->duplex = settings[i].duplex; break; }
/** * phy_probe - probe and init a PHY device * @dev: device to probe and init * * Description: Take care of setting up the phy_device structure, * set the state to READY (the driver's init function should * set it to STARTING if needed). */ staticintphy_probe(struct device *dev) { structphy_device *phydev = to_phy_device(dev); structdevice_driver *drv = phydev->mdio.dev.driver; structphy_driver *phydrv = to_phy_driver(drv); int err = 0;
phydev->drv = phydrv;
/* Disable the interrupt if the PHY doesn't support it * but the interrupt is still a valid one */ if (!phy_drv_supports_irq(phydrv) && phy_interrupt_is_valid(phydev)) phydev->irq = PHY_POLL;
if (phydrv->flags & PHY_IS_INTERNAL) phydev->is_internal = true;
mutex_lock(&phydev->lock);
/* Deassert the reset signal */ phy_device_reset(phydev, 0);
if (phydev->drv->probe) { err = phydev->drv->probe(phydev); if (err) goto out; }
/* Start out supporting everything. Eventually, * a controller will attach, and may modify one * or both of these values */ if (phydrv->features) { linkmode_copy(phydev->supported, phydrv->features); } elseif (phydrv->get_features) { err = phydrv->get_features(phydev); } elseif (phydev->is_c45) { err = genphy_c45_pma_read_abilities(phydev); } else { err = genphy_read_abilities(phydev); }
if (err) goto out;
if (!linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, phydev->supported)) phydev->autoneg = 0;
if (linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT, phydev->supported)) phydev->is_gigabit_capable = 1; if (linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, phydev->supported)) phydev->is_gigabit_capable = 1;
/* Get the EEE modes we want to prohibit. We will ask * the PHY stop advertising these mode later on */ of_set_phy_eee_broken(phydev);
/* The Pause Frame bits indicate that the PHY can support passing * pause frames. During autonegotiation, the PHYs will determine if * they should allow pause frames to pass. The MAC driver should then * use that result to determine whether to enable flow control via * pause frames. * * Normally, PHY drivers should not set the Pause bits, and instead * allow phylib to do that. However, there may be some situations * (e.g. hardware erratum) where the driver wants to set only one * of these bits. */ if (!test_bit(ETHTOOL_LINK_MODE_Pause_BIT, phydev->supported) && !test_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, phydev->supported)) { linkmode_set_bit(ETHTOOL_LINK_MODE_Pause_BIT, phydev->supported); linkmode_set_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, phydev->supported); }
/* Set the state to READY by default */ phydev->state = PHY_READY;
out: /* Assert the reset signal */ if (err) phy_device_reset(phydev, 1);
val = phy_read(phydev, MII_BMSR); if (val < 0) return val;
linkmode_mod_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, phydev->supported, val & BMSR_ANEGCAPABLE);
linkmode_mod_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT, phydev->supported, val & BMSR_100FULL); linkmode_mod_bit(ETHTOOL_LINK_MODE_100baseT_Half_BIT, phydev->supported, val & BMSR_100HALF); linkmode_mod_bit(ETHTOOL_LINK_MODE_10baseT_Full_BIT, phydev->supported, val & BMSR_10FULL); linkmode_mod_bit(ETHTOOL_LINK_MODE_10baseT_Half_BIT, phydev->supported, val & BMSR_10HALF);
if (val & BMSR_ESTATEN) { val = phy_read(phydev, MII_ESTATUS); if (val < 0) return val;
linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, phydev->supported, val & ESTATUS_1000_TFULL); linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT, phydev->supported, val & ESTATUS_1000_THALF); linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseX_Full_BIT, phydev->supported, val & ESTATUS_1000_XFULL); }
/** * phy_remove_link_mode - Remove a supported link mode * @phydev: phy_device structure to remove link mode from * @link_mode: Link mode to be removed * * Description: Some MACs don't support all link modes which the PHY * does. e.g. a 1G MAC often does not support 1000Half. Add a helper * to remove a link mode. */ voidphy_advertise_supported(struct phy_device *phydev) { __ETHTOOL_DECLARE_LINK_MODE_MASK(new);
[ 14.611725] RTL8211F Gigabit Ethernet stmmac-0:01: Downshift occurred from negotiated speed 1Gbps to actual speed 100Mbps, check cabling! [ 14.613638] rk_gmac-dwmac 2a220000.ethernet eth0: Link is Up - 100Mbps/Full - flow control rx/tx [ 14.613669] IPv6: ADDRCONF(NETDEV_CHANGE): eth0: link becomes ready
/** * phy_check_downshift - check whether downshift occurred * @phydev: The phy_device struct * * Check whether a downshift to a lower speed occurred. If this should be the * case warn the user. * Prerequisite for detecting downshift is that PHY driver implements the * read_status callback and sets phydev->speed to the actual link speed. */ voidphy_check_downshift(struct phy_device *phydev) { __ETHTOOL_DECLARE_LINK_MODE_MASK(common); int i, speed = SPEED_UNKNOWN;
phydev->downshifted_rate = 0;
if (phydev->autoneg == AUTONEG_DISABLE || phydev->speed == SPEED_UNKNOWN) return;
for (i = 0; i < ARRAY_SIZE(settings); i++) if (test_bit(settings[i].bit, common)) { speed = settings[i].speed; break; }
if (speed == SPEED_UNKNOWN || phydev->speed >= speed) return;
phydev_warn(phydev, "Downshift occurred from negotiated speed %s to actual speed %s, check cabling!\n", phy_speed_to_str(speed), phy_speed_to_str(phydev->speed));
然后在下面判断实际的phydev->speed的值与应设的speed的大小,如果phydev->speed小于speed,则使用更小的phydev->speed,并打印出报错:Downshift occurred from negotiated speed 1Gbps to actual speed 100Mbps, check cabling!
/** * phy_check_link_status - check link status and set state accordingly * @phydev: the phy_device struct * * Description: Check for link and whether autoneg was triggered / is running * and set state accordingly */ staticintphy_check_link_status(struct phy_device *phydev) { int err;
WARN_ON(!mutex_is_locked(&phydev->lock));
/* Keep previous state if loopback is enabled because some PHYs * report that Link is Down when loopback is enabled. */ if (phydev->loopback_enabled) return0;
err = phy_read_status(phydev); if (err) return err;