staticint smsc_phy_reset(struct phy_device *phydev)
{ int rc = phy_read(phydev, MII_LAN83C185_SPECIAL_MODES); if (rc < 0) return rc;
/* If the SMSC PHY is in power down mode, then set it * in all capable mode before using it.
*/ if ((rc & MII_LAN83C185_MODE_MASK) == MII_LAN83C185_MODE_POWERDOWN) { /* set "all capable" mode */
rc |= MII_LAN83C185_MODE_ALL;
phy_write(phydev, MII_LAN83C185_SPECIAL_MODES, rc);
}
/* reset the phy */ return genphy_soft_reset(phydev);
}
staticint lan87xx_config_aneg(struct phy_device *phydev)
{
u8 mdix_ctrl; int val; int rc;
/* When auto-negotiation is disabled (forced mode), the PHY's * Auto-MDIX will continue toggling the TX/RX pairs. * * To establish a stable link, we must select a fixed MDI mode. * If the user has not specified a fixed MDI mode (i.e., mdix_ctrl is * 'auto'), we default to ETH_TP_MDI. This choice of a ETH_TP_MDI mode * mirrors the behavior the hardware would exhibit if the AUTOMDIX_EN * strap were configured for a fixed MDI connection.
*/ if (phydev->autoneg == AUTONEG_DISABLE) { if (phydev->mdix_ctrl == ETH_TP_MDI_AUTO)
mdix_ctrl = ETH_TP_MDI; else
mdix_ctrl = phydev->mdix_ctrl;
} else {
mdix_ctrl = phydev->mdix_ctrl;
}
switch (mdix_ctrl) { case ETH_TP_MDI:
val = SPECIAL_CTRL_STS_OVRRD_AMDIX_; break; case ETH_TP_MDI_X:
val = SPECIAL_CTRL_STS_OVRRD_AMDIX_ |
SPECIAL_CTRL_STS_AMDIX_STATE_; break; case ETH_TP_MDI_AUTO:
val = SPECIAL_CTRL_STS_OVRRD_AMDIX_ |
SPECIAL_CTRL_STS_AMDIX_ENABLE_; break; default: return genphy_config_aneg(phydev);
}
rc = phy_read(phydev, SPECIAL_CTRL_STS); if (rc < 0) return rc;
staticint lan95xx_config_aneg_ext(struct phy_device *phydev)
{ if (phydev->phy_id == 0x0007c0f0) { /* LAN9500A or LAN9505A */ /* Extend Manual AutoMDIX timer */ int rc = phy_set_bits(phydev, PHY_EDPD_CONFIG,
PHY_EDPD_CONFIG_EXT_CROSSOVER_);
if (rc < 0) return rc;
}
return lan87xx_config_aneg(phydev);
}
/* * The LAN87xx suffers from rare absence of the ENERGYON-bit when Ethernet cable * plugs in while LAN87xx is in Energy Detect Power-Down mode. This leads to * unstable detection of plugging in Ethernet cable. * This workaround disables Energy Detect Power-Down mode and waiting for * response on link pulses to detect presence of plugged Ethernet cable. * The Energy Detect Power-Down mode is enabled again in the end of procedure to * save approximately 220 mW of power if cable is unplugged. * The workaround is only applicable to poll mode. Energy Detect Power-Down may * not be used in interrupt mode lest link change detection becomes unreliable.
*/ int lan87xx_read_status(struct phy_device *phydev)
{ struct smsc_phy_priv *priv = phydev->priv; int err;
err = genphy_read_status(phydev); if (err) return err;
if (!phydev->link && priv && priv->edpd_enable &&
priv->edpd_max_wait_ms) { unsignedint max_wait = priv->edpd_max_wait_ms * 1000; int rc;
/* Disable EDPD to wake up PHY */
rc = phy_read(phydev, MII_LAN83C185_CTRL_STATUS); if (rc < 0) return rc;
/* Wait max 640 ms to detect energy and the timeout is not * an actual error.
*/
read_poll_timeout(phy_read, rc,
rc & MII_LAN83C185_ENERGYON || rc < 0,
10000, max_wait, true, phydev,
MII_LAN83C185_CTRL_STATUS); if (rc < 0) return rc;
staticint lan87xx_phy_config_init(struct phy_device *phydev)
{ int rc;
/* The LAN87xx PHY's initial MDI-X mode is determined by the AUTOMDIX_EN * hardware strap, but the driver cannot read the strap's status. This * creates an unpredictable initial state. * * To ensure consistent and reliable behavior across all boards, * override the strap configuration on initialization and force the PHY * into a known state with Auto-MDIX enabled, which is the expected * default for modern hardware.
*/
rc = phy_modify(phydev, SPECIAL_CTRL_STS,
SPECIAL_CTRL_STS_OVRRD_AMDIX_ |
SPECIAL_CTRL_STS_AMDIX_ENABLE_ |
SPECIAL_CTRL_STS_AMDIX_STATE_,
SPECIAL_CTRL_STS_OVRRD_AMDIX_ |
SPECIAL_CTRL_STS_AMDIX_ENABLE_); if (rc < 0) return rc;
phydev->mdix_ctrl = ETH_TP_MDI_AUTO;
return smsc_phy_config_init(phydev);
}
staticint lan874x_phy_config_init(struct phy_device *phydev)
{
u16 val; int rc;
/* Setup LED2/nINT/nPME pin to function as nPME. May need user option * to use LED1/nINT/nPME.
*/
val = MII_LAN874X_PHY_PME2_SET;
/* The bits MII_LAN874X_PHY_WOL_PFDA_FR, MII_LAN874X_PHY_WOL_WUFR, * MII_LAN874X_PHY_WOL_MPR, and MII_LAN874X_PHY_WOL_BCAST_FR need to * be cleared to de-assert PME signal after a WoL event happens, but * using PME auto clear gets around that.
*/
val |= MII_LAN874X_PHY_PME_SELF_CLEAR;
rc = phy_write_mmd(phydev, MDIO_MMD_PCS, MII_LAN874X_PHY_MMD_WOL_WUCSR,
val); if (rc < 0) return rc;
/* set nPME self clear delay time */
rc = phy_write_mmd(phydev, MDIO_MMD_PCS, MII_LAN874X_PHY_MMD_MCFGR,
MII_LAN874X_PHY_PME_SELF_CLEAR_DELAY); if (rc < 0) return rc;
staticint lan874x_chk_wol_pattern(const u8 pattern[], const u16 *mask,
u8 len, u8 *data, u8 *datalen)
{
size_t i, j, k; int ret = 0;
u16 bits;
/* Pattern filtering can match up to 128 bytes of frame data. There * are 8 registers to program the 16-bit masks, where each bit means * the byte will be compared. The frame data will then go through a * CRC16 calculation for hardware comparison. This helper function * makes sure only relevant frame data are included in this * calculation. It provides a warning when the masks and expected * data size do not match.
*/
i = 0;
k = 0; while (len > 0) {
bits = *mask; for (j = 0; j < 16; j++, i++, len--) { /* No more pattern. */ if (!len) { /* The rest of bitmap is not empty. */ if (bits)
ret = i + 1; break;
} if (bits & 1)
data[k++] = pattern[i];
bits >>= 1;
}
mask++;
}
*datalen = k; return ret;
}
/* Starting pattern offset is set before calling this function. */
val |= MII_LAN874X_PHY_WOL_FILTER_EN;
rc = phy_write_mmd(phydev, MDIO_MMD_PCS,
MII_LAN874X_PHY_MMD_WOL_WUF_CFGA, val); if (rc < 0) return rc;
/* lan874x has only one WoL filter pattern */ if ((wol->wolopts & (WAKE_ARP | WAKE_MCAST)) ==
(WAKE_ARP | WAKE_MCAST)) {
phydev_info(phydev, "lan874x WoL supports one of ARP|MCAST at a time\n"); return -EOPNOTSUPP;
}
/* Need to use pattern matching */ if (wol->wolopts & (WAKE_ARP | WAKE_MCAST))
val_wucsr |= MII_LAN874X_PHY_WOL_WUEN; else
val_wucsr &= ~MII_LAN874X_PHY_WOL_WUEN;
rc = lan874x_chk_wol_pattern(pattern, mask, 2, data,
&datalen); if (rc)
phydev_dbg(phydev, "pattern not valid at %d\n", rc);
/* Need to match broadcast destination address and provided * data pattern at offset 12.
*/
val = 12 | MII_LAN874X_PHY_WOL_FILTER_BCSTEN;
rc = lan874x_set_wol_pattern(phydev, val, data, datalen, mask,
2); if (rc < 0) return rc;
priv->wol_arp = true;
}
if (wol->wolopts & WAKE_MCAST) { /* Need to match multicast destination address. */
val = MII_LAN874X_PHY_WOL_FILTER_MCASTTEN;
rc = lan874x_set_wol_pattern(phydev, val, data, 0, NULL, 0); if (rc < 0) return rc;
priv->wol_arp = false;
}
if (wol->wolopts & (WAKE_MAGIC | WAKE_UCAST)) { const u8 *mac = (const u8 *)ndev->dev_addr; int i, reg;
reg = MII_LAN874X_PHY_MMD_WOL_RX_ADDRC; for (i = 0; i < 6; i += 2, reg--) {
rc = phy_write_mmd(phydev, MDIO_MMD_PCS, reg,
((mac[i + 1] << 8) | mac[i])); if (rc < 0) return rc;
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.