Implement single-pair BroadR-Reach modes on bcm5481x PHY by Broadcom.
Create set of functions alternative to IEEE 802.3 to handle
configuration of these modes on compatible Broadcom PHYs.
Signed-off-by: Kamil Horák (2N) <kamilh@axis.com>
---
drivers/net/phy/bcm-phy-lib.c | 115 ++++++++++
drivers/net/phy/bcm-phy-lib.h | 4 +
drivers/net/phy/broadcom.c | 392 ++++++++++++++++++++++++++++++++--
3 files changed, 493 insertions(+), 18 deletions(-)
diff --git a/drivers/net/phy/bcm-phy-lib.c b/drivers/net/phy/bcm-phy-lib.c
index 876f28fd8256..9e15eb4a5d43 100644
--- a/drivers/net/phy/bcm-phy-lib.c
+++ b/drivers/net/phy/bcm-phy-lib.c
@@ -794,6 +794,49 @@ static int _bcm_phy_cable_test_get_status(struct phy_device *phydev,
return ret;
}
+static int bcm_setup_lre_forced(struct phy_device *phydev)
+{
+ u16 ctl = 0;
+
+ phydev->pause = 0;
+ phydev->asym_pause = 0;
+
+ if (phydev->speed == SPEED_100)
+ ctl |= LRECR_SPEED100;
+
+ if (phydev->duplex != DUPLEX_FULL)
+ return -EOPNOTSUPP;
+
+ return phy_modify(phydev, MII_BCM54XX_LRECR, LRECR_SPEED100, ctl);
+}
+
+/**
+ * bcm_linkmode_adv_to_lre_adv_t - translate linkmode advertisement to LDS
+ * @advertising: the linkmode advertisement settings
+ * Return: LDS Auto-Negotiation Advertised Ability register value
+ *
+ * A small helper function that translates linkmode advertisement
+ * settings to phy LDS autonegotiation advertisements for the
+ * MII_BCM54XX_LREANAA register of Broadcom PHYs capable of LDS
+ */
+static u32 bcm_linkmode_adv_to_lre_adv_t(unsigned long *advertising)
+{
+ u32 result = 0;
+
+ if (linkmode_test_bit(ETHTOOL_LINK_MODE_10baseT1BRR_Full_BIT,
+ advertising))
+ result |= LREANAA_10_1PAIR;
+ if (linkmode_test_bit(ETHTOOL_LINK_MODE_100baseT1_Full_BIT,
+ advertising))
+ result |= LREANAA_100_1PAIR;
+ if (linkmode_test_bit(ETHTOOL_LINK_MODE_Pause_BIT, advertising))
+ result |= LRELPA_PAUSE;
+ if (linkmode_test_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, advertising))
+ result |= LRELPA_PAUSE_ASYM;
+
+ return result;
+}
+
int bcm_phy_cable_test_start(struct phy_device *phydev)
{
return _bcm_phy_cable_test_start(phydev, false);
@@ -1066,6 +1109,78 @@ int bcm_phy_led_brightness_set(struct phy_device *phydev,
}
EXPORT_SYMBOL_GPL(bcm_phy_led_brightness_set);
+int bcm_setup_master_slave(struct phy_device *phydev)
+{
+ u16 ctl = 0;
+
+ switch (phydev->master_slave_set) {
+ case MASTER_SLAVE_CFG_MASTER_PREFERRED:
+ case MASTER_SLAVE_CFG_MASTER_FORCE:
+ ctl = LRECR_MASTER;
+ break;
+ case MASTER_SLAVE_CFG_SLAVE_PREFERRED:
+ case MASTER_SLAVE_CFG_SLAVE_FORCE:
+ break;
+ case MASTER_SLAVE_CFG_UNKNOWN:
+ case MASTER_SLAVE_CFG_UNSUPPORTED:
+ return 0;
+ default:
+ phydev_warn(phydev, "Unsupported Master/Slave mode\n");
+ return -EOPNOTSUPP;
+ }
+
+ return phy_modify_changed(phydev, MII_BCM54XX_LRECR, LRECR_MASTER, ctl);
+}
+EXPORT_SYMBOL_GPL(bcm_setup_master_slave);
+
+int bcm_config_lre_aneg(struct phy_device *phydev, bool changed)
+{
+ int err;
+
+ if (genphy_config_eee_advert(phydev))
+ changed = true;
+
+ err = bcm_setup_master_slave(phydev);
+ if (err < 0)
+ return err;
+ else if (err)
+ changed = true;
+
+ if (phydev->autoneg != AUTONEG_ENABLE)
+ return bcm_setup_lre_forced(phydev);
+
+ err = bcm_config_lre_advert(phydev);
+ if (err < 0)
+ return err;
+ else if (err)
+ changed = true;
+
+ return genphy_check_and_restart_aneg(phydev, changed);
+}
+EXPORT_SYMBOL_GPL(bcm_config_lre_aneg);
+
+/**
+ * bcm_config_lre_advert - sanitize and advertise Long-Distance Signaling
+ * auto-negotiation parameters
+ * @phydev: target phy_device struct
+ * Return: 0 if the PHY's advertisement hasn't changed, < 0 on error,
+ * > 0 if it has changed
+ *
+ * Writes MII_BCM54XX_LREANAA with the appropriate values. The values are to be
+ * sanitized before, to make sure we only advertise what is supported.
+ * The sanitization is done already in phy_ethtool_ksettings_set()
+ */
+int bcm_config_lre_advert(struct phy_device *phydev)
+{
+ u32 adv = bcm_linkmode_adv_to_lre_adv_t(phydev->advertising);
+
+ /* Setup BroadR-Reach mode advertisement */
+ return phy_modify_changed(phydev, MII_BCM54XX_LREANAA,
+ LRE_ADVERTISE_ALL | LREANAA_PAUSE |
+ LREANAA_PAUSE_ASYM, adv);
+}
+EXPORT_SYMBOL_GPL(bcm_config_lre_advert);
+
MODULE_DESCRIPTION("Broadcom PHY Library");
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Broadcom Corporation");
diff --git a/drivers/net/phy/bcm-phy-lib.h b/drivers/net/phy/bcm-phy-lib.h
index b52189e45a84..fecdd66ad736 100644
--- a/drivers/net/phy/bcm-phy-lib.h
+++ b/drivers/net/phy/bcm-phy-lib.h
@@ -121,4 +121,8 @@ irqreturn_t bcm_phy_wol_isr(int irq, void *dev_id);
int bcm_phy_led_brightness_set(struct phy_device *phydev,
u8 index, enum led_brightness value);
+int bcm_setup_master_slave(struct phy_device *phydev);
+int bcm_config_lre_aneg(struct phy_device *phydev, bool changed);
+int bcm_config_lre_advert(struct phy_device *phydev);
+
#endif /* _LINUX_BCM_PHY_LIB_H */
diff --git a/drivers/net/phy/broadcom.c b/drivers/net/phy/broadcom.c
index 370e4ed45098..2aaa2e8bfe49 100644
--- a/drivers/net/phy/broadcom.c
+++ b/drivers/net/phy/broadcom.c
@@ -5,6 +5,8 @@
* Broadcom BCM5411, BCM5421 and BCM5461 Gigabit Ethernet
* transceivers.
*
+ * Broadcom BCM54810, BCM54811 BroadR-Reach transceivers.
+ *
* Copyright (c) 2006 Maciej W. Rozycki
*
* Inspired by code written by Amy Fong.
@@ -38,6 +40,28 @@ struct bcm54xx_phy_priv {
bool wake_irq_enabled;
};
+/* Link modes for BCM58411 PHY */
+static const int bcm54811_linkmodes[] = {
+ ETHTOOL_LINK_MODE_100baseT1_Full_BIT,
+ ETHTOOL_LINK_MODE_10baseT1BRR_Full_BIT,
+ ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
+ ETHTOOL_LINK_MODE_1000baseX_Full_BIT,
+ ETHTOOL_LINK_MODE_1000baseT_Half_BIT,
+ ETHTOOL_LINK_MODE_100baseT_Full_BIT,
+ ETHTOOL_LINK_MODE_100baseT_Half_BIT,
+ ETHTOOL_LINK_MODE_10baseT_Full_BIT,
+ ETHTOOL_LINK_MODE_10baseT_Half_BIT
+};
+
+/* Long-Distance Signaling (BroadR-Reach mode aneg) relevant linkmode bits */
+static const int lds_br_bits[] = {
+ ETHTOOL_LINK_MODE_Autoneg_BIT,
+ ETHTOOL_LINK_MODE_Pause_BIT,
+ ETHTOOL_LINK_MODE_Asym_Pause_BIT,
+ ETHTOOL_LINK_MODE_10baseT1BRR_Full_BIT,
+ ETHTOOL_LINK_MODE_100baseT1_Full_BIT
+};
+
static bool bcm54xx_phy_can_wakeup(struct phy_device *phydev)
{
struct bcm54xx_phy_priv *priv = phydev->priv;
@@ -553,18 +577,92 @@ static int bcm54810_write_mmd(struct phy_device *phydev, int devnum, u16 regnum,
return -EOPNOTSUPP;
}
-static int bcm54811_config_init(struct phy_device *phydev)
+static int bcm5481x_get_brrmode(struct phy_device *phydev, u8 *data)
{
- int err, reg;
+ int reg;
- /* Disable BroadR-Reach function. */
reg = bcm_phy_read_exp(phydev, BCM54810_EXP_BROADREACH_LRE_MISC_CTL);
- reg &= ~BCM54810_EXP_BROADREACH_LRE_MISC_CTL_EN;
- err = bcm_phy_write_exp(phydev, BCM54810_EXP_BROADREACH_LRE_MISC_CTL,
- reg);
- if (err < 0)
+
+ *data = (reg & BCM54810_EXP_BROADREACH_LRE_MISC_CTL_EN) ? 1 : 0;
+
+ return 0;
+}
+
+static int bcm54811_read_abilities(struct phy_device *phydev)
+{
+ int i, val, err;
+ u8 brr_mode;
+
+ for (i = 0; i < ARRAY_SIZE(bcm54811_linkmodes); i++)
+ linkmode_clear_bit(bcm54811_linkmodes[i], phydev->supported);
+
+ err = bcm5481x_get_brrmode(phydev, &brr_mode);
+ if (err)
+ return err;
+
+ if (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;
+
+ linkmode_mod_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
+ phydev->supported, 1);
+ 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);
+ return 0;
+ }
+
+ return genphy_read_abilities(phydev);
+}
+
+static int bcm5481x_set_brrmode(struct phy_device *phydev, bool on)
+{
+ int reg;
+ int err;
+ u16 val;
+
+ reg = bcm_phy_read_exp(phydev, BCM54810_EXP_BROADREACH_LRE_MISC_CTL);
+
+ 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;
+
+ /* Update the abilities based on the current brr on/off setting */
+ err = bcm54811_read_abilities(phydev);
+ if (err)
return err;
+ /* Ensure LRE or IEEE register set is accessed according to the brr
+ * on/off, thus set the override
+ */
+ val = BCM54811_EXP_BROADREACH_LRE_OVERLAY_CTL_EN;
+ if (!on)
+ val |= BCM54811_EXP_BROADREACH_LRE_OVERLAY_CTL_OVERRIDE_VAL;
+
+ return bcm_phy_write_exp(phydev,
+ BCM54811_EXP_BROADREACH_LRE_OVERLAY_CTL, val);
+}
+
+static int bcm54811_config_init(struct phy_device *phydev)
+{
+ struct device_node *np = phydev->mdio.dev.of_node;
+ bool brr = false;
+ int err, reg;
+
err = bcm54xx_config_init(phydev);
/* Enable CLK125 MUX on LED4 if ref clock is enabled. */
@@ -576,29 +674,80 @@ static int bcm54811_config_init(struct phy_device *phydev)
return err;
}
- return err;
+ /* Configure BroadR-Reach function. */
+ brr = of_property_read_bool(np, "brr-mode");
+
+ /* With BCM54811, BroadR-Reach implies no autoneg */
+ if (brr)
+ phydev->autoneg = 0;
+
+ return bcm5481x_set_brrmode(phydev, brr);
}
-static int bcm5481_config_aneg(struct phy_device *phydev)
+static int bcm5481x_config_delay_swap(struct phy_device *phydev)
{
struct device_node *np = phydev->mdio.dev.of_node;
- int ret;
-
- /* Aneg firstly. */
- ret = genphy_config_aneg(phydev);
- /* Then we can set up the delay. */
+ /* Set up the delay. */
bcm54xx_config_clock_delay(phydev);
if (of_property_read_bool(np, "enet-phy-lane-swap")) {
/* Lane Swap - Undocumented register...magic! */
- ret = bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_SEL_ER + 0x9,
- 0x11B);
+ int ret = bcm_phy_write_exp(phydev,
+ MII_BCM54XX_EXP_SEL_ER + 0x9,
+ 0x11B);
if (ret < 0)
return ret;
}
- return ret;
+ return 0;
+}
+
+static int bcm5481_config_aneg(struct phy_device *phydev)
+{
+ u8 brr_mode;
+ int ret;
+
+ ret = bcm5481x_get_brrmode(phydev, &brr_mode);
+ if (ret)
+ return ret;
+
+ /* Aneg firstly. */
+ if (brr_mode)
+ 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);
+}
+
+static int bcm54811_config_aneg(struct phy_device *phydev)
+{
+ u8 brr_mode;
+ int ret;
+
+ /* Aneg firstly. */
+ ret = bcm5481x_get_brrmode(phydev, &brr_mode);
+ if (ret)
+ return ret;
+
+ if (brr_mode) {
+ /* BCM54811 is only capable of autonegotiation in IEEE mode */
+ phydev->autoneg = 0;
+ 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);
}
struct bcm54616s_phy_priv {
@@ -1062,6 +1211,211 @@ static void bcm54xx_link_change_notify(struct phy_device *phydev)
bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_EXP08, ret);
}
+static int bcm_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
+ * and there is no preferred master or slave configuration
+ */
+ 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;
+
+ phydev->master_slave_get = cfg;
+ phydev->master_slave_state = state;
+
+ return 0;
+}
+
+/* Read LDS Link Partner Ability in BroadR-Reach mode */
+static int bcm_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);
+
+ return 0;
+ }
+
+ /* Long-Distance Signaling Link Partner Ability */
+ lrelpa = phy_read(phydev, MII_BCM54XX_LRELPA);
+ if (lrelpa < 0)
+ return lrelpa;
+
+ linkmode_mod_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT,
+ phydev->lp_advertising,
+ lrelpa & LRELPA_PAUSE_ASYM);
+ linkmode_mod_bit(ETHTOOL_LINK_MODE_Pause_BIT,
+ phydev->lp_advertising,
+ lrelpa & LRELPA_PAUSE);
+ linkmode_mod_bit(ETHTOOL_LINK_MODE_100baseT1_Full_BIT,
+ phydev->lp_advertising,
+ lrelpa & LRELPA_100_1PAIR);
+ linkmode_mod_bit(ETHTOOL_LINK_MODE_10baseT1BRR_Full_BIT,
+ phydev->lp_advertising,
+ lrelpa & LRELPA_10_1PAIR);
+ } else {
+ linkmode_zero(phydev->lp_advertising);
+ }
+
+ return 0;
+}
+
+static int bcm_read_status_fixed(struct phy_device *phydev)
+{
+ int lrecr = phy_read(phydev, MII_BCM54XX_LRECR);
+
+ if (lrecr < 0)
+ return lrecr;
+
+ phydev->duplex = DUPLEX_FULL;
+
+ if (lrecr & LRECR_SPEED100)
+ phydev->speed = SPEED_100;
+ else
+ phydev->speed = SPEED_10;
+
+ return 0;
+}
+
+/**
+ * lre_update_link - update link status in @phydev
+ * @phydev: target phy_device struct
+ * Return: 0 on success, < 0 on error
+ *
+ * Description: Update the value in phydev->link to reflect the
+ * current link value. In order to do this, we need to read
+ * the status register twice, keeping the second value.
+ * This is a genphy_update_link modified to work on LRE registers
+ * of BroadR-Reach PHY
+ */
+static int lre_update_link(struct phy_device *phydev)
+{
+ int status = 0, lrecr;
+
+ lrecr = phy_read(phydev, MII_BCM54XX_LRECR);
+ if (lrecr < 0)
+ return lrecr;
+
+ /* Autoneg is being started, therefore disregard BMSR value and
+ * report link as down.
+ */
+ if (lrecr & BMCR_ANRESTART)
+ goto done;
+
+ /* 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) {
+ status = phy_read(phydev, MII_BCM54XX_LRESR);
+ if (status < 0)
+ return status;
+ else if (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"
+ * bit has been reset, but "link up" bit not yet.
+ */
+ if (phydev->autoneg == AUTONEG_ENABLE && !phydev->autoneg_complete)
+ phydev->link = 0;
+
+ return 0;
+}
+
+static int bcm54811_read_status(struct phy_device *phydev)
+{
+ u8 brr_mode;
+ int err;
+
+ err = bcm5481x_get_brrmode(phydev, &brr_mode);
+
+ if (err)
+ return err;
+
+ if (brr_mode) {
+ /* Get the status in BroadRReach mode just like
+ * genphy_read_status does in normal mode
+ */
+
+ 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)
+ return 0;
+
+ phydev->speed = SPEED_UNKNOWN;
+ phydev->duplex = DUPLEX_UNKNOWN;
+ phydev->pause = 0;
+ phydev->asym_pause = 0;
+
+ err = bcm_read_master_slave(phydev);
+ if (err < 0)
+ return err;
+
+ /* Read LDS Link Partner Ability */
+ err = bcm_read_lpa(phydev);
+ if (err < 0)
+ return err;
+
+ if (phydev->autoneg ==
+ AUTONEG_ENABLE && phydev->autoneg_complete) {
+ phy_resolve_aneg_linkmode(phydev);
+ } else if (phydev->autoneg == AUTONEG_DISABLE) {
+ err = bcm_read_status_fixed(phydev);
+ if (err < 0)
+ return err;
+ }
+ } else {
+ err = genphy_read_status(phydev);
+ }
+
+ return err;
+}
+
static struct phy_driver broadcom_drivers[] = {
{
.phy_id = PHY_ID_BCM5411,
@@ -1212,9 +1566,11 @@ static struct phy_driver broadcom_drivers[] = {
.get_stats = bcm54xx_get_stats,
.probe = bcm54xx_phy_probe,
.config_init = bcm54811_config_init,
- .config_aneg = bcm5481_config_aneg,
+ .config_aneg = bcm54811_config_aneg,
.config_intr = bcm_phy_config_intr,
.handle_interrupt = bcm_phy_handle_interrupt,
+ .read_status = bcm54811_read_status,
+ .get_features = bcm54811_read_abilities,
.suspend = bcm54xx_suspend,
.resume = bcm54xx_resume,
.link_change_notify = bcm54xx_link_change_notify,
--
2.39.2
On Fri, Jun 21, 2024 at 01:26:33PM +0200, Kamil Horák (2N) wrote:
> Implement single-pair BroadR-Reach modes on bcm5481x PHY by Broadcom.
> Create set of functions alternative to IEEE 802.3 to handle
> configuration of these modes on compatible Broadcom PHYs.
What i've not seen anywhere is a link between BroadR-Reach and LRE.
Maybe you could explain the relationship here in the commit message?
And maybe also how LDS fits in.
> +int bcm_setup_master_slave(struct phy_device *phydev)
This is missing the lre in the name.
> +static int bcm54811_read_abilities(struct phy_device *phydev)
> +{
> + int i, val, err;
> + u8 brr_mode;
> +
> + for (i = 0; i < ARRAY_SIZE(bcm54811_linkmodes); i++)
> + linkmode_clear_bit(bcm54811_linkmodes[i], phydev->supported);
I think that needs a comment since it is not clear what is going on
here. What set these bits in supported?
> +
> + err = bcm5481x_get_brrmode(phydev, &brr_mode);
> + if (err)
> + return err;
> +
> + if (brr_mode) {
I would expect the DT property to be here somewhere. If the DT
property is present, set phydev->supported to only the BRR modes,
otherwise set it to the standard baseT modes. That should then allow
the core to do most of the validation. This is based on my
understanding the coupling hardware makes the two modes mutually
exclusive?
> + /* With BCM54811, BroadR-Reach implies no autoneg */
> + if (brr)
> + phydev->autoneg = 0;
So long as phydev->supported does not indicate autoneg, this should
not happen.
Andrew
On 6/22/24 21:12, Andrew Lunn wrote:
> On Fri, Jun 21, 2024 at 01:26:33PM +0200, Kamil Horák (2N) wrote:
>> Implement single-pair BroadR-Reach modes on bcm5481x PHY by Broadcom.
>> Create set of functions alternative to IEEE 802.3 to handle
>> configuration of these modes on compatible Broadcom PHYs.
> What i've not seen anywhere is a link between BroadR-Reach and LRE.
> Maybe you could explain the relationship here in the commit message?
> And maybe also how LDS fits in.
Tried to extend it a bit... LRE should be for "Long Reach Ethernet" but
Broadcom
only uses the acronym in the datasheets... LDS is "Long-Distance
Signaling", really screwed
term for a link auto-negotiation...
>
>> +int bcm_setup_master_slave(struct phy_device *phydev)
> This is missing the lre in the name.
Fixed.
>
>> +static int bcm54811_read_abilities(struct phy_device *phydev)
>> +{
>> + int i, val, err;
>> + u8 brr_mode;
>> +
>> + for (i = 0; i < ARRAY_SIZE(bcm54811_linkmodes); i++)
>> + linkmode_clear_bit(bcm54811_linkmodes[i], phydev->supported);
> I think that needs a comment since it is not clear what is going on
> here. What set these bits in supported?
This is an equivalent of genphy_read_abilities for an IEEE PHY, that is,
it fills the phydev->supported bit array exactly
as genphy_read_abilities does. The genphy_read_abilities is even called
if the PHY is not in BRR mode.
>> +
>> + err = bcm5481x_get_brrmode(phydev, &brr_mode);
>> + if (err)
>> + return err;
>> +
>> + if (brr_mode) {
> I would expect the DT property to be here somewhere. If the DT
> property is present, set phydev->supported to only the BRR modes,
> otherwise set it to the standard baseT modes. That should then allow
> the core to do most of the validation. This is based on my
> understanding the coupling hardware makes the two modes mutually
> exclusive?
From my point of view relying on DT property only would imply to
validate the setting with what is read from the PHY on
all code locations where it is currently read by bcm5481x_get_brrmode.
This is because the PHY can be reset externally
(at least by power-cycling it) and after reset, it is in IEEE mode.
Thus, I chose to set the BRR mode on/off upon initialization
and then read the setting from the chip when necessary. The PHY can
also be reset by writing bit 15 to register 0
in both IEEE and BRR modes (LRECR/BMCR).
The device I am developing on has no option for IEEE interface but in
pure theory, kind of hardware plug-in would be
possible as I was told by our hardware team. However, not even the
evaluation kit for bcm54811 can be switched
between BRR and IEEE hardware without at least soldering and desoldering
some components on the PCB.
>
>> + /* With BCM54811, BroadR-Reach implies no autoneg */
>> + if (brr)
>> + phydev->autoneg = 0;
> So long as phydev->supported does not indicate autoneg, this should
> not happen.
I also thought so but unfortunately our batch of bcm54811 indicates
possible autoneg in its status register
(LRESR_LDSABILITY) but refuses to negotiate. So this is rather a
preparation for bcm54810 full support. Unlike bcm54811,
the bcm54810 should be aneg-capable but I cannot verify it without the
hardware available. The information around
it is rather fuzzy, we were told by Broadcom tech support that the
54811 should autonegotiate as well but
the datasheets from the same source clearly indicates "no". Same for
the bcm54811 evaluation kit,
there is no autoneg option available (only 10/100Mbit and master/slave).
In conclusion, the idea was to support as much as possible but with
given hardware, only a subset could be verified
- that is bcm54811 10 or 100 Mbit on one pair and forced master /
slave selection. As for the other possibilities, I only
could test that the autoneg is probably not there or at least it does
not function with identical hardware on both ends.
We also have a BRR switch and some media converters (BRR/Ethernet) from
other manufacturer with bcm54811 to help
the development and all those are fixed setting only, no autoneg.
>
> Andrew
Kamil
On Thu, Jul 04, 2024 at 04:01:13PM +0200, Kamil Horák (2N) wrote:
>
> On 6/22/24 21:12, Andrew Lunn wrote:
> > On Fri, Jun 21, 2024 at 01:26:33PM +0200, Kamil Horák (2N) wrote:
> > > Implement single-pair BroadR-Reach modes on bcm5481x PHY by Broadcom.
> > > Create set of functions alternative to IEEE 802.3 to handle
> > > configuration of these modes on compatible Broadcom PHYs.
> > What i've not seen anywhere is a link between BroadR-Reach and LRE.
> > Maybe you could explain the relationship here in the commit message?
> > And maybe also how LDS fits in.
>
> Tried to extend it a bit... LRE should be for "Long Reach Ethernet" but
> Broadcom
>
> only uses the acronym in the datasheets... LDS is "Long-Distance Signaling",
> really screwed
>
> term for a link auto-negotiation...
You are allowed to ignore the data sheet. If using AN makes the code
more understandable, use AN. Just add a comment in the commit message.
> > > +static int bcm54811_read_abilities(struct phy_device *phydev)
> > > +{
> > > + int i, val, err;
> > > + u8 brr_mode;
> > > +
> > > + for (i = 0; i < ARRAY_SIZE(bcm54811_linkmodes); i++)
> > > + linkmode_clear_bit(bcm54811_linkmodes[i], phydev->supported);
> > I think that needs a comment since it is not clear what is going on
> > here. What set these bits in supported?
>
> This is an equivalent of genphy_read_abilities for an IEEE PHY, that is, it
> fills the phydev->supported bit array exactly
>
> as genphy_read_abilities does. The genphy_read_abilities is even called if
> the PHY is not in BRR mode.
I lost the context. But genphy_read_abilities() is only called if
phydrv->get_features is not set. Don't you make use of this
bcm54811_read_abilities for get_features? So i'm wondering, what set
these bits in the first place?
> > > +
> > > + err = bcm5481x_get_brrmode(phydev, &brr_mode);
> > > + if (err)
> > > + return err;
> > > +
> > > + if (brr_mode) {
> > I would expect the DT property to be here somewhere. If the DT
> > property is present, set phydev->supported to only the BRR modes,
> > otherwise set it to the standard baseT modes. That should then allow
> > the core to do most of the validation. This is based on my
> > understanding the coupling hardware makes the two modes mutually
> > exclusive?
>
> From my point of view relying on DT property only would imply to validate
> the setting with what is read from the PHY on
>
> all code locations where it is currently read by bcm5481x_get_brrmode.
In general, the DT value is the source of truth. It does not matter
how the PHY is strapped etc, we should reconfigure it how the DT
property indicates. So i really would set phydev->supported based on
it.
> > > + /* With BCM54811, BroadR-Reach implies no autoneg */
> > > + if (brr)
> > > + phydev->autoneg = 0;
> > So long as phydev->supported does not indicate autoneg, this should
> > not happen.
>
> I also thought so but unfortunately our batch of bcm54811 indicates possible
> autoneg in its status register
>
> (LRESR_LDSABILITY) but refuses to negotiate. So this is rather a
> preparation for bcm54810 full support. Unlike bcm54811,
If the hardware is broke, feel free to ignore the bit. I would also
keep it KISS. If somebody does want bcm54810 to auto-neg, they can add
the feature, and ask you to test for regressions with the bcm54811.
Andrew
© 2016 - 2025 Red Hat, Inc.