switch (compat->an_mode) { case DW_AN_C73: case DW_10GBASER:
dev = MDIO_MMD_PCS; break; case DW_AN_C37_SGMII: case DW_2500BASEX: case DW_AN_C37_1000BASEX:
dev = MDIO_MMD_VEND2; break; default: return -EINVAL;
}
ret = xpcs_write(xpcs, dev, MII_BMCR, BMCR_RESET); if (ret < 0) return ret;
/* By default, in USXGMII mode XPCS operates at 10G baud and *replicatesdatatoachievelowerspeeds.Hereby,inthis *defaultconfigurationweneedtoadvertiseallsupported *modesandnotonlytheoneswewanttouse.
*/
if (an_stat1 & MDIO_AN_STAT1_COMPLETE) {
ret = xpcs_read(xpcs, MDIO_MMD_AN, MDIO_AN_LPA); if (ret < 0) return ret;
/* Check if Aneg outcome is valid */ if (!(ret & DW_C73_AN_ADV_SF)) {
xpcs_config_aneg_c73(xpcs, compat); return0;
}
return1;
}
return0;
}
staticint xpcs_read_lpa_c73(struct dw_xpcs *xpcs, struct phylink_link_state *state, u16 an_stat1)
{
u16 lpa[3]; int i, ret;
if (!(an_stat1 & MDIO_AN_STAT1_LPABLE)) {
phylink_clear(state->lp_advertising, Autoneg); return0;
}
phylink_set(state->lp_advertising, Autoneg);
/* Read Clause 73 link partner advertisement */ for (i = ARRAY_SIZE(lpa); --i >= 0; ) {
ret = xpcs_read(xpcs, MDIO_MMD_AN, MDIO_AN_LPA + i); if (ret < 0) return ret;
lpa[i] = ret;
}
mii_c73_mod_linkmode(state->lp_advertising, lpa);
return0;
}
staticint xpcs_get_max_xlgmii_speed(struct dw_xpcs *xpcs, struct phylink_link_state *state)
{ unsignedlong *adv = state->advertising; int speed = SPEED_UNKNOWN; int bit;
for_each_set_bit(bit, adv, __ETHTOOL_LINK_MODE_MASK_NBITS) { int new_speed = SPEED_UNKNOWN;
switch (bit) { case ETHTOOL_LINK_MODE_25000baseCR_Full_BIT: case ETHTOOL_LINK_MODE_25000baseKR_Full_BIT: case ETHTOOL_LINK_MODE_25000baseSR_Full_BIT:
new_speed = SPEED_25000; break; case ETHTOOL_LINK_MODE_40000baseKR4_Full_BIT: case ETHTOOL_LINK_MODE_40000baseCR4_Full_BIT: case ETHTOOL_LINK_MODE_40000baseSR4_Full_BIT: case ETHTOOL_LINK_MODE_40000baseLR4_Full_BIT:
new_speed = SPEED_40000; break; case ETHTOOL_LINK_MODE_50000baseCR2_Full_BIT: case ETHTOOL_LINK_MODE_50000baseKR2_Full_BIT: case ETHTOOL_LINK_MODE_50000baseSR2_Full_BIT: case ETHTOOL_LINK_MODE_50000baseKR_Full_BIT: case ETHTOOL_LINK_MODE_50000baseSR_Full_BIT: case ETHTOOL_LINK_MODE_50000baseCR_Full_BIT: case ETHTOOL_LINK_MODE_50000baseLR_ER_FR_Full_BIT: case ETHTOOL_LINK_MODE_50000baseDR_Full_BIT:
new_speed = SPEED_50000; break; case ETHTOOL_LINK_MODE_100000baseKR4_Full_BIT: case ETHTOOL_LINK_MODE_100000baseSR4_Full_BIT: case ETHTOOL_LINK_MODE_100000baseCR4_Full_BIT: case ETHTOOL_LINK_MODE_100000baseLR4_ER4_Full_BIT: case ETHTOOL_LINK_MODE_100000baseKR2_Full_BIT: case ETHTOOL_LINK_MODE_100000baseSR2_Full_BIT: case ETHTOOL_LINK_MODE_100000baseCR2_Full_BIT: case ETHTOOL_LINK_MODE_100000baseLR2_ER2_FR2_Full_BIT: case ETHTOOL_LINK_MODE_100000baseDR2_Full_BIT:
new_speed = SPEED_100000; break; default: continue;
}
/* Populate the supported link modes for this PHY interface type. *FIXME:whatabouttheportmodesandautonegbit?Thismasks *allthoseaway.
*/ for (i = 0; compat->supported[i] != __ETHTOOL_LINK_MODE_MASK_NBITS; i++)
set_bit(compat->supported[i], xpcs_supported);
/* According to Chap 7.12, to set 1000BASE-X C37 AN, AN must *bedisabledfirst:- *1)VR_MII_MMD_CTRLBit(12)[AN_ENABLE]=0b *2)VR_MII_AN_CTRLBit(2:1)[PCS_MODE]=00b(1000BASE-XC37) * *NotethatVR_MII_MMD_CTRLisMII_BMCR.
*/
mdio_ctrl = xpcs_read(xpcs, MDIO_MMD_VEND2, MII_BMCR); if (mdio_ctrl < 0) return mdio_ctrl;
if (mdio_ctrl & BMCR_ANENABLE) {
ret = xpcs_write(xpcs, MDIO_MMD_VEND2, MII_BMCR,
mdio_ctrl & ~BMCR_ANENABLE); if (ret < 0) return ret;
}
mask = DW_VR_MII_PCS_MODE_MASK;
val = FIELD_PREP(DW_VR_MII_PCS_MODE_MASK,
DW_VR_MII_PCS_MODE_C37_1000BASEX);
if (!xpcs->pcs.poll) {
mask |= DW_VR_MII_AN_INTR_EN;
val |= DW_VR_MII_AN_INTR_EN;
}
ret = xpcs_modify(xpcs, MDIO_MMD_VEND2, DW_VR_MII_AN_CTRL, mask, val); if (ret < 0) return ret;
/* Check for advertising changes and update the C45 MII ADV *registeraccordingly.
*/
adv = phylink_mii_c22_pcs_encode_advertisement(interface,
advertising); if (adv >= 0) {
ret = xpcs_modify_changed(xpcs, MDIO_MMD_VEND2,
MII_ADVERTISE, 0xffff, adv); if (ret < 0) return ret;
changed = ret;
}
/* Clear CL37 AN complete status */
ret = xpcs_write(xpcs, MDIO_MMD_VEND2, DW_VR_MII_AN_INTR_STS, 0); if (ret < 0) return ret;
if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED) {
ret = xpcs_write(xpcs, MDIO_MMD_VEND2, MII_BMCR,
mdio_ctrl | BMCR_ANENABLE); if (ret < 0) return ret;
}
return changed;
}
staticint xpcs_config_2500basex(struct dw_xpcs *xpcs)
{ int ret;
ret = xpcs_modify(xpcs, MDIO_MMD_VEND2, DW_VR_MII_DIG_CTRL1,
DW_VR_MII_DIG_CTRL1_2G5_EN |
DW_VR_MII_DIG_CTRL1_MAC_AUTO_SW,
DW_VR_MII_DIG_CTRL1_2G5_EN); if (ret < 0) return ret;
compat = xpcs_find_compat(xpcs, interface); if (!compat) return -ENODEV;
if (xpcs->info.pma == WX_TXGBE_XPCS_PMA_10G_ID) { /* Wangxun devices need backplane CL37 AN enabled for *SGMIIand1000base-X
*/ if (interface == PHY_INTERFACE_MODE_SGMII ||
interface == PHY_INTERFACE_MODE_1000BASEX)
xpcs_write_vpcs(xpcs, DW_VR_XS_PCS_DIG_CTRL1,
DW_CL37_BP | DW_EN_VSMMD1);
}
switch (compat->an_mode) { case DW_10GBASER: break; case DW_AN_C73: if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED) {
ret = xpcs_config_aneg_c73(xpcs, compat); if (ret) return ret;
} break; case DW_AN_C37_SGMII:
ret = xpcs_config_aneg_c37_sgmii(xpcs, neg_mode); if (ret) return ret; break; case DW_AN_C37_1000BASEX:
ret = xpcs_config_aneg_c37_1000basex(xpcs, neg_mode,
advertising); if (ret) return ret; break; case DW_2500BASEX:
ret = xpcs_config_2500basex(xpcs); if (ret) return ret; break; default: return -EINVAL;
}
if (compat->pma_config) {
ret = compat->pma_config(xpcs); if (ret) return ret;
}
staticint xpcs_get_state_c73(struct dw_xpcs *xpcs, struct phylink_link_state *state, conststruct dw_xpcs_compat *compat)
{ bool an_enabled; int pcs_stat1; int an_stat1; int ret;
/* The link status bit is latching-low, so it is important to *avoidunnecessaryre-readsofthisregistertoavoidmissing *alink-downevent.
*/
pcs_stat1 = xpcs_read(xpcs, MDIO_MMD_PCS, MDIO_STAT1); if (pcs_stat1 < 0) {
state->link = false; return pcs_stat1;
}
/* Link needs to be read first ... */
state->link = !!(pcs_stat1 & MDIO_STAT1_LSTATUS);
/* ... and then we check the faults. */
ret = xpcs_read_fault_c73(xpcs, state, pcs_stat1); if (ret) {
ret = xpcs_soft_reset(xpcs, compat); if (ret) return ret;
/* There is no point doing anything else if the link is down. */ if (!state->link) return0;
an_enabled = linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
state->advertising); if (an_enabled) { /* The link status bit is latching-low, so it is important to *avoidunnecessaryre-readsofthisregistertoavoidmissing *alink-downevent.
*/
an_stat1 = xpcs_read(xpcs, MDIO_MMD_AN, MDIO_STAT1); if (an_stat1 < 0) {
state->link = false; return an_stat1;
}
/* For C37 SGMII mode, we check DW_VR_MII_AN_INTR_STS for link *status,speedandduplex.
*/
ret = xpcs_read(xpcs, MDIO_MMD_VEND2, DW_VR_MII_AN_INTR_STS); if (ret < 0) return ret;
if (ret & DW_VR_MII_C37_ANSGM_SP_LNKSTS) { int speed_value;
compat = xpcs_find_compat(xpcs, state->interface); if (!compat) return;
switch (compat->an_mode) { case DW_10GBASER:
phylink_mii_c45_pcs_get_state(xpcs->mdiodev, state); break; case DW_AN_C73:
ret = xpcs_get_state_c73(xpcs, state, compat); if (ret)
dev_err(&xpcs->mdiodev->dev, "%s returned %pe\n", "xpcs_get_state_c73", ERR_PTR(ret)); break; case DW_AN_C37_SGMII:
ret = xpcs_get_state_c37_sgmii(xpcs, state); if (ret)
dev_err(&xpcs->mdiodev->dev, "%s returned %pe\n", "xpcs_get_state_c37_sgmii", ERR_PTR(ret)); break; case DW_AN_C37_1000BASEX:
ret = xpcs_get_state_c37_1000basex(xpcs, neg_mode, state); if (ret)
dev_err(&xpcs->mdiodev->dev, "%s returned %pe\n", "xpcs_get_state_c37_1000basex", ERR_PTR(ret)); break; case DW_2500BASEX:
ret = xpcs_get_state_2500basex(xpcs, state); if (ret)
dev_err(&xpcs->mdiodev->dev, "%s returned %pe\n", "xpcs_get_state_2500basex", ERR_PTR(ret)); break; default: return;
}
}
staticvoid xpcs_link_up_sgmii_1000basex(struct dw_xpcs *xpcs, unsignedint neg_mode,
phy_interface_t interface, int speed, int duplex)
{ int ret;
if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED) return;
if (interface == PHY_INTERFACE_MODE_1000BASEX) { if (speed != SPEED_1000) {
dev_err(&xpcs->mdiodev->dev, "%s: speed %dMbps not supported\n",
__func__, speed); return;
}
if (duplex != DUPLEX_FULL)
dev_err(&xpcs->mdiodev->dev, "%s: half duplex not supported\n",
__func__);
}
ret = xpcs_write(xpcs, MDIO_MMD_VEND2, MII_BMCR,
mii_bmcr_encode_fixed(speed, duplex)); if (ret)
dev_err(&xpcs->mdiodev->dev, "%s: xpcs_write returned %pe\n",
__func__, ERR_PTR(ret));
}
staticvoid xpcs_link_up(struct phylink_pcs *pcs, unsignedint neg_mode,
phy_interface_t interface, int speed, int duplex)
{ struct dw_xpcs *xpcs = phylink_pcs_to_xpcs(pcs);
switch (interface) { case PHY_INTERFACE_MODE_USXGMII:
xpcs_link_up_usxgmii(xpcs, speed); break;
case PHY_INTERFACE_MODE_SGMII: case PHY_INTERFACE_MODE_1000BASEX:
xpcs_link_up_sgmii_1000basex(xpcs, neg_mode, interface, speed,
duplex); break;
staticint xpcs_read_ids(struct dw_xpcs *xpcs)
{ int ret;
u32 id;
/* First, search C73 PCS using PCS MMD 3. Return ENODEV if communication *failedindicatingthatdevicecouldn'tbereached.
*/
ret = xpcs_read(xpcs, MDIO_MMD_PCS, MII_PHYSID1); if (ret < 0) return -ENODEV;
id = ret << 16;
ret = xpcs_read(xpcs, MDIO_MMD_PCS, MII_PHYSID2); if (ret < 0) return ret;
id |= ret;
/* If Device IDs are not all zeros or ones, then 10GBase-X/R or C73 *KR/KX4PCSfound.Otherwisefallbacktodetecting1000Base-XorC37 *PCSinMIIMMD31.
*/ if (!id || id == 0xffffffff) {
ret = xpcs_read(xpcs, MDIO_MMD_VEND2, MII_PHYSID1); if (ret < 0) return ret;
id = ret << 16;
ret = xpcs_read(xpcs, MDIO_MMD_VEND2, MII_PHYSID2); if (ret < 0) return ret;
id |= ret;
}
/* Set the PCS ID if it hasn't been pre-initialized */ if (xpcs->info.pcs == DW_XPCS_ID_NATIVE)
xpcs->info.pcs = id;
/* Find out PMA/PMD ID from MMD 1 device ID registers */
ret = xpcs_read(xpcs, MDIO_MMD_PMAPMD, MDIO_DEVID1); if (ret < 0) return ret;
id = ret;
ret = xpcs_read(xpcs, MDIO_MMD_PMAPMD, MDIO_DEVID2); if (ret < 0) return ret;
/* Note the inverted dword order and masked out Model/Revision numbers *withrespecttowhatisdonewiththePCSID...
*/
ret = (ret >> 10) & 0x3F;
id |= ret << 16;
/* Set the PMA ID if it hasn't been pre-initialized */ if (xpcs->info.pma == DW_XPCS_PMA_ID_NATIVE)
xpcs->info.pma = id;
mdiodev = mdio_device_create(bus, addr); if (IS_ERR(mdiodev)) return ERR_CAST(mdiodev);
xpcs = xpcs_create(mdiodev);
/* xpcs_create() has taken a refcount on the mdiodev if it was *successful.Ifxpcs_create()fails,thiswillfreethemdio *devicehere.Inanycase,wedon'tneedtoholdourreference *anymore,andputtingitherewillallowmdio_device_put()in *xpcs_destroy()toautomaticallyfreethemdiodevice.
*/
mdio_device_put(mdiodev);
if (!fwnode_device_is_available(fwnode)) return ERR_PTR(-ENODEV);
mdiodev = fwnode_mdio_find_device(fwnode); if (!mdiodev) return ERR_PTR(-EPROBE_DEFER);
xpcs = xpcs_create(mdiodev);
/* xpcs_create() has taken a refcount on the mdiodev if it was *successful.Ifxpcs_create()fails,thiswillfreethemdio *devicehere.Inanycase,wedon'tneedtoholdourreference *anymore,andputtingitherewillallowmdio_device_put()in *xpcs_destroy()toautomaticallyfreethemdiodevice.
*/
mdio_device_put(mdiodev);
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.