for (; len; vals++, len--) {
ret = phy_write_mmd(phydev, vals->devad, vals->regnum,
vals->val); if (ret < 0) return ret;
}
return 0;
}
staticint mv88q2xxx_soft_reset(struct phy_device *phydev)
{ int ret; int val;
/* Enable RESET of DCL */ if (phydev->autoneg == AUTONEG_ENABLE || phydev->speed == SPEED_1000) {
ret = phy_write_mmd(phydev, MDIO_MMD_PCS, 0xfe1b, 0x48); if (ret < 0) return ret;
}
ret = phy_write_mmd(phydev, MDIO_MMD_PCS, MDIO_PCS_1000BT1_CTRL,
MDIO_PCS_1000BT1_CTRL_RESET); if (ret < 0) return ret;
ret = phy_read_mmd_poll_timeout(phydev, MDIO_MMD_PCS,
MDIO_PCS_1000BT1_CTRL, val,
!(val & MDIO_PCS_1000BT1_CTRL_RESET),
50000, 600000, true); if (ret < 0) return ret;
ret = phy_write_mmd(phydev, MDIO_MMD_PCS, 0xffe4, 0xc); if (ret < 0) return ret;
/* Disable RESET of DCL */ if (phydev->autoneg == AUTONEG_ENABLE || phydev->speed == SPEED_1000) return phy_write_mmd(phydev, MDIO_MMD_PCS, 0xfe1b, 0x58);
return 0;
}
staticint mv88q2xxx_read_link_gbit(struct phy_device *phydev)
{ int ret; bool link = false;
/* Read vendor specific Auto-Negotiation status register to get local * and remote receiver status according to software initialization * guide. However, when not in polling mode the local and remote * receiver status are not evaluated due to the Marvell 88Q2xxx APIs.
*/
ret = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_MMD_AN_MV_STAT); if (ret < 0) { return ret;
} elseif (((ret & MDIO_MMD_AN_MV_STAT_LOCAL_RX) &&
(ret & MDIO_MMD_AN_MV_STAT_REMOTE_RX)) ||
!phy_polling_mode(phydev)) { /* The link state is latched low so that momentary link * drops can be detected. Do not double-read the status * in polling mode to detect such short link drops except * the link was already down.
*/ if (!phy_polling_mode(phydev) || !phydev->link) {
ret = phy_read_mmd(phydev, MDIO_MMD_PCS,
MDIO_PCS_1000BT1_STAT); if (ret < 0) return ret; elseif (ret & MDIO_PCS_1000BT1_STAT_LINK)
link = true;
}
if (!link) {
ret = phy_read_mmd(phydev, MDIO_MMD_PCS,
MDIO_PCS_1000BT1_STAT); if (ret < 0) return ret; elseif (ret & MDIO_PCS_1000BT1_STAT_LINK)
link = true;
}
}
phydev->link = link;
return 0;
}
staticint mv88q2xxx_read_link_100m(struct phy_device *phydev)
{ int ret;
/* The link state is latched low so that momentary link * drops can be detected. Do not double-read the status * in polling mode to detect such short link drops except * the link was already down. In case we are not polling, * we always read the realtime status.
*/ if (!phy_polling_mode(phydev)) {
phydev->link = false;
ret = phy_read_mmd(phydev, MDIO_MMD_PCS,
MDIO_MMD_PCS_MV_100BT1_STAT2); if (ret < 0) return ret;
if (ret & MDIO_MMD_PCS_MV_100BT1_STAT2_LINK)
phydev->link = true;
ret = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_MMD_PCS_MV_100BT1_STAT1); if (ret < 0) return ret;
out: /* Check if we have link and if the remote and local receiver are ok */ if ((ret & MDIO_MMD_PCS_MV_100BT1_STAT1_LINK) &&
(ret & MDIO_MMD_PCS_MV_100BT1_STAT1_LOCAL_RX) &&
(ret & MDIO_MMD_PCS_MV_100BT1_STAT1_REMOTE_RX))
phydev->link = true; else
phydev->link = false;
return 0;
}
staticint mv88q2xxx_read_link(struct phy_device *phydev)
{ /* The 88Q2XXX PHYs do not have the PMA/PMD status register available, * therefore we need to read the link status from the vendor specific * registers depending on the speed.
*/
staticint mv88q2xxx_read_status(struct phy_device *phydev)
{ int ret;
if (phydev->autoneg == AUTONEG_ENABLE) { /* We have to get the negotiated speed first, otherwise we are * not able to read the link.
*/
ret = mv88q2xxx_read_aneg_speed(phydev); if (ret < 0) return ret;
ret = mv88q2xxx_read_link(phydev); if (ret < 0) return ret;
ret = genphy_c45_read_lpa(phydev); if (ret < 0) return ret;
ret = genphy_c45_baset1_read_status(phydev); if (ret < 0) return ret;
ret = mv88q2xxx_read_master_slave_state(phydev); if (ret < 0) return ret;
phy_resolve_aneg_linkmode(phydev);
return 0;
}
ret = mv88q2xxx_read_link(phydev); if (ret < 0) return ret;
return genphy_c45_read_pma(phydev);
}
staticint mv88q2xxx_get_features(struct phy_device *phydev)
{ int ret;
ret = genphy_c45_pma_read_abilities(phydev); if (ret) return ret;
/* We need to read the baset1 extended abilities manually because the * PHY does not signalize it has the extended abilities register * available.
*/
ret = genphy_c45_pma_baset1_read_abilities(phydev); if (ret) return ret;
return 0;
}
staticint mv88q2xxx_config_aneg(struct phy_device *phydev)
{ int ret;
ret = genphy_c45_config_aneg(phydev); if (ret) return ret;
return phydev->drv->soft_reset(phydev);
}
staticint mv88q2xxx_get_sqi(struct phy_device *phydev)
{ int ret;
if (phydev->speed == SPEED_100) { /* Read the SQI from the vendor specific receiver status * register
*/
ret = phy_read_mmd(phydev, MDIO_MMD_PCS,
MDIO_MMD_PCS_MV_RX_STAT); if (ret < 0) return ret;
ret = ret >> 12;
} else { /* Read from vendor specific registers, they are not documented * but can be found in the Software Initialization Guide. Only * revisions >= A0 are supported.
*/
ret = phy_modify_mmd(phydev, MDIO_MMD_PCS, 0xfc5d, 0xff, 0xac); if (ret < 0) return ret;
ret = phy_read_mmd(phydev, MDIO_MMD_PCS, 0xfc88); if (ret < 0) return ret;
}
staticint mv88q2xxx_config_intr(struct phy_device *phydev)
{ int ret;
if (phydev->interrupts == PHY_INTERRUPT_ENABLED) { /* Enable interrupts for 1000BASE-T1 link up and down events * and enable general interrupts for 100BASE-T1.
*/
ret = phy_write_mmd(phydev, MDIO_MMD_PCS,
MDIO_MMD_PCS_MV_INT_EN,
MDIO_MMD_PCS_MV_INT_EN_LINK_UP |
MDIO_MMD_PCS_MV_INT_EN_LINK_DOWN |
MDIO_MMD_PCS_MV_INT_EN_100BT1); if (ret < 0) return ret;
/* Enable interrupts for 100BASE-T1 link events */ return phy_write_mmd(phydev, MDIO_MMD_PCS,
MDIO_MMD_PCS_MV_100BT1_INT_EN,
MDIO_MMD_PCS_MV_100BT1_INT_EN_LINKEVENT);
} else {
ret = phy_write_mmd(phydev, MDIO_MMD_PCS,
MDIO_MMD_PCS_MV_INT_EN, 0); if (ret < 0) return ret;
/* Before we can acknowledge the 100BT1 general interrupt, that is in * the 1000BT1 interrupt status register, we have to acknowledge any * interrupts that are related to it. Therefore we read first the 100BT1 * interrupt status register, followed by reading the 1000BT1 interrupt * status register.
*/
/* Check link status for 1000BT1 */ if ((irq & MDIO_MMD_PCS_MV_GPIO_INT_STAT_LINK_UP) ||
(irq & MDIO_MMD_PCS_MV_GPIO_INT_STAT_LINK_DOWN))
trigger_machine = true;
if (!trigger_machine) return IRQ_NONE;
phy_trigger_machine(phydev);
return IRQ_HANDLED;
}
staticint mv88q2xxx_suspend(struct phy_device *phydev)
{ int ret;
/* Disable PHY interrupts */ if (phy_interrupt_is_valid(phydev)) {
phydev->interrupts = PHY_INTERRUPT_DISABLED;
ret = mv88q2xxx_config_intr(phydev); if (ret) return ret;
}
/* The 88Q2XXX PHYs do have the extended ability register available, but * register MDIO_PMA_EXTABLE where they should signalize it does not * work according to specification. Therefore, we force it here.
*/
phydev->pma_extable = MDIO_PMA_EXTABLE_BT1;
/* Configure interrupt with default settings, output is driven low for * active interrupt and high for inactive.
*/ if (phy_interrupt_is_valid(phydev)) {
ret = phy_set_bits_mmd(phydev, MDIO_MMD_PCS,
MDIO_MMD_PCS_MV_GPIO_INT_CTRL,
MDIO_MMD_PCS_MV_GPIO_INT_CTRL_TRI_DIS); if (ret < 0) return ret;
}
/* Enable LED function and disable TX disable feature on LED/TX_ENABLE */ if (priv->enable_led0) {
ret = phy_clear_bits_mmd(phydev, MDIO_MMD_PCS,
MDIO_MMD_PCS_MV_RESET_CTRL,
MDIO_MMD_PCS_MV_RESET_CTRL_TX_DISABLE); if (ret < 0) return ret;
}
/* Enable temperature sense again. There might have been a hard reset * of the PHY and in this case the register content is restored to * defaults and we need to enable it again.
*/
ret = mv88q2xxx_enable_temp_sense(phydev); if (ret < 0) return ret;
return 0;
}
staticint mv88q2110_config_init(struct phy_device *phydev)
{ int ret;
ret = mv88q2xxx_write_mmd_vals(phydev, mv88q2110_init_seq0,
ARRAY_SIZE(mv88q2110_init_seq0)); if (ret < 0) return ret;
usleep_range(5000, 10000);
ret = mv88q2xxx_write_mmd_vals(phydev, mv88q2110_init_seq1,
ARRAY_SIZE(mv88q2110_init_seq1)); if (ret < 0) return ret;
return mv88q2xxx_config_init(phydev);
}
staticint mv88q222x_revb0_config_init(struct phy_device *phydev)
{ int ret;
ret = mv88q2xxx_write_mmd_vals(phydev, mv88q222x_revb0_init_seq0,
ARRAY_SIZE(mv88q222x_revb0_init_seq0)); if (ret < 0) return ret;
usleep_range(5000, 10000);
ret = mv88q2xxx_write_mmd_vals(phydev, mv88q222x_revb0_init_seq1,
ARRAY_SIZE(mv88q222x_revb0_init_seq1)); if (ret < 0) return ret;
if (is_rev_b1)
ret = mv88q2xxx_write_mmd_vals(phydev, mv88q222x_revb1_init_seq0,
ARRAY_SIZE(mv88q222x_revb1_init_seq0)); else
ret = mv88q2xxx_write_mmd_vals(phydev, mv88q222x_revb2_init_seq0,
ARRAY_SIZE(mv88q222x_revb2_init_seq0)); if (ret < 0) return ret;
usleep_range(3000, 5000);
ret = mv88q2xxx_write_mmd_vals(phydev, mv88q222x_revb1_revb2_init_seq1,
ARRAY_SIZE(mv88q222x_revb1_revb2_init_seq1)); if (ret < 0) return ret;
val = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_MMD_PCS_MV_LED_FUNC_CTRL); if (val < 0) return val;
if (index == MV88Q2XXX_LED_INDEX_TX_ENABLE)
val = FIELD_GET(MDIO_MMD_PCS_MV_LED_FUNC_CTRL_LED_0_MASK, val); else
val = FIELD_GET(MDIO_MMD_PCS_MV_LED_FUNC_CTRL_LED_1_MASK, val);
switch (val) { case MDIO_MMD_PCS_MV_LED_FUNC_CTRL_LINK:
*rules = BIT(TRIGGER_NETDEV_LINK); break; case MDIO_MMD_PCS_MV_LED_FUNC_CTRL_LINK_1000BT1_ON:
*rules = BIT(TRIGGER_NETDEV_LINK_1000); break; case MDIO_MMD_PCS_MV_LED_FUNC_CTRL_TX:
*rules = BIT(TRIGGER_NETDEV_TX); break; case MDIO_MMD_PCS_MV_LED_FUNC_CTRL_RX_TX:
*rules = BIT(TRIGGER_NETDEV_TX) | BIT(TRIGGER_NETDEV_RX); break; case MDIO_MMD_PCS_MV_LED_FUNC_CTRL_LINK_RX_TX:
*rules = BIT(TRIGGER_NETDEV_LINK) | BIT(TRIGGER_NETDEV_TX) |
BIT(TRIGGER_NETDEV_RX); break; default:
*rules = 0; break;
}
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.