if (on)
reg |= BCM54810_EXP_BROADREACH_LRE_MISC_CTL_EN; else
reg &= ~BCM54810_EXP_BROADREACH_LRE_MISC_CTL_EN;
err = bcm_phy_write_exp(phydev,
BCM54810_EXP_BROADREACH_LRE_MISC_CTL, reg); if (err) return err;
/* Ensure LRE or IEEE register set is accessed according to the brr *on/off,thussettheoverride
*/
val = BCM54811_EXP_BROADREACH_LRE_OVERLAY_CTL_EN; if (!on)
val |= BCM54811_EXP_BROADREACH_LRE_OVERLAY_CTL_OVERRIDE_VAL;
/* Enable CLK125 MUX on LED4 if ref clock is enabled. */ if (!(phydev->dev_flags & PHY_BRCM_RX_REFCLK_UNUSED)) {
reg = bcm_phy_read_exp(phydev, BCM54612E_EXP_SPARE0); if (reg < 0) return reg;
err = bcm_phy_write_exp(phydev, BCM54612E_EXP_SPARE0,
BCM54612E_LED4_CLK125OUT_EN | reg); if (err < 0) return err;
}
/* With BCM54811, BroadR-Reach implies no autoneg */ if (priv->brr_mode)
phydev->autoneg = 0;
/* Enable MII Lite (No TXER, RXER, CRS, COL) if configured */ if (phydev->interface == PHY_INTERFACE_MODE_MIILITE)
exp_sync_ethernet = BCM_EXP_SYNC_ETHERNET_MII_LITE; else
exp_sync_ethernet = 0;
switch (BRCM_PHY_MODEL(phydev)) { case PHY_ID_BCM50610: case PHY_ID_BCM50610M:
err = bcm54xx_config_clock_delay(phydev); break; case PHY_ID_BCM54210E:
err = bcm54210e_config_init(phydev); break; case PHY_ID_BCM54612E:
err = bcm54612e_config_init(phydev); break; case PHY_ID_BCM54616S:
err = bcm54616s_config_init(phydev); break; case PHY_ID_BCM54810: /* For BCM54810, we need to disable BroadR-Reach function */
val = bcm_phy_read_exp(phydev,
BCM54810_EXP_BROADREACH_LRE_MISC_CTL);
val &= ~BCM54810_EXP_BROADREACH_LRE_MISC_CTL_EN;
err = bcm_phy_write_exp(phydev,
BCM54810_EXP_BROADREACH_LRE_MISC_CTL,
val); break; case PHY_ID_BCM54811:
err = bcm54811_config_init(phydev); break;
} if (err) return err;
bcm54xx_phydsp_config(phydev);
/* For non-SFP setups, encode link speed into LED1 and LED3 pair *(green/amber). *AlsoflashthesetwoLEDsonactivity.Thismeansconfiguring *themforMULTICOLORandencodinglink/activityintothem. *Don'tdothisfordevicesonanSFPmodule,sincesomeofthese *usetheLEDoutputstocontroltheSFPLOSsignal,andchanging *thesesettingswillcauseLOStomalfunction.
*/ if (!phy_on_sfp(phydev)) {
val = BCM54XX_SHD_LEDS1_LED1(BCM_LED_SRC_MULTICOLOR1) |
BCM54XX_SHD_LEDS1_LED3(BCM_LED_SRC_MULTICOLOR1);
bcm_phy_write_shadow(phydev, BCM54XX_SHD_LEDS1, val);
/* Acknowledge any left over interrupt and charge the device for *wake-up.
*/
err = bcm_phy_read_exp(phydev, BCM54XX_WOL_INT_STATUS); if (err < 0) return err;
if (err)
pm_wakeup_event(&phydev->mdio.dev, 0);
return0;
}
staticint bcm54xx_iddq_set(struct phy_device *phydev, bool enable)
{ int ret = 0;
if (!(phydev->dev_flags & PHY_BRCM_IDDQ_SUSPEND)) return ret;
ret = bcm_phy_read_exp(phydev, BCM54XX_TOP_MISC_IDDQ_CTRL); if (ret < 0) goto out;
if (enable)
ret |= BCM54XX_TOP_MISC_IDDQ_SR | BCM54XX_TOP_MISC_IDDQ_LP; else
ret &= ~(BCM54XX_TOP_MISC_IDDQ_SR | BCM54XX_TOP_MISC_IDDQ_LP);
ret = bcm_phy_write_exp(phydev, BCM54XX_TOP_MISC_IDDQ_CTRL, ret);
out: return ret;
}
staticint bcm54xx_set_wakeup_irq(struct phy_device *phydev, bool state)
{ struct bcm54xx_phy_priv *priv = phydev->priv; int ret = 0;
if (!bcm54xx_phy_can_wakeup(phydev)) return ret;
if (priv->wake_irq_enabled != state) { if (state)
ret = enable_irq_wake(priv->wake_irq); else
ret = disable_irq_wake(priv->wake_irq);
priv->wake_irq_enabled = state;
}
return ret;
}
staticint bcm54xx_suspend(struct phy_device *phydev)
{ int ret = 0;
bcm54xx_ptp_stop(phydev);
/* Acknowledge any Wake-on-LAN interrupt prior to suspend */
ret = bcm_phy_read_exp(phydev, BCM54XX_WOL_INT_STATUS); if (ret < 0) return ret;
if (phydev->wol_enabled) return bcm54xx_set_wakeup_irq(phydev, true);
/* We cannot use a read/modify/write here otherwise the PHY gets into *abadstatewhereitsLEDskeepflashing,thusdefeatingthepurpose *oflowpowermode.
*/
ret = phy_write(phydev, MII_BMCR, BMCR_PDOWN); if (ret < 0) return ret;
return bcm54xx_iddq_set(phydev, true);
}
staticint bcm54xx_resume(struct phy_device *phydev)
{ int ret = 0;
if (phydev->wol_enabled) {
ret = bcm54xx_set_wakeup_irq(phydev, false); if (ret) return ret;
}
ret = bcm54xx_iddq_set(phydev, false); if (ret < 0) return ret;
/* Writes to register other than BMCR would be ignored *unlessweclearthePDOWNbitfirst
*/
ret = genphy_resume(phydev); if (ret < 0) return ret;
/* Upon exiting power down, the PHY remains in an internal reset state *for40us
*/
fsleep(40);
/* Issue a soft reset after clearing the power down bit *andbeforedoinganyotherconfiguration.
*/ if (phydev->dev_flags & PHY_BRCM_IDDQ_SUSPEND) {
ret = genphy_soft_reset(phydev); if (ret < 0) return ret;
}
/* Set BroadR-Reach mode as configured in the DT. */
err = bcm5481x_set_brrmode(phydev, priv->brr_mode); if (err) return err;
if (priv->brr_mode) {
linkmode_set_bit_array(phy_basic_ports_array,
ARRAY_SIZE(phy_basic_ports_array),
phydev->supported);
val = phy_read(phydev, MII_BCM54XX_LRESR); if (val < 0) return val;
/* BCM54811 is not capable of LDS but the corresponding bit *inLRESRissetto1andmarked"Ignore"inthedatasheet. *Sowemustreadthebcm54811asunabletoauto-negotiate *inBroadR-Reachmode.
*/ if (BRCM_PHY_MODEL(phydev) == PHY_ID_BCM54811)
aneg = 0; else
aneg = val & LRESR_LDSABILITY;
linkmode_mod_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
phydev->supported,
aneg);
linkmode_mod_bit(ETHTOOL_LINK_MODE_100baseT1_Full_BIT,
phydev->supported,
val & LRESR_100_1PAIR);
linkmode_mod_bit(ETHTOOL_LINK_MODE_10baseT1BRR_Full_BIT,
phydev->supported,
val & LRESR_10_1PAIR); return0;
}
/* Aneg firstly. */ if (priv->brr_mode) { /* BCM54811 is only capable of autonegotiation in IEEE mode. *InBroadR-Reachmode,disabletheLongDistanceSignaling, *theBRRmodeautonegassupportedinotherBroadcomPHYs. *Thisbitismarkedas"Reserved"and"Default1,mustbe *writtento0aftereverydevicereset"inthedatasheet.
*/
ret = phy_modify(phydev, MII_BCM54XX_LRECR, LRECR_LDSEN, 0); if (ret < 0) return ret;
ret = bcm_config_lre_aneg(phydev, false);
} else {
ret = genphy_config_aneg(phydev);
}
if (ret) return ret;
/* Then we can set up the delay and swap. */ return bcm5481x_config_delay_swap(phydev);
}
staticint bcm54616s_probe(struct phy_device *phydev)
{ struct bcm54616s_phy_priv *priv; int val;
priv = devm_kzalloc(&phydev->mdio.dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM;
phydev->priv = priv;
val = bcm_phy_read_shadow(phydev, BCM54XX_SHD_MODE); if (val < 0) return val;
/* The PHY is strapped in RGMII-fiber mode when INTERF_SEL[1:0] *is01b,andthelinkbetweenPHYanditslinkpartnercanbe *either1000Base-Xor100Base-FX. *RGMII-1000Base-Xisproperlysupported,butRGMII-100Base-FX *supportisstillmissingasofnow.
*/ if ((val & BCM54XX_SHD_INTF_SEL_MASK) == BCM54XX_SHD_INTF_SEL_RGMII) {
val = bcm_phy_read_shadow(phydev, BCM54616S_SHD_100FX_CTRL); if (val < 0) return val;
/* Bit 0 of the SerDes 100-FX Control register, when set *to1,setstheMII/RGMII->100BASE-FXconfiguration. *Whenthisbitissetto0,itsetstheGMII/RGMII-> *1000BASE-Xconfiguration.
*/ if (!(val & BCM54616S_100FX_MODE))
priv->mode_1000bx_en = true;
/* Enable auto MDIX */
err = __phy_set_bits(phydev, MII_BRCM_FET_SHDW_MISCCTRL,
MII_BRCM_FET_SHDW_MC_FAME); if (err < 0) goto done;
}
if (phydev->dev_flags & PHY_BRCM_AUTO_PWRDWN_ENABLE) { /* Enable auto power down */
err = __phy_set_bits(phydev, MII_BRCM_FET_SHDW_AUXSTAT2,
MII_BRCM_FET_SHDW_AS2_APDE);
}
staticint brcm_fet_suspend(struct phy_device *phydev)
{ int reg, err, err2, brcmtest;
/* We cannot use a read/modify/write here otherwise the PHY continues *todriveLEDswhichdefeatsthepurposeoflowpowermode.
*/
err = phy_write(phydev, MII_BMCR, BMCR_PDOWN); if (err < 0) return err;
staticint bcm5221_config_aneg(struct phy_device *phydev)
{ int ret, val;
ret = genphy_config_aneg(phydev); if (ret) return ret;
switch (phydev->mdix_ctrl) { case ETH_TP_MDI:
val = BCM5221_AEGSR_MDIX_DIS; break; case ETH_TP_MDI_X:
val = BCM5221_AEGSR_MDIX_DIS | BCM5221_AEGSR_MDIX_MAN_SWAP; break; case ETH_TP_MDI_AUTO:
val = 0; break; default: return0;
}
staticvoid bcm54xx_phy_get_wol(struct phy_device *phydev, struct ethtool_wolinfo *wol)
{ /* We cannot wake-up if we do not have a dedicated PHY interrupt line *oranoutofbandGPIOdescriptorforwake-up.Zeroing *wol->supportedallowsthecaller(MACdriver)toplaythroughand *offeritsownWake-on-LANschemeifavailable.
*/ if (!bcm54xx_phy_can_wakeup(phydev)) {
wol->supported = 0; return;
}
bcm_phy_get_wol(phydev, wol);
}
staticint bcm54xx_phy_set_wol(struct phy_device *phydev, struct ethtool_wolinfo *wol)
{ int ret;
/* We cannot wake-up if we do not have a dedicated PHY interrupt line *oranoutofbandGPIOdescriptorforwake-up.Returning-EOPNOTSUPP *allowsthecaller(MACdriver)toplaythroughandofferitsown *Wake-on-LANschemeifavailable.
*/ if (!bcm54xx_phy_can_wakeup(phydev)) return -EOPNOTSUPP;
ret = bcm_phy_set_wol(phydev, wol); if (ret < 0) return ret;
return0;
}
staticint bcm54xx_phy_probe(struct phy_device *phydev)
{ struct bcm54xx_phy_priv *priv; struct gpio_desc *wakeup_gpio; int ret = 0;
priv = devm_kzalloc(&phydev->mdio.dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM;
priv->wake_irq = -ENXIO;
phydev->priv = priv;
priv->stats = devm_kcalloc(&phydev->mdio.dev,
bcm_phy_get_sset_count(phydev), sizeof(u64),
GFP_KERNEL); if (!priv->stats) return -ENOMEM;
priv->ptp = bcm_ptp_probe(phydev); if (IS_ERR(priv->ptp)) return PTR_ERR(priv->ptp);
/* We cannot utilize the _optional variant here since we want to know *whethertheGPIOdescriptorexistsornottoadvertiseWake-on-LAN *supportornot.
*/
wakeup_gpio = devm_gpiod_get(&phydev->mdio.dev, "wakeup", GPIOD_IN); if (PTR_ERR(wakeup_gpio) == -EPROBE_DEFER) return PTR_ERR(wakeup_gpio);
if (!IS_ERR(wakeup_gpio)) {
priv->wake_irq = gpiod_to_irq(wakeup_gpio);
/* Dummy interrupt handler which is not enabled but is provided *inorderfortheinterruptdescriptortobefullyset-up.
*/
ret = devm_request_irq(&phydev->mdio.dev, priv->wake_irq,
bcm_phy_wol_isr,
IRQF_TRIGGER_LOW | IRQF_NO_AUTOEN,
dev_name(&phydev->mdio.dev), phydev); if (ret) return ret;
}
/* If we do not have a main interrupt or a side-band wake-up interrupt, *thenthedevicecannotbemarkedaswake-upcapable.
*/ if (!bcm54xx_phy_can_wakeup(phydev)) return0;
/* Don't change the DAC wake settings if auto power down *isnotrequested.
*/ if (!(phydev->dev_flags & PHY_BRCM_AUTO_PWRDWN_ENABLE)) return;
ret = bcm_phy_read_exp(phydev, MII_BCM54XX_EXP_EXP08); if (ret < 0) return;
/* Enable/disable 10BaseT auto and forced early DAC wake depending *onthenegotiatedspeed,thosesettingsshouldonlybedone *for10Mbits/sec.
*/ if (phydev->speed == SPEED_10)
ret |= mask; else
ret &= ~mask;
bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_EXP08, ret);
}
staticint lre_read_master_slave(struct phy_device *phydev)
{ int cfg = MASTER_SLAVE_CFG_UNKNOWN, state; int val;
/* In BroadR-Reach mode we are always capable of master-slave *andthereisnopreferredmasterorslaveconfiguration
*/
phydev->master_slave_get = MASTER_SLAVE_CFG_UNKNOWN;
phydev->master_slave_state = MASTER_SLAVE_STATE_UNKNOWN;
val = phy_read(phydev, MII_BCM54XX_LRECR); if (val < 0) return val;
if ((val & LRECR_LDSEN) == 0) { if (val & LRECR_MASTER)
cfg = MASTER_SLAVE_CFG_MASTER_FORCE; else
cfg = MASTER_SLAVE_CFG_SLAVE_FORCE;
}
val = phy_read(phydev, MII_BCM54XX_LRELDSE); if (val < 0) return val;
if (val & LDSE_MASTER)
state = MASTER_SLAVE_STATE_MASTER; else
state = MASTER_SLAVE_STATE_SLAVE;
/* Read LDS Link Partner Ability in BroadR-Reach mode */ staticint lre_read_lpa(struct phy_device *phydev)
{ int i, lrelpa;
if (phydev->autoneg != AUTONEG_ENABLE) { if (!phydev->autoneg_complete) { /* aneg not yet done, reset all relevant bits */ for (i = 0; i < ARRAY_SIZE(lds_br_bits); i++)
linkmode_clear_bit(lds_br_bits[i],
phydev->lp_advertising);
return0;
}
/* Long-Distance Signaling Link Partner Ability */
lrelpa = phy_read(phydev, MII_BCM54XX_LRELPA); if (lrelpa < 0) return lrelpa;
lrecr = phy_read(phydev, MII_BCM54XX_LRECR); if (lrecr < 0) return lrecr;
/* Autoneg is being started, therefore disregard BMSR value and *reportlinkasdown.
*/ if (lrecr & BMCR_ANRESTART) goto done;
/* The link state is latched low so that momentary link *dropscanbedetected.Donotdouble-readthestatus *inpollingmodetodetectsuchshortlinkdropsexcept *thelinkwasalreadydown.
*/ if (!phy_polling_mode(phydev) || !phydev->link) {
status = phy_read(phydev, MII_BCM54XX_LRESR); if (status < 0) return status; elseif (status & LRESR_LSTATUS) goto done;
}
/* Read link and autonegotiation status */
status = phy_read(phydev, MII_BCM54XX_LRESR); if (status < 0) return status;
done:
phydev->link = status & LRESR_LSTATUS ? 1 : 0;
phydev->autoneg_complete = status & LRESR_LDSCOMPLETE ? 1 : 0;
/* Consider the case that autoneg was started and "aneg complete" *bithasbeenreset,but"linkup"bitnotyet.
*/ if (phydev->autoneg == AUTONEG_ENABLE && !phydev->autoneg_complete)
phydev->link = 0;
return0;
}
/* Get the status in BroadRReach mode just like genphy_read_status does *innormalmode
*/ staticint bcm54811_lre_read_status(struct phy_device *phydev)
{ int err, old_link = phydev->link;
/* Update the link, but return if there was an error */
err = lre_update_link(phydev); if (err) return err;
/* why bother the PHY if nothing can have changed */ if (phydev->autoneg ==
AUTONEG_ENABLE && old_link && phydev->link) return0;
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.