/* "simple" stat list + corresponding marvell_get_*_simple functions are used * on PHYs without a page register
*/ struct marvell_hw_stat_simple { constchar *string;
u8 reg;
u8 bits;
};
if (!(irq_status & MII_M1011_IMASK_INIT)) return IRQ_NONE;
phy_trigger_machine(phydev);
return IRQ_HANDLED;
}
staticint marvell_set_polarity(struct phy_device *phydev, int polarity)
{
u16 val;
switch (polarity) { case ETH_TP_MDI:
val = MII_M1011_PHY_SCR_MDI; break; case ETH_TP_MDI_X:
val = MII_M1011_PHY_SCR_MDI_X; break; case ETH_TP_MDI_AUTO: case ETH_TP_MDI_INVALID: default:
val = MII_M1011_PHY_SCR_AUTO_CROSS; break;
}
err = genphy_config_aneg(phydev); if (err < 0) return err;
if (phydev->autoneg != AUTONEG_ENABLE || changed) { /* A write to speed/duplex bits (that is performed by * genphy_config_aneg() call above) must be followed by * a software reset. Otherwise, the write has no effect.
*/
err = genphy_soft_reset(phydev); if (err < 0) return err;
}
return 0;
}
staticint m88e1101_config_aneg(struct phy_device *phydev)
{ int err;
/* This Marvell PHY has an errata which requires * that certain registers get written in order * to restart autonegotiation
*/
err = genphy_soft_reset(phydev); if (err < 0) return err;
#if IS_ENABLED(CONFIG_OF_MDIO) /* Set and/or override some configuration registers based on the * marvell,reg-init property stored in the of_node for the phydev. * * marvell,reg-init = <reg-page reg mask value>,...; * * There may be one or more sets of <reg-page reg mask value>: * * reg-page: which register bank to use. * reg: the register. * mask: if non-zero, ANDed with existing register value. * value: ORed with the masked value and written to the regiser. *
*/ staticint marvell_of_reg_init(struct phy_device *phydev)
{ const __be32 *paddr; int len, i, saved_page, current_page, ret = 0;
if (!phydev->mdio.dev.of_node) return 0;
paddr = of_get_property(phydev->mdio.dev.of_node, "marvell,reg-init", &len); if (!paddr || len < (4 * sizeof(*paddr))) return 0;
len /= sizeof(*paddr); for (i = 0; i < len - 3; i += 4) {
u16 page = be32_to_cpup(paddr + i);
u16 reg = be32_to_cpup(paddr + i + 1);
u16 mask = be32_to_cpup(paddr + i + 2);
u16 val_bits = be32_to_cpup(paddr + i + 3); int val;
if (page != current_page) {
current_page = page;
ret = marvell_write_page(phydev, page); if (ret < 0) goto err;
}
val = 0; if (mask) {
val = __phy_read(phydev, reg); if (val < 0) {
ret = val; goto err;
}
val &= mask;
}
val |= val_bits;
staticint m88e1121_config_aneg(struct phy_device *phydev)
{ int changed = 0; int err = 0;
if (phy_interface_is_rgmii(phydev)) {
err = m88e1121_config_aneg_rgmii_delays(phydev); if (err < 0) return err;
}
changed = err;
err = marvell_set_polarity(phydev, phydev->mdix_ctrl); if (err < 0) return err;
changed |= err;
err = genphy_config_aneg(phydev); if (err < 0) return err;
if (phydev->autoneg != AUTONEG_ENABLE || changed) { /* A software reset is used to ensure a "commit" of the * changes is done.
*/
err = genphy_soft_reset(phydev); if (err < 0) return err;
}
return 0;
}
staticint m88e1318_config_aneg(struct phy_device *phydev)
{ int err;
/** * linkmode_adv_to_fiber_adv_t * @advertise: the linkmode advertisement settings * * A small helper function that translates linkmode advertisement * settings to phy autonegotiation advertisements for the MII_ADV * register for fiber link.
*/ staticinline u32 linkmode_adv_to_fiber_adv_t(unsignedlong *advertise)
{
u32 result = 0;
if (linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT, advertise))
result |= ADVERTISE_1000XHALF; if (linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, advertise))
result |= ADVERTISE_1000XFULL;
if (linkmode_test_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, advertise) &&
linkmode_test_bit(ETHTOOL_LINK_MODE_Pause_BIT, advertise))
result |= ADVERTISE_1000XPSE_ASYM; elseif (linkmode_test_bit(ETHTOOL_LINK_MODE_Pause_BIT, advertise))
result |= ADVERTISE_1000XPAUSE;
return result;
}
/** * marvell_config_aneg_fiber - restart auto-negotiation or write BMCR * @phydev: target phy_device struct * * Description: If auto-negotiation is enabled, we configure the * advertising, and then restart auto-negotiation. If it is not * enabled, then we write the BMCR. Adapted for fiber link in * some Marvell's devices.
*/ staticint marvell_config_aneg_fiber(struct phy_device *phydev)
{ int changed = 0; int err;
u16 adv;
if (phydev->autoneg != AUTONEG_ENABLE) return genphy_setup_forced(phydev);
/* Only allow advertising what this PHY supports */
linkmode_and(phydev->advertising, phydev->advertising,
phydev->supported);
staticint m88e1111_config_aneg(struct phy_device *phydev)
{ int extsr = phy_read(phydev, MII_M1111_PHY_EXT_SR); int err;
if (extsr < 0) return extsr;
/* If not using SGMII or copper 1000BaseX modes, use normal process. * Steps below are only required for these modes.
*/ if (phydev->interface != PHY_INTERFACE_MODE_SGMII &&
(extsr & MII_M1111_HWCFG_MODE_MASK) !=
MII_M1111_HWCFG_MODE_COPPER_1000X_AN) return marvell_config_aneg(phydev);
err = marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE); if (err < 0) goto error;
/* Configure the copper link first */
err = marvell_config_aneg(phydev); if (err < 0) goto error;
/* Then the fiber link */
err = marvell_set_page(phydev, MII_MARVELL_FIBER_PAGE); if (err < 0) goto error;
if (phydev->interface == PHY_INTERFACE_MODE_SGMII) /* Do not touch the fiber advertisement if we're in copper->sgmii mode. * Just ensure that SGMII-side autonegotiation is enabled. * If we switched from some other mode to SGMII it may not be.
*/
err = genphy_check_and_restart_aneg(phydev, false); else
err = marvell_config_aneg_fiber(phydev); if (err < 0) goto error;
staticint m88e1111_config_init_1000basex(struct phy_device *phydev)
{ int extsr = phy_read(phydev, MII_M1111_PHY_EXT_SR); int err, mode;
if (extsr < 0) return extsr;
/* If using copper mode, ensure 1000BaseX auto-negotiation is enabled. * FIXME: this does not actually enable 1000BaseX auto-negotiation if * it was previously disabled in the Fiber BMCR!
*/
mode = extsr & MII_M1111_HWCFG_MODE_MASK; if (mode == MII_M1111_HWCFG_MODE_COPPER_1000X_NOAN) {
err = phy_modify(phydev, MII_M1111_PHY_EXT_SR,
MII_M1111_HWCFG_MODE_MASK |
MII_M1111_HWCFG_SERIAL_AN_BYPASS,
MII_M1111_HWCFG_MODE_COPPER_1000X_AN |
MII_M1111_HWCFG_SERIAL_AN_BYPASS); if (err < 0) return err;
} return 0;
}
staticint m88e1111_config_init(struct phy_device *phydev)
{ int err;
if (phy_interface_is_rgmii(phydev)) {
err = m88e1111_config_init_rgmii(phydev); if (err < 0) return err;
}
if (phydev->interface == PHY_INTERFACE_MODE_SGMII) {
err = m88e1111_config_init_sgmii(phydev); if (err < 0) return err;
}
if (phydev->interface == PHY_INTERFACE_MODE_RTBI) {
err = m88e1111_config_init_rtbi(phydev); if (err < 0) return err;
}
if (phydev->interface == PHY_INTERFACE_MODE_1000BASEX) {
err = m88e1111_config_init_1000basex(phydev); if (err < 0) return err;
}
err = marvell_of_reg_init(phydev); if (err < 0) return err;
err = genphy_soft_reset(phydev); if (err < 0) return err;
if (phydev->interface == PHY_INTERFACE_MODE_SGMII) { /* If the HWCFG_MODE was changed from another mode (such as * 1000BaseX) to SGMII, the state of the support bits may have * also changed now that the PHY has been reset. * Update the PHY abilities accordingly.
*/
err = genphy_read_abilities(phydev);
linkmode_or(phydev->advertising, phydev->advertising,
phydev->supported);
} return err;
}
/* As per Marvell Release Notes - Alaska 88E1510/88E1518/88E1512/ * 88E1514 Rev A0, Errata Section 5.1: * If EEE is intended to be used, the following register writes * must be done once after every hardware reset.
*/
err = marvell_set_page(phydev, 0x00FF); if (err < 0) return err;
for (i = 0; i < ARRAY_SIZE(errata_vals); ++i) {
err = phy_write(phydev, 17, errata_vals[i].reg17); if (err) return err;
err = phy_write(phydev, 16, errata_vals[i].reg16); if (err) return err;
}
if (*msecs == ETHTOOL_PHY_FAST_LINK_DOWN_OFF) return phy_clear_bits(phydev, MII_88E1540_COPPER_CTRL3,
MII_88E1540_COPPER_CTRL3_FAST_LINK_DOWN);
/* According to the Marvell data sheet EEE must be disabled for * Fast Link Down detection to work properly
*/ if (phydev->eee_cfg.eee_enabled) {
phydev_warn(phydev, "Fast Link Down detection requires EEE to be disabled!\n"); return -EBUSY;
}
if (*msecs <= 5)
val = MII_88E1540_COPPER_CTRL3_LINK_DOWN_DELAY_00MS; elseif (*msecs <= 15)
val = MII_88E1540_COPPER_CTRL3_LINK_DOWN_DELAY_10MS; elseif (*msecs <= 30)
val = MII_88E1540_COPPER_CTRL3_LINK_DOWN_DELAY_20MS; else
val = MII_88E1540_COPPER_CTRL3_LINK_DOWN_DELAY_40MS;
val = FIELD_PREP(MII_88E1540_COPPER_CTRL3_LINK_DOWN_DELAY_MASK, val);
ret = phy_modify(phydev, MII_88E1540_COPPER_CTRL3,
MII_88E1540_COPPER_CTRL3_LINK_DOWN_DELAY_MASK, val); if (ret) return ret;
/* The VOD can be out of specification on link up. Poke an * undocumented register, in an undocumented page, with a magic value * to fix this.
*/ staticint m88e6390_errata(struct phy_device *phydev)
{ int err;
err = phy_write_paged(phydev, 0xf8, 0x08, 0x36); if (err) return err;
return genphy_soft_reset(phydev);
}
staticint m88e6390_config_aneg(struct phy_device *phydev)
{ int err;
err = m88e6390_errata(phydev); if (err) return err;
return m88e1510_config_aneg(phydev);
}
/** * fiber_lpa_mod_linkmode_lpa_t * @advertising: the linkmode advertisement settings * @lpa: value of the MII_LPA register for fiber link * * A small helper function that translates MII_LPA bits to linkmode LP * advertisement settings. Other bits in advertising are left * unchanged.
*/ staticvoid fiber_lpa_mod_linkmode_lpa_t(unsignedlong *advertising, u32 lpa)
{
linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT,
advertising, lpa & LPA_1000XHALF);
/* marvell_read_status_page * * 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.
*/ staticint marvell_read_status_page(struct phy_device *phydev, int page)
{ int status; int fiber; int err;
status = phy_read(phydev, MII_M1011_PHY_STATUS); if (status < 0) return status;
/* Use the generic register for copper link status, * and the PHY status register for fiber link status.
*/ if (page == MII_MARVELL_FIBER_PAGE) {
phydev->link = !!(status & MII_M1011_PHY_STATUS_LINK);
} else {
err = genphy_update_link(phydev); if (err) return err;
}
if (fiber) {
phydev->mdix = ETH_TP_MDI_INVALID;
} else { /* The MDI-X state is set regardless of Autoneg being enabled * and reflects forced MDI-X state as well as auto resolution
*/ if (status & MII_M1011_PHY_STATUS_RESOLVED)
phydev->mdix = status & MII_M1011_PHY_STATUS_MDIX ?
ETH_TP_MDI_X : ETH_TP_MDI; else
phydev->mdix = ETH_TP_MDI_INVALID;
}
/* marvell_read_status * * Some Marvell's phys have two modes: fiber and copper. * Both need status checked. * Description: * First, check the fiber link and status. * If the fiber link is down, check the copper link and status which * will be the default value if both link are down.
*/ staticint marvell_read_status(struct phy_device *phydev)
{ int err;
/* Check the fiber mode first */ if (linkmode_test_bit(ETHTOOL_LINK_MODE_FIBRE_BIT,
phydev->supported) &&
phydev->interface != PHY_INTERFACE_MODE_SGMII) {
err = marvell_set_page(phydev, MII_MARVELL_FIBER_PAGE); if (err < 0) goto error;
err = marvell_read_status_page(phydev, MII_MARVELL_FIBER_PAGE); if (err < 0) goto error;
/* If the fiber link is up, it is the selected and * used link. In this case, we need to stay in the * fiber page. Please to be careful about that, avoid * to restore Copper page in other functions which * could break the behaviour for some fiber phy like * 88E1512.
*/ if (phydev->link) return 0;
/* If fiber link is down, check and save copper mode state */
err = marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE); if (err < 0) goto error;
}
/* marvell_suspend * * Some Marvell's phys have two modes: fiber and copper. * Both need to be suspended
*/ staticint marvell_suspend(struct phy_device *phydev)
{ int err;
/* Suspend the fiber mode first */ if (linkmode_test_bit(ETHTOOL_LINK_MODE_FIBRE_BIT,
phydev->supported)) {
err = marvell_set_page(phydev, MII_MARVELL_FIBER_PAGE); if (err < 0) goto error;
/* With the page set, use the generic suspend */
err = genphy_suspend(phydev); if (err < 0) goto error;
/* Then, the copper link */
err = marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE); if (err < 0) goto error;
}
/* With the page set, use the generic suspend */ return genphy_suspend(phydev);
/* marvell_resume * * Some Marvell's phys have two modes: fiber and copper. * Both need to be resumed
*/ staticint marvell_resume(struct phy_device *phydev)
{ int err;
/* Resume the fiber mode first */ if (linkmode_test_bit(ETHTOOL_LINK_MODE_FIBRE_BIT,
phydev->supported)) {
err = marvell_set_page(phydev, MII_MARVELL_FIBER_PAGE); if (err < 0) goto error;
/* With the page set, use the generic resume */
err = genphy_resume(phydev); if (err < 0) goto error;
/* Then, the copper link */
err = marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE); if (err < 0) goto error;
}
/* With the page set, use the generic resume */ return genphy_resume(phydev);
/* m88e1510_resume * * The 88e1510 PHY has an erratum where the phy downshift counter is not cleared * after phy being suspended(BMCR_PDOWN set) and then later resumed(BMCR_PDOWN * cleared). This can cause the link to intermittently downshift to a lower speed. * * Disabling and re-enabling the downshift feature clears the counter, allowing * the PHY to retry gigabit link negotiation up to the programmed retry count * before downshifting. This behavior has been observed on copper links.
*/ staticint m88e1510_resume(struct phy_device *phydev)
{ int err;
u8 cnt = 0;
err = marvell_resume(phydev); if (err < 0) return err;
/* read downshift counter value */
err = m88e1011_get_downshift(phydev, &cnt); if (err < 0) return err;
if (cnt) { /* downshift disabled */
err = m88e1011_set_downshift(phydev, 0); if (err < 0) return err;
/* downshift enabled, with previous counter value */
err = m88e1011_set_downshift(phydev, cnt);
}
return err;
}
staticint marvell_aneg_done(struct phy_device *phydev)
{ int retval = phy_read(phydev, MII_M1011_PHY_STATUS);
oldpage = phy_save_page(phydev); if (oldpage < 0) goto error;
if (wol->wolopts & (WAKE_MAGIC | WAKE_PHY)) { /* Explicitly switch to page 0x00, just to be sure */
err = marvell_write_page(phydev, MII_MARVELL_COPPER_PAGE); if (err < 0) goto error;
/* If WOL event happened once, the LED[2] interrupt pin * will not be cleared unless we reading the interrupt status * register. If interrupts are in use, the normal interrupt * handling will clear the WOL event. Clear the WOL event * before enabling it if !phy_interrupt_is_valid()
*/ if (!phy_interrupt_is_valid(phydev))
__phy_read(phydev, MII_M1011_IEVENT);
/* Enable the WOL interrupt */
err = __phy_set_bits(phydev, MII_88E1318S_PHY_CSIER,
MII_88E1318S_PHY_CSIER_WOL_EIE); if (err < 0) goto error;
err = marvell_write_page(phydev, MII_MARVELL_LED_PAGE); if (err < 0) goto error;
if (wol->wolopts & WAKE_MAGIC) {
err = marvell_write_page(phydev, MII_MARVELL_WOL_PAGE); if (err < 0) goto error;
/* Store the device address for the magic packet */
err = __phy_write(phydev, MII_88E1318S_PHY_MAGIC_PACKET_WORD2,
((phydev->attached_dev->dev_addr[5] << 8) |
phydev->attached_dev->dev_addr[4])); if (err < 0) goto error;
err = __phy_write(phydev, MII_88E1318S_PHY_MAGIC_PACKET_WORD1,
((phydev->attached_dev->dev_addr[3] << 8) |
phydev->attached_dev->dev_addr[2])); if (err < 0) goto error;
err = __phy_write(phydev, MII_88E1318S_PHY_MAGIC_PACKET_WORD0,
((phydev->attached_dev->dev_addr[1] << 8) |
phydev->attached_dev->dev_addr[0])); if (err < 0) goto error;
/* Clear WOL status and enable magic packet matching */
err = __phy_set_bits(phydev, MII_88E1318S_PHY_WOL_CTRL,
MII_88E1318S_PHY_WOL_CTRL_CLEAR_WOL_STATUS |
MII_88E1318S_PHY_WOL_CTRL_MAGIC_PACKET_MATCH_ENABLE); if (err < 0) goto error;
} else {
err = marvell_write_page(phydev, MII_MARVELL_WOL_PAGE); if (err < 0) goto error;
/* Clear WOL status and disable magic packet matching */
err = __phy_modify(phydev, MII_88E1318S_PHY_WOL_CTRL,
MII_88E1318S_PHY_WOL_CTRL_MAGIC_PACKET_MATCH_ENABLE,
MII_88E1318S_PHY_WOL_CTRL_CLEAR_WOL_STATUS); if (err < 0) goto error;
}
if (wol->wolopts & WAKE_PHY) {
err = marvell_write_page(phydev, MII_MARVELL_WOL_PAGE); if (err < 0) goto error;
/* Clear WOL status and enable link up event */
err = __phy_modify(phydev, MII_88E1318S_PHY_WOL_CTRL, 0,
MII_88E1318S_PHY_WOL_CTRL_CLEAR_WOL_STATUS |
MII_88E1318S_PHY_WOL_CTRL_LINK_UP_ENABLE); if (err < 0) goto error;
} else {
err = marvell_write_page(phydev, MII_MARVELL_WOL_PAGE); if (err < 0) goto error;
/* Clear WOL status and disable link up event */
err = __phy_modify(phydev, MII_88E1318S_PHY_WOL_CTRL,
MII_88E1318S_PHY_WOL_CTRL_LINK_UP_ENABLE,
MII_88E1318S_PHY_WOL_CTRL_CLEAR_WOL_STATUS); if (err < 0) goto error;
}
err = phy_write_paged(phydev, MII_MARVELL_VCT5_PAGE,
MII_VCT5_TX_PULSE_CTRL, reg); if (err) return err;
/* Reading the TDR data is very MDIO heavy. We need to optimize * access to keep the time to a minimum. So lock the bus once, * and don't release it until complete. We can then avoid having * to change the page for every access, greatly speeding things * up.
*/
page = phy_select_page(phydev, MII_MARVELL_VCT5_PAGE); if (page < 0) goto restore_page;
for (distance = priv->first;
distance <= priv->last;
distance += priv->step) {
err = marvell_vct5_amplitude_distance(phydev, distance,
priv->pair); if (err) goto restore_page;
staticint marvell_cable_test_start_common(struct phy_device *phydev)
{ int bmcr, bmsr, ret;
/* If auto-negotiation is enabled, but not complete, the cable * test never completes. So disable auto-neg.
*/
bmcr = phy_read(phydev, MII_BMCR); if (bmcr < 0) return bmcr;
bmsr = phy_read(phydev, MII_BMSR);
if (bmsr < 0) return bmsr;
if (bmcr & BMCR_ANENABLE) {
ret = phy_clear_bits(phydev, MII_BMCR, BMCR_ANENABLE); if (ret < 0) return ret;
ret = genphy_soft_reset(phydev); if (ret < 0) return ret;
}
/* If the link is up, allow it some time to go down */ if (bmsr & BMSR_LSTATUS)
msleep(1500);
ret = marvell_cable_test_start_common(phydev); if (ret) return ret;
priv->cable_test_tdr = false;
/* Reset the VCT5 API control to defaults, otherwise * VCT7 does not work correctly.
*/
ret = phy_write_paged(phydev, MII_MARVELL_VCT5_PAGE,
MII_VCT5_CTRL,
MII_VCT5_CTRL_TX_SAME_CHANNEL |
MII_VCT5_CTRL_SAMPLES_DEFAULT |
MII_VCT5_CTRL_MODE_MAXIMUM_PEEK |
MII_VCT5_CTRL_PEEK_HYST_DEFAULT); if (ret) return ret;
ret = phy_write_paged(phydev, MII_MARVELL_VCT5_PAGE,
MII_VCT5_SAMPLE_POINT_DISTANCE, 0); if (ret) return ret;
ret = phy_read_paged(phydev, MII_MARVELL_VCT7_PAGE, MII_VCT7_CTRL); if (ret < 0) return ret;
meter = ret & MII_VCT7_CTRL_METERS;
if (marvell_vct7_distance_valid(pair0))
marvell_vct7_report_length(phydev, 0, meter); if (marvell_vct7_distance_valid(pair1))
marvell_vct7_report_length(phydev, 1, meter); if (marvell_vct7_distance_valid(pair2))
marvell_vct7_report_length(phydev, 2, meter); if (marvell_vct7_distance_valid(pair3))
marvell_vct7_report_length(phydev, 3, meter);
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.