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 * replicates data to achieve lower speeds. Hereby, in this * default configuration we need to advertise all supported * modes and not only the ones we want to use.
*/
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); return 0;
}
return 1;
}
return 0;
}
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); return 0;
}
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);
return 0;
}
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: what about the port modes and autoneg bit? This masks * all those away.
*/ for (i = 0; compat->supported[i] != __ETHTOOL_LINK_MODE_MASK_NBITS; i++)
set_bit(compat->supported[i], xpcs_supported);
/* For AN for C37 SGMII mode, the settings are :- * 1) VR_MII_MMD_CTRL Bit(12) [AN_ENABLE] = 0b (Disable SGMII AN in case it is already enabled) * 2) VR_MII_AN_CTRL Bit(2:1)[PCS_MODE] = 10b (SGMII AN) * 3) VR_MII_AN_CTRL Bit(3) [TX_CONFIG] = 0b (MAC side SGMII) * DW xPCS used with DW EQoS MAC is always MAC side SGMII. * 4) VR_MII_DIG_CTRL1 Bit(9) [MAC_AUTO_SW] = 1b (Automatic * speed/duplex mode change by HW after SGMII AN complete) * 5) VR_MII_MMD_CTRL Bit(12) [AN_ENABLE] = 1b (Enable SGMII AN) * * Note that VR_MII_MMD_CTRL is MII_BMCR. * * Note: Since it is MAC side SGMII, there is no need to set * SR_MII_AN_ADV. MAC side SGMII receives AN Tx Config from * PHY about the link state change after C28 AN is completed * between PHY and Link Partner. There is also no need to * trigger AN restart for MAC-side SGMII.
*/
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 | DW_VR_MII_TX_CONFIG_MASK;
val = FIELD_PREP(DW_VR_MII_PCS_MODE_MASK,
DW_VR_MII_PCS_MODE_C37_SGMII);
if (xpcs->info.pma == WX_TXGBE_XPCS_PMA_10G_ID) {
mask |= DW_VR_MII_AN_CTRL_8BIT;
val |= DW_VR_MII_AN_CTRL_8BIT; /* Hardware requires it to be PHY side SGMII */
tx_conf = DW_VR_MII_TX_CONFIG_PHY_SIDE_SGMII;
} else {
tx_conf = DW_VR_MII_TX_CONFIG_MAC_SIDE_SGMII;
}
val |= FIELD_PREP(DW_VR_MII_TX_CONFIG_MASK, tx_conf);
ret = xpcs_modify(xpcs, MDIO_MMD_VEND2, DW_VR_MII_AN_CTRL, mask, val); if (ret < 0) return ret;
val = 0;
mask = DW_VR_MII_DIG_CTRL1_2G5_EN | DW_VR_MII_DIG_CTRL1_MAC_AUTO_SW;
if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED)
val = DW_VR_MII_DIG_CTRL1_MAC_AUTO_SW;
if (xpcs->info.pma == WX_TXGBE_XPCS_PMA_10G_ID) {
mask |= DW_VR_MII_DIG_CTRL1_PHY_MODE_CTRL;
val |= DW_VR_MII_DIG_CTRL1_PHY_MODE_CTRL;
}
ret = xpcs_modify(xpcs, MDIO_MMD_VEND2, DW_VR_MII_DIG_CTRL1, mask, val); 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);
/* According to Chap 7.12, to set 1000BASE-X C37 AN, AN must * be disabled first:- * 1) VR_MII_MMD_CTRL Bit(12)[AN_ENABLE] = 0b * 2) VR_MII_AN_CTRL Bit(2:1)[PCS_MODE] = 00b (1000BASE-X C37) * * Note that VR_MII_MMD_CTRL is MII_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 * register accordingly.
*/
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 * SGMII and 1000base-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 * avoid unnecessary re-reads of this register to avoid missing * a link-down event.
*/
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) return 0;
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 * avoid unnecessary re-reads of this register to avoid missing * a link-down event.
*/
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, speed and duplex.
*/
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;
/** * xpcs_config_eee_mult_fact() - set the EEE clock multiplying factor * @xpcs: pointer to a &struct dw_xpcs instance * @mult_fact: the multiplying factor * * Configure the EEE clock multiplying factor. This value should be such that * clk_eee_time_period * (mult_fact + 1) is within the range 80 to 120ns.
*/ void xpcs_config_eee_mult_fact(struct dw_xpcs *xpcs, u8 mult_fact)
{
xpcs->eee_mult_fact = mult_fact;
}
EXPORT_SYMBOL_GPL(xpcs_config_eee_mult_fact);
staticint xpcs_read_ids(struct dw_xpcs *xpcs)
{ int ret;
u32 id;
/* First, search C73 PCS using PCS MMD 3. Return ENODEV if communication * failed indicating that device couldn't be reached.
*/
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/KX4 PCS found. Otherwise fallback to detecting 1000Base-X or C37 * PCS in MII MMD 31.
*/ 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 * with respect to what is done with the PCS ID...
*/
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;
/** * xpcs_create_mdiodev() - create a DW xPCS instance with the MDIO @addr * @bus: pointer to the MDIO-bus descriptor for the device to be looked at * @addr: device MDIO-bus ID * * Return: a pointer to the DW XPCS handle if successful, otherwise -ENODEV if * the PCS device couldn't be found on the bus and other negative errno related * to the data allocation and MDIO-bus communications.
*/ struct dw_xpcs *xpcs_create_mdiodev(struct mii_bus *bus, int addr)
{ struct mdio_device *mdiodev; struct dw_xpcs *xpcs;
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. If xpcs_create() fails, this will free the mdio * device here. In any case, we don't need to hold our reference * anymore, and putting it here will allow mdio_device_put() in * xpcs_destroy() to automatically free the mdio device.
*/
mdio_device_put(mdiodev);
/** * xpcs_create_fwnode() - Create a DW xPCS instance from @fwnode * @fwnode: fwnode handle poining to the DW XPCS device * * Return: a pointer to the DW XPCS handle if successful, otherwise -ENODEV if * the fwnode device is unavailable or the PCS device couldn't be found on the * bus, -EPROBE_DEFER if the respective MDIO-device instance couldn't be found, * other negative errno related to the data allocations and MDIO-bus * communications.
*/ struct dw_xpcs *xpcs_create_fwnode(struct fwnode_handle *fwnode)
{ struct mdio_device *mdiodev; struct dw_xpcs *xpcs;
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. If xpcs_create() fails, this will free the mdio * device here. In any case, we don't need to hold our reference * anymore, and putting it here will allow mdio_device_put() in * xpcs_destroy() to automatically free the mdio device.
*/
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.