// SPDX-License-Identifier: GPL-2.0+ /* * Marvell 10G 88x3310 PHY driver * * Based upon the ID registers, this PHY appears to be a mixture of IPs * from two different companies. * * There appears to be several different data paths through the PHY which * are automatically managed by the PHY. The following has been determined * via observation and experimentation for a setup using single-lane Serdes: * * SGMII PHYXS -- BASE-T PCS -- 10G PMA -- AN -- Copper (for <= 1G) * 10GBASE-KR PHYXS -- BASE-T PCS -- 10G PMA -- AN -- Copper (for 10G) * 10GBASE-KR PHYXS -- BASE-R PCS -- Fiber * * With XAUI, observation shows: * * XAUI PHYXS -- <appropriate PCS as above> * * and no switching of the host interface mode occurs. * * If both the fiber and copper ports are connected, the first to gain * link takes priority and the other port is completely locked out.
*/ #include <linux/bitfield.h> #include <linux/ctype.h> #include <linux/delay.h> #include <linux/hwmon.h> #include <linux/marvell_phy.h> #include <linux/phy.h> #include <linux/sfp.h> #include <linux/netdevice.h>
/* These registers appear at 0x800X and 0xa00X - the 0xa00X control * registers appear to set themselves to the 0x800X when AN is * restarted, but status registers appear readable from either.
*/
MV_AN_CTRL1000 = 0x8000, /* 1000base-T control register */
MV_AN_STAT1000 = 0x8001, /* 1000base-T status register */
staticint mv3310_hwmon_probe(struct phy_device *phydev)
{ struct device *dev = &phydev->mdio.dev; struct mv3310_priv *priv = dev_get_drvdata(&phydev->mdio.dev); int i, j, ret;
priv->hwmon_name = devm_kstrdup(dev, dev_name(dev), GFP_KERNEL); if (!priv->hwmon_name) return -ENODEV;
for (i = j = 0; priv->hwmon_name[i]; i++) { if (isalnum(priv->hwmon_name[i])) { if (i != j)
priv->hwmon_name[j] = priv->hwmon_name[i];
j++;
}
}
priv->hwmon_name[j] = '\0';
ret = mv3310_hwmon_config(phydev, true); if (ret) return ret;
ret = phy_clear_bits_mmd(phydev, MDIO_MMD_VEND2, MV_V2_PORT_CTRL,
MV_V2_PORT_CTRL_PWRDOWN);
/* Sometimes, the power down bit doesn't clear immediately, and * a read of this register causes the bit not to clear. Delay * 100us to allow the PHY to come out of power down mode before * the next access.
*/
udelay(100);
if (phydev->drv->phy_id != MARVELL_PHY_ID_88X3310 ||
priv->firmware_ver < 0x00030000) return ret;
val = phy_read_mmd(phydev, MDIO_MMD_PCS, MV_PCS_DSC1); if (val < 0) return val;
if (val & MV_PCS_DSC1_ENABLE) /* assume that all fields are the same */
*ds = 1 + FIELD_GET(MV_PCS_DSC1_10GBT, (u16)val); else
*ds = DOWNSHIFT_DEV_DISABLE;
if (ds == DOWNSHIFT_DEV_DISABLE) return phy_clear_bits_mmd(phydev, MDIO_MMD_PCS, MV_PCS_DSC1,
MV_PCS_DSC1_ENABLE);
/* DOWNSHIFT_DEV_DEFAULT_COUNT is confusing. It looks like it should * set the default settings for the PHY. However, it is used for * "ethtool --set-phy-tunable ethN downshift on". The intention is * to enable downshift at a default number of retries. The default * settings for 88x3310 are for two retries with downshift disabled. * So let's use two retries with downshift enabled.
*/ if (ds == DOWNSHIFT_DEV_DEFAULT_COUNT)
ds = 2;
if (ds > 8) return -E2BIG;
ds -= 1;
val = FIELD_PREP(MV_PCS_DSC2_2P5G, ds);
val |= FIELD_PREP(MV_PCS_DSC2_5G, ds);
err = phy_modify_mmd(phydev, MDIO_MMD_PCS, MV_PCS_DSC2,
MV_PCS_DSC2_2P5G | MV_PCS_DSC2_5G, val); if (err < 0) return err;
val = MV_PCS_DSC1_ENABLE;
val |= FIELD_PREP(MV_PCS_DSC1_10GBT, ds);
val |= FIELD_PREP(MV_PCS_DSC1_1GBR, ds);
val |= FIELD_PREP(MV_PCS_DSC1_100BTX, ds);
staticint mv3310_resume(struct phy_device *phydev)
{ int ret;
ret = mv3310_power_up(phydev); if (ret) return ret;
return mv3310_hwmon_config(phydev, true);
}
/* Some PHYs in the Alaska family such as the 88X3310 and the 88E2010 * don't set bit 14 in PMA Extended Abilities (1.11), although they do * support 2.5GBASET and 5GBASET. For these models, we can still read their * 2.5G/5G extended abilities register (1.21). We detect these models based on * the PMA device identifier, with a mask matching models known to have this * issue
*/ staticbool mv3310_has_pma_ngbaset_quirk(struct phy_device *phydev)
{ if (!(phydev->c45_ids.devices_in_package & MDIO_DEVS_PMAPMD)) returnfalse;
/* Only some revisions of the 88X3310 family PMA seem to be impacted */ return (phydev->c45_ids.device_ids[MDIO_MMD_PMAPMD] &
MV_PHY_ALASKA_NBT_QUIRK_MASK) == MV_PHY_ALASKA_NBT_QUIRK_REV;
}
staticint mv2110_get_mactype(struct phy_device *phydev)
{ int mactype;
/* Check that the PHY interface type is compatible */ if (!test_bit(phydev->interface, priv->supported_interfaces)) return -ENODEV;
phydev->mdix_ctrl = ETH_TP_MDI_AUTO;
/* Power up so reset works */
err = mv3310_power_up(phydev); if (err) return err;
/* If host provided host supported interface modes, try to select the * best one
*/ if (!phy_interface_empty(phydev->host_interfaces)) {
mactype = chip->select_mactype(phydev->host_interfaces); if (mactype >= 0) {
phydev_info(phydev, "Changing MACTYPE to %i\n",
mactype);
err = chip->set_mactype(phydev, mactype); if (err) return err;
}
}
mactype = chip->get_mactype(phydev); if (mactype < 0) return mactype;
staticint mv3310_get_features(struct phy_device *phydev)
{ int ret, val;
ret = genphy_c45_pma_read_abilities(phydev); if (ret) return ret;
if (mv3310_has_pma_ngbaset_quirk(phydev)) {
val = phy_read_mmd(phydev, MDIO_MMD_PMAPMD,
MDIO_PMA_NG_EXTABLE); if (val < 0) return val;
linkmode_mod_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT,
phydev->supported,
val & MDIO_PMA_NG_EXTABLE_2_5GBT);
linkmode_mod_bit(ETHTOOL_LINK_MODE_5000baseT_Full_BIT,
phydev->supported,
val & MDIO_PMA_NG_EXTABLE_5GBT);
}
return 0;
}
staticint mv3310_config_mdix(struct phy_device *phydev)
{
u16 val; int err;
switch (phydev->mdix_ctrl) { case ETH_TP_MDI_AUTO:
val = MV_PCS_CSCR1_MDIX_AUTO; break; case ETH_TP_MDI_X:
val = MV_PCS_CSCR1_MDIX_MDIX; break; case ETH_TP_MDI:
val = MV_PCS_CSCR1_MDIX_MDI; break; default: return -EINVAL;
}
ret = mv3310_config_mdix(phydev); if (ret < 0) return ret;
if (phydev->autoneg == AUTONEG_DISABLE) return genphy_c45_pma_setup_forced(phydev);
ret = genphy_c45_an_config_aneg(phydev); if (ret < 0) return ret; if (ret > 0)
changed = true;
/* Clause 45 has no standardized support for 1000BaseT, therefore * use vendor registers for this mode.
*/
reg = linkmode_adv_to_mii_ctrl1000_t(phydev->advertising);
ret = phy_modify_mmd_changed(phydev, MDIO_MMD_AN, MV_AN_CTRL1000,
ADVERTISE_1000FULL | ADVERTISE_1000HALF, reg); if (ret < 0) return ret; if (ret > 0)
changed = true;
/* In all of the "* with Rate Matching" modes the PHY interface is fixed * at 10Gb. The PHY adapts the rate to actual wire speed with help of * internal 16KB buffer. * * In USXGMII mode the PHY interface mode is also fixed.
*/ if (priv->mactype->fixed_interface) {
phydev->interface = priv->mactype->interface_10g; return;
}
/* The PHY automatically switches its serdes interface (and active PHYXS * instance) between Cisco SGMII, 2500BaseX, 5GBase-R and 10GBase-R / * xaui / rxaui modes according to the speed. * Florian suggests setting phydev->interface to communicate this to the * MAC. Only do this if we are already in one of the above modes.
*/ switch (phydev->speed) { case SPEED_10000:
phydev->interface = priv->mactype->interface_10g; break; case SPEED_5000:
phydev->interface = PHY_INTERFACE_MODE_5GBASER; break; case SPEED_2500:
phydev->interface = PHY_INTERFACE_MODE_2500BASEX; break; case SPEED_1000: case SPEED_100: case SPEED_10:
phydev->interface = PHY_INTERFACE_MODE_SGMII; break; default: break;
}
}
/* 10GBASE-ER,LR,LRM,SR do not support autonegotiation. */ staticint mv3310_read_status_10gbaser(struct phy_device *phydev)
{
phydev->link = 1;
phydev->speed = SPEED_10000;
phydev->duplex = DUPLEX_FULL;
phydev->port = PORT_FIBRE;
return 0;
}
staticint mv3310_read_status_copper(struct phy_device *phydev)
{ int cssr1, speed, val;
val = genphy_c45_read_link(phydev); if (val < 0) return val;
val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_STAT1); if (val < 0) return val;
staticvoid mv3110_get_wol(struct phy_device *phydev, struct ethtool_wolinfo *wol)
{ int ret;
wol->supported = WAKE_MAGIC;
wol->wolopts = 0;
ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, MV_V2_WOL_CTRL); if (ret < 0) return;
if (ret & MV_V2_WOL_CTRL_MAGIC_PKT_EN)
wol->wolopts |= WAKE_MAGIC;
}
staticint mv3110_set_wol(struct phy_device *phydev, struct ethtool_wolinfo *wol)
{ int ret;
if (wol->wolopts & WAKE_MAGIC) { /* Enable the WOL interrupt */
ret = phy_set_bits_mmd(phydev, MDIO_MMD_VEND2,
MV_V2_PORT_INTR_MASK,
MV_V2_PORT_INTR_STS_WOL_EN); if (ret < 0) return ret;
/* Store the device address for the magic packet */
ret = phy_write_mmd(phydev, MDIO_MMD_VEND2,
MV_V2_MAGIC_PKT_WORD2,
((phydev->attached_dev->dev_addr[5] << 8) |
phydev->attached_dev->dev_addr[4])); if (ret < 0) return ret;
ret = phy_write_mmd(phydev, MDIO_MMD_VEND2,
MV_V2_MAGIC_PKT_WORD1,
((phydev->attached_dev->dev_addr[3] << 8) |
phydev->attached_dev->dev_addr[2])); if (ret < 0) return ret;
ret = phy_write_mmd(phydev, MDIO_MMD_VEND2,
MV_V2_MAGIC_PKT_WORD0,
((phydev->attached_dev->dev_addr[1] << 8) |
phydev->attached_dev->dev_addr[0])); if (ret < 0) return ret;
/* Clear WOL status and enable magic packet matching */
ret = phy_set_bits_mmd(phydev, MDIO_MMD_VEND2,
MV_V2_WOL_CTRL,
MV_V2_WOL_CTRL_MAGIC_PKT_EN |
MV_V2_WOL_CTRL_CLEAR_STS); if (ret < 0) return ret;
} else { /* Disable magic packet matching & reset WOL status bit */
ret = phy_modify_mmd(phydev, MDIO_MMD_VEND2,
MV_V2_WOL_CTRL,
MV_V2_WOL_CTRL_MAGIC_PKT_EN,
MV_V2_WOL_CTRL_CLEAR_STS); if (ret < 0) return ret;
}
/* Reset the clear WOL status bit as it does not self-clear */ return phy_clear_bits_mmd(phydev, MDIO_MMD_VEND2,
MV_V2_WOL_CTRL,
MV_V2_WOL_CTRL_CLEAR_STS);
}
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.